Logo Search packages:      
Sourcecode: netbeans version File versions  Download package

SimpleTestStepLocation.java

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common
 * Development and Distribution License("CDDL") (collectively, the
 * "License"). You may not use this file except in compliance with the
 * License. You can obtain a copy of the License at
 * http://www.netbeans.org/cddl-gplv2.html
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
 * specific language governing permissions and limitations under the
 * License.  When distributing the software, include this License Header
 * Notice in each file and include the License file at
 * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Sun in the GPL Version 2 section of the License file that
 * accompanied this code. If applicable, add the following below the
 * License Header, with the fields enclosed by brackets [] replaced by
 * your own identifying information:
 * "Portions Copyrighted [year] [name of copyright owner]"
 *
 * Contributor(s):
 *
 * The Original Software is NetBeans. The Initial Developer of the Original
 * Software is Sun Microsystems, Inc. Portions Copyright 2004-2007 Sun
 * Microsystems, Inc. All Rights Reserved.
 *
 * If you wish your version of this file to be governed by only the CDDL
 * or only the GPL Version 2, indicate your decision by adding
 * "[Contributor] elects to include this software in this distribution
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
 * single choice of license, a recipient has the option to distribute
 * your version of this file under either the CDDL, the GPL Version 2 or
 * to extend the choice of license to its licensees as provided above.
 * However, if you add GPL Version 2 code and therefore, elected the GPL
 * Version 2 license, then the option applies only if the new code is
 * made subject to such option by the copyright holder.
 */

package org.netbeans.modules.junit.wizards;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Container;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.HierarchyEvent;
import java.awt.event.HierarchyListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.MouseEvent;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.Set;
import javax.swing.AbstractAction;
import javax.swing.AbstractButton;
import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JRootPane;
import javax.swing.JSeparator;
import javax.swing.JTextField;
import javax.swing.ListSelectionModel;
import javax.swing.SwingUtilities;
import javax.swing.border.BevelBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.MouseInputListener;
import javax.swing.text.AbstractDocument;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.DocumentFilter;
import javax.swing.text.NavigationFilter;
import javax.swing.text.Position;
import org.netbeans.api.java.classpath.ClassPath;
import org.netbeans.api.project.Project;
import org.netbeans.api.project.ProjectUtils;
import org.netbeans.api.project.SourceGroup;
import org.netbeans.modules.junit.GuiUtils;
import org.netbeans.modules.junit.JUnitCfgOfCreate;
import org.netbeans.modules.junit.MessageStack;
import org.netbeans.modules.junit.NamedObject;
import org.netbeans.modules.junit.SizeRestrictedPanel;
import org.netbeans.modules.junit.TestCreator;
import org.netbeans.spi.java.project.support.ui.PackageView;
import org.openide.DialogDescriptor;
import org.openide.DialogDisplayer;
import org.openide.ErrorManager;
import org.openide.NotifyDescriptor;
import org.openide.WizardDescriptor;
import org.openide.awt.Mnemonics;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileUtil;
import org.openide.loaders.DataObject;
import org.openide.nodes.AbstractNode;
import org.openide.nodes.Children;
import org.openide.nodes.FilterNode;
import org.openide.nodes.Node;
import org.openide.nodes.NodeAcceptor;
import org.openide.nodes.NodeOperation;
import org.openide.util.HelpCtx;
import org.openide.util.NbBundle;
import org.openide.util.UserCancelException;

/**
 *
 * @author  Marian Petras
 */
00138 public final class SimpleTestStepLocation implements WizardDescriptor.Panel<WizardDescriptor> {
    
    /**
     * message layer for displaying messages about problems with checkbox
     * selection
     *
     * @see  MessageStack
     */
00146     private static final int MSG_LAYER_CHECKBOXES = 0;
    /**
     * message layer for displaying messages about problems with classname
     *
     * @see  MessageStack
     */
00152     private static final int MSG_LAYER_CLASSNAME = 1;
    
    private final String testClassNameSuffix
            = NbBundle.getMessage(TestCreator.class,
                                  "PROP_test_classname_suffix");        //NOI18N
    
    private Component visualComp;
    private List<ChangeListener> changeListeners;
    private JTextField tfClassToTest;
    private JButton btnBrowse;
    private JTextField tfTestClass;
    private JTextField tfProjectName;
    private JComboBox cboxLocation;
    private JTextField tfCreatedFile;
    
    private JCheckBox chkPublic;
    private JCheckBox chkProtected;
    private JCheckBox chkPackagePrivate;
    private JCheckBox chkSetUp;
    private JCheckBox chkTearDown;
    private JCheckBox chkMethodBodies;
    private JCheckBox chkJavadoc;
    private JCheckBox chkHints;

    /** message stack for displaying error messages */
00177     private final MessageStack msgStack = new MessageStack(2);
    private String msgClassNameInvalid;
    private String msgClassToTestDoesNotExist;
    private String msgChkBoxesInvalid;

    
    /**
     * project to create a test class in
     */
00186     private Project project;
    private WizardDescriptor wizard;
    

    // focus change detection mechanism
    
    /**
     * true, if the current chosen project have multiple testable SourceGroups.
     * If it does, class name entered in the Class to Test textfield must be
     * checked agains all of them (to detect ambiguity) and if there are
     * multiple classes matching, the user must be forced to choose one
     * before leaving the textfield.
     * <p>
     * The focus change detection mechanism is activated by the
     * {@link #hierarchyListener} after this wizard panel is added
     * to the wizard dialog. The listener activates the mechanism
     * only if the mechanism is
     * {@linkplain #focusChangeDetectionEnabled enabled}.
     *
     * @see  #setUp
     */
00207     private boolean multipleSourceRoots;
    /**
     * true if the focus change detection mechanism is enabled.
     * Being it enabled does not mean that it is activated
     * - it cannot be activated until the visual component is added
     * to the wizard dialog
     */
00214     private boolean interactionRestrictionsEnabled = false;
    /**
     * true if the focus change detection mechanism is active
     */
00218     private boolean interactionRestrictionsActive = false;
    /** <!-- PENDING --> */
00220     private boolean interactionRestrictionsSuspended = false;
    /** */
00222     private boolean mouseClicksBlocked = false;
    /**
     * hierarchy listener that detects when the visual component
     * is added to the wizard dialog. Once it is added to the dialog,
     * the focus change detection mechanism can be activated.
     *
     * @see  #focusChangeDetectionEnabled
     */
00230     private HierarchyListener displayabilityListener;
    /** root pane of the wizard dialog */
00232     private JRootPane rootPane;
    /**
     * default button of the wizard.
     * It is actually the default button of the dialog's {@link #rootPane}.
     */
00237     private JButton defaultButton;
    /**
     * action key of the root pane's original default action
     * <!-- PENDING -->
     */
00242     private String rootPaneDefaultActionKey;
    /**
     * root pane's original default action
     * <!-- PENDING -->
     */
00247     private Action rootPaneDefaultAction;
    /**
     * mouse listener of the wizard dialog's glass pane.
     * It is a part of the focus change detection mechanism.
     */
00252     private MouseInputListener glassPaneListener;
    /**
     * UI components on which mouse events are checked and evauluated.
     * The mouse events are checked only if there are
     * {@link #multipleSourceRoots}.
     */
00258     private Component[] mouseBlocked;
    /** 
     * UI components on which mnemonic activation is checked and evaluated.
     * Mnemonic activation events are checked only if there are
     * {@link #multipleSourceRoots}.
     */
00264     private JComponent[] mnemonicBlocked;
    /**
     * information about actions mapped to action keys of UI components
     * accessible using mnemonics.
     * This is used for blocking access to those components using
     * mnemonics and for restoring the UI components' action maps
     * to the original state.
     *
     * @see  #blockMnemonics
     * @see  #unblockMnemonics
     */
00275     private ActionMappingInfo[] actionMappingInfo;
    /**
     * component that is explicitely allowed to gain focus.
     * This is used when a button press event is about to be dispatched
     * to the button, so that the focus listener does not interrupt
     * focus transfer to the button.
     */
00282     private Component focusGainAllowedFor;
    

    // project structure (static)
    
    /**
     * <code>SourceGroups</code> that have at least one test
     * <code>SourceGroup</code> assigned. It is equal to set of keys
     * of the {@link #sourcesToTestsMap}.
     * It is updated whenever {@link #project} changes.
     *
     * @see  #setUp
     */
00295     private SourceGroup[] testableSourceGroups;
    /** root folders of {@link #testableSourceGroups} */
00297     private FileObject[] testableSourceGroupsRoots;
    /** <!-- PENDING --> */
00299     private SourceGroup[] allTestSourceGroups;
    /**
     * relation between <code>SourceGroup</code>s
     * and their respective test <code>SourceGroup</code>s.
     * It is updated whenever {@link #project} changes.
     *
     * @see  #setUp
     */
00307     private Map<SourceGroup,Object[]> sourcesToTestsMap;
    

    // entered and computed data
    
    /**
     * index of the first <code>SourceGroup</code> where a file named
     * according to contents of {@link #srcRelFileNameSys} was found.
     * The search is performed in {@link #testableSourceGroupsRoots}.
     * If such a file is not found in any of the source groups roots,
     * this variable is set to <code>-1</code>.
     *
     * @see  #classExists
     */
00321     private int sourceGroupParentIndex = -1;
    /** */
00323     private FileObject srcFile;
    private SourceGroup srcGroup = null;
    private String testsRootDirName = "";                               //NOI18N
    private String srcRelFileNameSys = "";                              //NOI18N
    private String testRelFileName = "";                                //NOI18N
    /** */
00329     private FileObject testRootFolder;
    /** */
00331     private int classNameLength = 0;
    /** length of the string denoting name of the selected SourceGroup */
00333     private boolean srcGroupNameDisplayed = false;
    /** <!-- PENDING --> */
00335     private boolean programmaticChange = false;
    /** <!-- PENDING --> */
00337     private boolean navigationFilterEnabled = false;
    /** <!-- PENDING --> */
00339     private ClsNameNavigationFilter clsNameNavigationFilter;
    /** <!-- PENDING --> */
00341     private ClsNameDocumentFilter clsNameDocumentFilter;
    
    /** */
00344     private boolean ignoreCboxItemChanges = false;
    /** */
00346     private boolean ignoreClsNameChanges = false;

    
    // validation of entered data
    
    /**
     * <code>true</code> if data entered in the form are valid.
     * The data are valid if the entered class name denotes an existing
     * class and at least one of the <em>Method Access Levels</em>
     * checkboxes is selected.
     */
00357     private boolean isValid = false;
    /** is the class name non-empty and valid? */
00359     private boolean classNameValid = false;
    /**
     * <code>true</code> if and only if a file named
     * according to contents of {@link #srcRelFileNameSys} was found.
     * The search is performed in {@link #testableSourceGroupsRoots}.
     * If this variable is <code>true</code>, variable
     * {@link #sourceGroupParentIndex} is set to a non-negative value.
     */
00367     private boolean classExists = false;
    /**
     * <code>true</code> if and only if at least one of the checkboxes
     * in the <em>Method Access Levels</em> group is selected
     */
00372     private boolean chkBoxesValid = false;

    
    //--------------------------------------------------------------------------
    
    public SimpleTestStepLocation() {
        visualComp = createVisualComp();
    }
    
    private Component createVisualComp() {
        JLabel lblClassToTest = new JLabel();
        JLabel lblCreatedTestClass = new JLabel();
        JLabel lblProject = new JLabel();
        JLabel lblLocation = new JLabel();
        JLabel lblFile = new JLabel();
        tfClassToTest = new JTextField(25);
        btnBrowse = new JButton();
        tfTestClass = new JTextField();
        tfProjectName = new JTextField();
        cboxLocation = new JComboBox();
        tfCreatedFile = new JTextField();
        
        ResourceBundle bundle
                = NbBundle.getBundle(SimpleTestStepLocation.class);
        
        Mnemonics.setLocalizedText(lblClassToTest,
                                   bundle.getString("LBL_ClassToTest"));//NOI18N
        Mnemonics.setLocalizedText(lblCreatedTestClass,
                                   bundle.getString("LBL_TestClass"));  //NOI18N
        Mnemonics.setLocalizedText(lblProject,
                                   bundle.getString("LBL_Project"));    //NOI18N
        Mnemonics.setLocalizedText(lblLocation,
                                   bundle.getString("LBL_Location"));   //NOI18N
        Mnemonics.setLocalizedText(lblFile,
                                   bundle.getString("LBL_CreatedFile"));//NOI18N
        Mnemonics.setLocalizedText(btnBrowse,
                                   bundle.getString("LBL_Browse"));     //NOI18N
        
        lblClassToTest.setLabelFor(tfClassToTest);
        lblCreatedTestClass.setLabelFor(tfTestClass);
        lblProject.setLabelFor(tfProjectName);
        lblFile.setLabelFor(tfCreatedFile);
        lblLocation.setLabelFor(cboxLocation);
        
        tfTestClass.setEditable(false);
        tfProjectName.setEditable(false);
        tfCreatedFile.setEditable(false);
        
        tfTestClass.setFocusable(false);
        tfProjectName.setFocusable(false);
        tfCreatedFile.setFocusable(false);
        
        cboxLocation.setEditable(false);
        
        JCheckBox[] chkBoxes;
        
        JComponent accessLevels = GuiUtils.createChkBoxGroup(
                NbBundle.getMessage(
                        GuiUtils.class,
                        "JUnitCfgOfCreate.groupAccessLevels"),          //NOI18N
                chkBoxes = GuiUtils.createCheckBoxes(new String[] {
                        GuiUtils.CHK_PUBLIC,
                        GuiUtils.CHK_PROTECTED,
                        GuiUtils.CHK_PACKAGE}));
        chkPublic = chkBoxes[0];
        chkProtected = chkBoxes[1];
        chkPackagePrivate = chkBoxes[2];
        
        JComponent optCode = GuiUtils.createChkBoxGroup(
                NbBundle.getMessage(
                        GuiUtils.class,
                        "JUnitCfgOfCreate.groupOptCode"),               //NOI18N
                chkBoxes = GuiUtils.createCheckBoxes(new String[] {
                        GuiUtils.CHK_SETUP,
                        GuiUtils.CHK_TEARDOWN,
                        GuiUtils.CHK_METHOD_BODIES}));
        chkSetUp = chkBoxes[0];
        chkTearDown = chkBoxes[1];
        chkMethodBodies = chkBoxes[2];
        
        JComponent optComments = GuiUtils.createChkBoxGroup(
                NbBundle.getMessage(
                        GuiUtils.class,
                        "JUnitCfgOfCreate.groupOptComments"),           //NOI18N
                chkBoxes = GuiUtils.createCheckBoxes(new String[] {
                        GuiUtils.CHK_JAVADOC,
                        GuiUtils.CHK_HINTS}));
        chkJavadoc = chkBoxes[0];
        chkHints = chkBoxes[1];
                        
        /* set layout of the components: */
        JPanel targetPanel
                = new SizeRestrictedPanel(new GridBagLayout(), false, true);
        
        GridBagConstraints gbcLeft = new GridBagConstraints();
        gbcLeft.anchor = GridBagConstraints.WEST;
        gbcLeft.gridwidth = 1;
        gbcLeft.insets = new Insets(0, 0, 6, 12);
        gbcLeft.fill = GridBagConstraints.NONE;
        gbcLeft.weightx = 0.0f;
        
        GridBagConstraints gbcRight = new GridBagConstraints();
        gbcRight.anchor = GridBagConstraints.WEST;
        gbcRight.gridwidth = GridBagConstraints.REMAINDER;
        gbcRight.insets = new Insets(0, 0, 6, 0);
        gbcRight.fill = GridBagConstraints.BOTH;
        gbcRight.weightx = 1.0f;
        
        // Class to Test:
        
        gbcRight.gridwidth = 1;
        
        GridBagConstraints gbcBrowse = new GridBagConstraints();
        gbcBrowse.insets = new Insets(0, 11, 6, 0);
        gbcBrowse.gridwidth = GridBagConstraints.REMAINDER;
        
        targetPanel.add(lblClassToTest, gbcLeft);
        targetPanel.add(tfClassToTest, gbcRight);
        targetPanel.add(btnBrowse, gbcBrowse);
        
        // Created Test Class:
        
        gbcLeft.insets.bottom = gbcRight.insets.bottom = 24;
        
        targetPanel.add(lblCreatedTestClass, gbcLeft);
        targetPanel.add(tfTestClass, gbcRight);
        targetPanel.add(new JPanel(), gbcBrowse);               //filler
        
        // Project:
        
        gbcRight.gridwidth = GridBagConstraints.REMAINDER;
        
        gbcLeft.insets.bottom = gbcRight.insets.bottom = 6;
        
        targetPanel.add(lblProject, gbcLeft);
        targetPanel.add(tfProjectName, gbcRight);
        
        // Location:
        
        gbcLeft.insets.bottom = gbcRight.insets.bottom = 12;
        
        targetPanel.add(lblLocation, gbcLeft);
        targetPanel.add(cboxLocation, gbcRight);
        
        // Created File:
        
        gbcLeft.insets.bottom = gbcRight.insets.bottom = 0;
        
        targetPanel.add(lblFile, gbcLeft);
        targetPanel.add(tfCreatedFile, gbcRight);
        
        JComponent optionsBox = new SizeRestrictedPanel(false, true);
        optionsBox.setLayout(
                new BoxLayout(optionsBox, BoxLayout.X_AXIS));
        optionsBox.add(accessLevels);
        optionsBox.add(Box.createHorizontalStrut(18));
        optionsBox.add(optCode);
        optionsBox.add(Box.createHorizontalStrut(18));
        optionsBox.add(optComments);
        //align groups of the checkboxes vertically to the top:
        accessLevels.setAlignmentY(0.0f);
        optCode.setAlignmentY(0.0f);
        optComments.setAlignmentY(0.0f);
        
        final Box result = Box.createVerticalBox();
        result.add(targetPanel);
        result.add(Box.createVerticalStrut(12));
            JPanel separatorPanel = new SizeRestrictedPanel(new GridLayout(),
                                                            false, true);
            separatorPanel.add(new JSeparator());
        result.add(separatorPanel);
        result.add(Box.createVerticalStrut(12));
        result.add(optionsBox);
        //result.add(Box.createVerticalGlue());  //not necessary
        
        /* tune layout of the components within the box: */
        targetPanel.setAlignmentX(0.0f);
        optionsBox.setAlignmentX(0.0f);
        optCode.setAlignmentX(0.0f);
        optComments.setAlignmentX(0.0f);
        
        result.setName(bundle.getString("LBL_panel_ChooseClass"));
        
        addAccessibilityDescriptions(result);
        setUpInteraction();
        
        return result;
    }
    
    /**
     * Sets up tooltips and accessibility names and descriptions
     * for GUI elements of the wizard panel.
     *
     * @param  wizPanel  wizard panel whose elements need to be made accessible.
     */
00567     private void addAccessibilityDescriptions(Component wizPanel) {
        final ResourceBundle bundle
                = NbBundle.getBundle(SimpleTestStepLocation.class);
        
        tfClassToTest.setToolTipText(
                bundle.getString("SimpleTest.classToTest.toolTip"));    //NOI18N
        tfClassToTest.getAccessibleContext().setAccessibleName(
                bundle.getString("SimpleTest.classToTest.AN"));         //NOI18N
        tfClassToTest.getAccessibleContext().setAccessibleDescription(
                bundle.getString("SimpleTest.classToTest.AD"));         //NOI18N
        
        btnBrowse.setToolTipText(
                bundle.getString("SimpleTest.btnBrowse.toolTip"));      //NOI18N
        btnBrowse.getAccessibleContext().setAccessibleName(
                bundle.getString("SimpleTest.btnBrowse.AN"));           //NOI18N
        btnBrowse.getAccessibleContext().setAccessibleDescription(
                bundle.getString("SimpleTest.btnBrowse.AD"));           //NOI18N
        
        cboxLocation.setToolTipText(
                bundle.getString("SimpleTest.location.toolTip"));       //NOI18N
        cboxLocation.getAccessibleContext().setAccessibleName(
                bundle.getString("SimpleTest.location.AN"));            //NOI18N
        cboxLocation.getAccessibleContext().setAccessibleDescription(
                bundle.getString("SimpleTest.location.AD"));            //NOI18N
        
        wizPanel.getAccessibleContext().setAccessibleDescription(
                bundle.getString("SimpleTest.AD"));                     //NOI18N
    }
    
    /**
     * <!-- PENDING -->
     *
     * @return  <code>true</code> if the selected item has changed,
     *          <code>false</code> otherwise
     */
00602     private boolean updateLocationComboBox() {
        Object[] srcRootsToOffer;
        
        if ((allTestSourceGroups.length == 1) || (srcGroup == null)) {
            srcRootsToOffer = allTestSourceGroups;
        } else {
            srcRootsToOffer = sourcesToTestsMap.get(srcGroup);
        }
        
        Object previousSelectedItem = cboxLocation.getSelectedItem();
        
        ignoreCboxItemChanges = true;
        try {
            Object[] items = createNamedItems(srcRootsToOffer);
            cboxLocation.setModel(new DefaultComboBoxModel(items));
            if (previousSelectedItem != null) {
                cboxLocation.setSelectedItem(previousSelectedItem);//may not process
            }
        } finally {
            ignoreCboxItemChanges = false;
        }
        
        Object newSelectedItem = cboxLocation.getSelectedItem();
        
        return !newSelectedItem.equals(previousSelectedItem);
    }
    
    /**
     */
    private static NamedObject[] createNamedItems(final Object[] srcRoots) {
        
        //PENDING - should not the source groups be sorted (alphabetically)?
        NamedObject[] items = new NamedObject[srcRoots.length];
        for (int i = 0; i < srcRoots.length; i++) {
            String name = (srcRoots[i] instanceof SourceGroup)
                          ? ((SourceGroup) srcRoots[i]).getDisplayName()
                          : (srcRoots[i] instanceof FileObject)
                            ? FileUtil.getFileDisplayName((FileObject)
                                                          srcRoots[i])
                            : srcRoots[i].toString();
            items[i] = new NamedObject(srcRoots[i],
                                       name);
        }
        return items;
    }
    
    /**
     */
    private void setUpInteraction() {
        
        class UIListener implements ActionListener, DocumentListener,
                                    FocusListener, ItemListener {
            public void actionPerformed(ActionEvent e) {
                
                /* button Browse... pressed */
                
                chooseClass();
            }
            public void insertUpdate(DocumentEvent e) {
                classNameChanged();
            }
            public void removeUpdate(DocumentEvent e) {
                classNameChanged();
            }
            public void changedUpdate(DocumentEvent e) {
                classNameChanged();
            }
            public void focusGained(FocusEvent e) {
                Object source = e.getSource();
                if (source == tfClassToTest) {
                    //tfClassToTest.getDocument().addDocumentListener(this);
                }
            }
            public void focusLost(FocusEvent e) {
                Object source = e.getSource();
                if (source == tfClassToTest) {
                    //tfClassToTest.getDocument().removeDocumentListener(this);
                    if (!e.isTemporary()) {
                        tfClassToTestFocusLost(e);
                    }
                } else if ((source == btnBrowse) && !e.isTemporary()) {
                    btnBrowseFocusLost(e);
                }
            }
            public void itemStateChanged(ItemEvent e) {
                if (e.getSource() == cboxLocation) {
                    if (!ignoreCboxItemChanges) {
                        locationChanged();
                    }
                } else {
                    /*
                     * source is one of the Method Access Levels ckeck-boxes
                     */
                    checkChkBoxesValidity();
                    setValidity();
                }
            }
        }
        
        final UIListener listener = new UIListener();
        
        btnBrowse.addActionListener(listener);
        tfClassToTest.addFocusListener(listener);
        btnBrowse.addFocusListener(listener);
        cboxLocation.addItemListener(listener);
        chkPublic.addItemListener(listener);
        chkProtected.addItemListener(listener);
        chkPackagePrivate.addItemListener(listener);
        tfClassToTest.getDocument().addDocumentListener(listener);
    }
    
    /**
     */
    private void tfClassToTestFocusLost(FocusEvent e) {
        final Component allowFocusGain = focusGainAllowedFor;
        focusGainAllowedFor = null;
        
        if (multipleSourceRoots
                && interactionRestrictionsActive
                && !interactionRestrictionsSuspended) {

            final Component opposite = e.getOppositeComponent();

            if ((allowFocusGain != null) && (opposite == allowFocusGain)) {
                return;
            }
            if (opposite == btnBrowse) {
                return;
            }
            if ((opposite instanceof JLabel)
                    && (((JLabel) opposite).getLabelFor() == tfClassToTest)) {
                /*
                 * When a JLabel's mnemonic key is pressed, the JLabel gains focus
                 * until the key is released again. That's why we must ignore such
                 * focus transfers.
                 */
                return;
            }
            
            if (!maybeDisplaySourceGroupChooser()) {
                
                /* send the request back to the Test to Class textfield: */
                tfClassToTest.requestFocus();
            }
        }
    }
    
    /**
     */
    private void btnBrowseFocusLost(FocusEvent e) {
        final Component allowFocusGain = focusGainAllowedFor;
        focusGainAllowedFor = null;
        
        if (multipleSourceRoots
                && interactionRestrictionsActive
                && !interactionRestrictionsSuspended) {

            final Component opposite = e.getOppositeComponent();

            if ((allowFocusGain != null) && (opposite == allowFocusGain)) {
                return;
            }
            if (opposite == tfClassToTest) {
                return;
            }
            if ((opposite instanceof JLabel)
                    && (((JLabel) opposite).getLabelFor() == tfClassToTest)) {
                /*
                 * When a JLabel's mnemonic key is pressed, the JLabel gains focus
                 * until the key is released again. That's why we must ignore such
                 * focus transfers.
                 */
                return;
            }

            if (!maybeDisplaySourceGroupChooser()) {
                
                /* send the request back to the Browse... button: */
                btnBrowse.requestFocus();
            }
        }
    }
    
    /**
     * <!-- PENDING -->
     *
     * @return  <code>false</code> if the SourceGroup chooser was displayed
     *          and the user cancelled the choice; <code>true</code> otherwise
     */
00791     private boolean maybeDisplaySourceGroupChooser() {
        assert multipleSourceRoots;
        
        if (classExists && (srcGroup == null)) {
            SourceGroup[] candidates = findParentGroupCandidates();
            
            assert candidates.length != 0;      //because the class exists
            
            if (candidates.length == 1) {
                setSelectedSrcGroup(candidates[0]);
                return true;
            } else {
                SourceGroup chosenSrcGroup = chooseSrcGroup(candidates);
                if (chosenSrcGroup != null) {
                    setSelectedSrcGroup(chosenSrcGroup);
                    return true;
                } else {
                    return false;
                }
            }
        } else {
            return true;
        }
    }
    
    /**
     * Displays a source root chooser which allows the user to choose
     * a parent source root for the entered class name.
     *
     * @param  candidates  source roots to be offered to the user
     * @return  the chosen source root,
     *          or <code>null</code> if the user cancelled the choice
     */
00824     private SourceGroup chooseSrcGroup(final SourceGroup[] candidates) {
        assert (candidates != null) && (candidates.length != 0);

        final String[] rootNames = new String[candidates.length];
        for (int i = 0; i < rootNames.length; i++) {
            rootNames[i] = candidates[i].getDisplayName();
        }
        
        final JButton btn = new JButton(
                NbBundle.getMessage(getClass(),
                                    "LBL_SelectBtn"));                  //NOI18N
        final JList list = new JList(rootNames);
        list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        list.setSelectedIndex(0);
        list.addListSelectionListener(new ListSelectionListener() {
            public void valueChanged(ListSelectionEvent e) {
                btn.setEnabled(!list.isSelectionEmpty());
            }
        });
        JPanel panel = new JPanel(new BorderLayout(0, 0));
        panel.add(list, BorderLayout.CENTER);
        panel.setBorder(BorderFactory.createCompoundBorder(
                BorderFactory.createEmptyBorder(12, 12, 0, 12),
                BorderFactory.createBevelBorder(BevelBorder.LOWERED)));
        
        String dialogTitle = NbBundle.getMessage(
                                    getClass(),
                                    "LBL_SourceRootChooserTitle");      //NOI18N
        DialogDescriptor descriptor
                = new DialogDescriptor(panel,                   //component
                                       dialogTitle,             //title
                                       true,                    //modal
                                       new Object[] {           //options
                                               btn,
                                               NotifyDescriptor.CANCEL_OPTION},
                                       btn,                     //default option
                                       DialogDescriptor.DEFAULT_ALIGN,
                                       (HelpCtx) null,
                                       (ActionListener) null);
        Object selected = DialogDisplayer.getDefault().notify(descriptor);
        return (selected == btn) ? candidates[list.getSelectedIndex()]
                                 : (SourceGroup) null;
    }
    
    /**
     */
    private void setSelectedSrcGroup(SourceGroup srcGroup) {
        setSelectedSrcGroup(srcGroup, true);
    }
    
    /**
     * <!-- PENDING -->
     */
    private void setSelectedSrcGroup(SourceGroup srcGroup, boolean updateDisp) {
        assert multipleSourceRoots
               && ((srcGroup == null) || (classNameValid && classExists));
        
        if (!checkObjChanged(this.srcGroup, srcGroup)) {
            return;
        }
        
        this.srcGroup = srcGroup;
        
        if (updateDisp) {
            
            /* update the display: */
            try {
                programmaticChange = true;

                String className = tfClassToTest.getText()
                                   .substring(0, classNameLength);
                String srcGroupDisplay = getSrcGrpDisp(srcGroup);
                
                ignoreClsNameChanges = true;
                tfClassToTest.setText(className + srcGroupDisplay);
                ignoreClsNameChanges = false;
                
                classNameLength = className.length();
                classNameChanged();
                srcGroupNameDisplayed = true;
                setNavigationFilterEnabled(true);
            } finally {
                ignoreClsNameChanges = false;
                programmaticChange = false;
            }
        }
        
        updateInteractionRestrictionsState();

            /*
         * There is no need to check and set validity.
         * The user should be offered to choose a source root only when
         * the entered class name is valid and the class exists
         * in at least two source roots.
         */
        
        /* update target folder: */
        if (allTestSourceGroups.length > 1) {
            boolean locationChanged = updateLocationComboBox();
            if (locationChanged) {
                updateTargetFolderData();
            }
        }
        
        /* update name of the file to be created: */
        updateCreatedFileName();
        
        /* set 'srcFile': */
        srcFile = (srcGroup != null)
                  ? srcGroup.getRootFolder().getFileObject(srcRelFileNameSys)
                  : null;
        
        assert (srcGroup == null) || (srcFile != null);
    }
    
    /**
     */
    private static String getSrcGrpDisp(SourceGroup srcGroup) {
        if (srcGroup == null) {
            return "";                                                  //NOI18N
        } else {
            String srcGroupName = srcGroup.getDisplayName();
            return new StringBuffer(srcGroupName.length() + 3)
                   .append(' ')
                   .append('(').append(srcGroupName).append(')')
                   .toString();
        }
    }
    
    /**
     */
    private void setNavigationFilterEnabled(boolean enabled) {
        if (enabled == navigationFilterEnabled) {
            if (enabled) {
                clsNameNavigationFilter.ensureCursorInRange();
            }
            return;
        }
        
        if (enabled) {
            if (clsNameNavigationFilter == null) {
                clsNameNavigationFilter = new ClsNameNavigationFilter();
            }
            tfClassToTest.setNavigationFilter(clsNameNavigationFilter);
            clsNameNavigationFilter.ensureCursorInRange();
        } else {
            tfClassToTest.setNavigationFilter(null);
        }
        this.navigationFilterEnabled = enabled;
    }
    
    /**
     * <!-- PENDING -->
     */
    private void updateInteractionRestrictionsState() {
        setInteractionRestrictionsSuspended(
                !classNameValid || !classExists || (srcGroup != null));
    }
    
    /**
     */
    private void updateTargetFolderData() {
        Object item = cboxLocation.getSelectedItem();
        if (item != null) {
            SourceGroup targetSourceGroup = (SourceGroup)
                                            ((NamedObject) item).object;
            testRootFolder = targetSourceGroup.getRootFolder();
            testsRootDirName = FileUtil.getFileDisplayName(testRootFolder);
        } else {
            testRootFolder = null;
            testsRootDirName = "";                                      //NOI18N
        }
    }
    
    /**
     * Called whenever selection in the Location combo-box is changed.
     */
01001     private void locationChanged() {
        updateTargetFolderData();
        updateCreatedFileName();
    }
    
    /**
     */
    private void classNameChanged() {
        if (ignoreClsNameChanges) {
            return;
        }
        
        String className;
        if (!programmaticChange) {
            className = tfClassToTest.getText().trim();
            classNameLength = className.length();
        } else {
            className = tfClassToTest.getText().substring(0, classNameLength);
        }
        
        String testClassName;
        if (className.length() != 0) {
            srcRelFileNameSys = className.replace('.', '/')
                                + ".java";                              //NOI18N
            testClassName = className + testClassNameSuffix;
            testRelFileName = testClassName.replace('.', File.separatorChar)
                              + ".java";                                //NOI18N
        } else {
            srcRelFileNameSys = "";                                     //NOI18N
            testClassName = "";                                         //NOI18N
            testRelFileName = "";                                       //NOI18N
        }
        tfTestClass.setText(testClassName);
        
        if (!programmaticChange) {
            updateCreatedFileName();
            if (checkClassNameValidity()) {
                checkSelectedClassExists();
            }
            setErrorMsg(msgStack.getDisplayedMessage());
            setValidity();

            /*
             * The user modified the class name.
             * It may be ambiguous - it may match classes in multiple SourceGroups.
             */
            if (multipleSourceRoots) {
                setSelectedSrcGroup(null, false);
            }
        }
        
        if (multipleSourceRoots) {
            updateInteractionRestrictionsState();
        }
    }
    
    /**
     * Identifies all <code>SourceGroup</code>s containing file having the
     * name entered by the user.
     * This method assumes that at least one such <code>SourceGroup</code>
     * has already been found and its index stored in field
     * {@link #sourceGroupParentIndex}.
     *
     * @return  array of matching <code>SourceGroup</code>s
     *          (always contains at least one element)
     */
01067     private SourceGroup[] findParentGroupCandidates() {
        assert sourceGroupParentIndex >= 0;
        
        List<SourceGroup> cands = null;
        final int count = testableSourceGroups.length;
        for (int i = sourceGroupParentIndex + 1; i < count; i++) {
            final FileObject groupRoot = testableSourceGroupsRoots[i];
            FileObject srcFile = groupRoot.getFileObject(srcRelFileNameSys);
            if (srcFile != null && testableSourceGroups[i].contains(srcFile)) {
                if (cands == null) {
                    cands = new ArrayList<SourceGroup>(testableSourceGroups.length - i + 1);
                    cands.add(testableSourceGroups[sourceGroupParentIndex]);
                }
                cands.add(testableSourceGroups[i]);
            }
        }
        return cands == null
              ? new SourceGroup[] {testableSourceGroups[sourceGroupParentIndex]}
              : cands.toArray(new SourceGroup[cands.size()]);
    }
    
    /**
     */
    private void updateCreatedFileName() {
        tfCreatedFile.setText(testsRootDirName + File.separatorChar + testRelFileName);
    }
    
    /**
     * Checks validity of the entered class name, updates messages
     * on the message stack and updates the <code>classNameValid</code> field.
     *
     * @see  #msgStack
     * @see  #setValidity()
     */
01101     private boolean checkClassNameValidity() {
        String className = tfClassToTest.getText().trim();
        if (srcGroupNameDisplayed) {
            className = className.substring(0, classNameLength);
        }
        
        if (className.length() == 0) {
            msgStack.clearMessage(MSG_LAYER_CLASSNAME);
            classNameValid = false;
        } else if (Utils.isValidClassName(className)) {
            msgStack.clearMessage(MSG_LAYER_CLASSNAME);
            classNameValid = true;
        } else {
            if (msgClassNameInvalid == null) {
                msgClassNameInvalid = NbBundle.getMessage(
                        JUnitCfgOfCreate.class,
                        "MSG_InvalidClassName");                        //NOI18N
            }
            msgStack.setMessage(MSG_LAYER_CLASSNAME, msgClassNameInvalid);
            classNameValid = false;
        }
        
        return classNameValid;
    }
    
    /**
     * Checks whether a class having the entered name exists, updates messages
     * on the message stack and updates the <code>classExists</code> field.
     *
     * @see  #setValidity()
     */
01132     private boolean checkSelectedClassExists() {
        sourceGroupParentIndex = -1;
        
        final int count = testableSourceGroups.length;
        for (int i = 0; i < count; i++) {
            final FileObject groupRoot = testableSourceGroupsRoots[i];
            FileObject srcFile = groupRoot.getFileObject(srcRelFileNameSys);
            if (srcFile != null && testableSourceGroups[i].contains(srcFile)) {
                this.srcFile = srcFile;
                sourceGroupParentIndex = i;
                break;
            }
        }
        
        classExists = (sourceGroupParentIndex != -1);
        
        if (classExists) {
            msgStack.clearMessage(MSG_LAYER_CLASSNAME);
        } else {
            if (msgClassToTestDoesNotExist == null) {
                msgClassToTestDoesNotExist = NbBundle.getMessage(
                        SimpleTestStepLocation.class,
                        "MSG_ClassToTestDoesNotExist");                 //NOI18N
            }
            msgStack.setMessage(MSG_LAYER_CLASSNAME,
                                msgClassToTestDoesNotExist);
        }
        
        return classExists;
    }
    
    /**
     * Checks whether at least one of the <em>Method Access Levels</em>
     * checkboxes is selected, updates messages
     * on the message stack and updates the <code>chkBoxesValid</code> field.
     *
     * @see  #setValidity()
     */
01170     private boolean checkChkBoxesValidity() {
        chkBoxesValid = chkPublic.isSelected()
                        || chkProtected.isSelected()
                        || chkPackagePrivate.isSelected();
        String msgUpdate;
        if (chkBoxesValid) {
            msgUpdate = msgStack.clearMessage(MSG_LAYER_CHECKBOXES);
        } else {
            if (msgChkBoxesInvalid == null) {
                //PENDING - text of the message:
                msgChkBoxesInvalid = NbBundle.getMessage(
                        JUnitCfgOfCreate.class,
                        "MSG_AllMethodTypesDisabled");                  //NOI18N
            }
            msgUpdate = msgStack.setMessage(MSG_LAYER_CHECKBOXES,
                                            msgChkBoxesInvalid);
        }
        if (msgUpdate != null) {
            setErrorMsg(msgStack.getDisplayedMessage());
        }
        return chkBoxesValid;
    }
    
    /**
     * Updates the <code>isValid</code> field and notifies all registered
     * <code>ChangeListener</code>s if validity has changed.
     */
01197     private void setValidity() {
        boolean wasValid = isValid;
        
        isValid = classNameValid && classExists && chkBoxesValid;
        
        if (isValid != wasValid) {
            fireChange();
            
            updateInteractionRestrictionsState();
            
            /*
             * This must be called after fireChange() because fireChange()
             * sets state (enabled/disabled) of the default button.
             */
            if (isValid
                    && interactionRestrictionsEnabled
                    && !interactionRestrictionsActive) {
                tryActivateInteractionRestrictions();
            }
        }
    }
    
    /**
     * Displays the given message in the wizard's message area.
     *
     * @param  message  message to be displayed, or <code>null</code>
     *                  if the message area should be cleared
     */
01225     private void setErrorMsg(String message) {
        if (wizard != null) {
            wizard.putProperty("WizardPanel_errorMessage", message);    //NOI18N
        }
    }
    
    /**
     * Displays a class chooser dialog and lets the user to select a class.
     * If the user confirms their choice, full name of the selected class
     * is put into the <em>Class To Test</em> text field.
     */
01236     private void chooseClass() {
        try {
            final Node[] sourceGroupNodes
                    = new Node[testableSourceGroups.length];
            for (int i = 0; i < sourceGroupNodes.length; i++) {
                /*
                 * Note:
                 * Precise structure of this view is *not* specified by the API.
                 */
                Node srcGroupNode
                       = PackageView.createPackageView(testableSourceGroups[i]);
                sourceGroupNodes[i]
                       = new FilterNode(srcGroupNode,
                                        new JavaChildren(srcGroupNode));
            }
            
            Node rootNode;
            if (sourceGroupNodes.length == 1) {
                rootNode = new FilterNode(
                        sourceGroupNodes[0],
                        new JavaChildren(sourceGroupNodes[0]));
            } else {
                Children children = new Children.Array();
                children.add(sourceGroupNodes);
                
                AbstractNode node = new AbstractNode(children);
                node.setName("Project Source Roots");                   //NOI18N
                node.setDisplayName(
                        NbBundle.getMessage(getClass(), "LBL_Sources"));//NOI18N
                //PENDING - set a better icon for the root node
                rootNode = node;
            }
            
            NodeAcceptor acceptor = new NodeAcceptor() {
                public boolean acceptNodes(Node[] nodes) {
                    Node.Cookie cookie;
                    return nodes.length == 1
                           && (cookie = nodes[0].getCookie(DataObject.class))
                              != null
                           && ((DataObject) cookie).getPrimaryFile().isFolder()
                              == false;
                }
            };
            
            Node selectedNode = NodeOperation.getDefault().select(
                    NbBundle.getMessage(SimpleTestStepLocation.class,
                                        "LBL_WinTitle_SelectClass"),    //NOI18N
                    NbBundle.getMessage(SimpleTestStepLocation.class,
                                        "LBL_SelectClassToTest"),       //NOI18N
                    rootNode,
                    acceptor)[0];
            
            SourceGroup selectedSourceGroup;
            if (sourceGroupNodes.length == 1) {
                selectedSourceGroup = testableSourceGroups[0];
            } else {
                Node previous = null;
                Node current = selectedNode.getParentNode();
                Node parent;
                while ((parent = current.getParentNode()) != null) {
                    previous = current;
                    current = parent;
                }
                /*
                 * 'current' now contains the root node of displayed node
                 * hierarchy. 'current' contains a parent node of the source
                 * root and 'previous' contains the parent source root of
                 * the selected class.
                 */
                selectedSourceGroup = null;
                Node selectedSrcGroupNode = previous;
                for (int i = 0; i < sourceGroupNodes.length; i++) {
                    if (sourceGroupNodes[i] == selectedSrcGroupNode) {
                        selectedSourceGroup = testableSourceGroups[i];
                        sourceGroupParentIndex = i;
                        break;
                    }
                }
                assert selectedSourceGroup != null;
                assert sourceGroupParentIndex >= 0;
            }
            srcGroup = selectedSourceGroup;
            
            FileObject selectedFileObj
                    = selectedNode.getCookie(DataObject.class).getPrimaryFile();
            
            /* display selected class name: */
            try {
                programmaticChange = true;
                
                String className = getClassName(selectedFileObj);
                classNameLength = className.length();
                if (!multipleSourceRoots) {
                    /*
                     * Caution! Calling setText("className") triggers two
                     * text change events - once when the original text is
                     * cleared and the second time when the new text is set.
                     * Method classNameChanged() must only be called when the
                     * text change is complete (see issue #91794) so we set
                     * the 'ignoreClsNameChanges' flag for the time the text
                     * is being changed and then call the classNameChanged()
                     * explicitely.
                     */
                    ignoreClsNameChanges = true;
                    tfClassToTest.setText(className);
                    ignoreClsNameChanges = false;
                    classNameChanged();
                } else {
                    String srcGroupDisplay = getSrcGrpDisp(selectedSourceGroup);

                    ignoreClsNameChanges = true;
                    tfClassToTest.setText(className + srcGroupDisplay);
                    ignoreClsNameChanges = false;
                    
                    classNameLength = className.length();
                    classNameChanged();
                    srcGroupNameDisplayed = true;
                    setNavigationFilterEnabled(true);
                }
                /*
                 * Change of text of the Class to Test text-field triggers
                 * update of variable 'testRelFileName'.
                 */
            } finally {
                ignoreClsNameChanges = false;
                programmaticChange = false;
            }
            
            /* set class name validity: */
            classNameValid = true;
            classExists = true;
            String msgUpdate = msgStack.clearMessage(MSG_LAYER_CLASSNAME);
            if (msgUpdate != null) {
                setErrorMsg(msgUpdate);
            }
            setValidity();
            updateInteractionRestrictionsState();
            
            /* update target folder: */
            if (multipleSourceRoots && (allTestSourceGroups.length > 1)) {
                boolean locationChanged = updateLocationComboBox();
                if (locationChanged) {
                    updateTargetFolderData();       //sets also 'testRootFolder'
                }
            }
            
            /* update name of the file to be created: */
            updateCreatedFileName();
            
            /* set 'srcFile': */
            srcFile = selectedFileObj;
            
        } catch (UserCancelException ex) {
            // if the user cancels the choice, do nothing
        }
    }
    
    private static String getClassName(FileObject fileObj) {
        //PENDING: is it ensured that the classpath is non-null?
        return ClassPath.getClassPath(fileObj, ClassPath.SOURCE)
               .getResourceName(fileObj, '.', false);
    }
    
    public Component getComponent() {
        return visualComp;
    }
    
    public boolean isValid() {
        return isValid;
    }
    
    public HelpCtx getHelp() {
        //PENDINGg
        return null;
    }
    
    public void readSettings(WizardDescriptor settings) {
        wizard = settings;
        
        chkPublic.setSelected(
               Boolean.TRUE.equals(wizard.getProperty(GuiUtils.CHK_PUBLIC)));
        chkProtected.setSelected(
               Boolean.TRUE.equals(wizard.getProperty(GuiUtils.CHK_PROTECTED)));
        chkPackagePrivate.setSelected(
               Boolean.TRUE.equals(wizard.getProperty(GuiUtils.CHK_PACKAGE)));
        chkSetUp.setSelected(
               Boolean.TRUE.equals(wizard.getProperty(GuiUtils.CHK_SETUP)));
        chkTearDown.setSelected(
               Boolean.TRUE.equals(wizard.getProperty(GuiUtils.CHK_TEARDOWN)));
        chkMethodBodies.setSelected(
           Boolean.TRUE.equals(wizard.getProperty(GuiUtils.CHK_METHOD_BODIES)));
        chkJavadoc.setSelected(
               Boolean.TRUE.equals(wizard.getProperty(GuiUtils.CHK_JAVADOC)));
        chkHints.setSelected(
               Boolean.TRUE.equals(wizard.getProperty(GuiUtils.CHK_HINTS)));
    }
    
    public void storeSettings(WizardDescriptor settings) {
        wizard = settings;
        
        wizard.putProperty(SimpleTestCaseWizard.PROP_CLASS_TO_TEST,
                           srcFile);
        wizard.putProperty(SimpleTestCaseWizard.PROP_TEST_ROOT_FOLDER,
                           testRootFolder);
        wizard.putProperty(GuiUtils.CHK_PUBLIC,
                           Boolean.valueOf(chkPublic.isSelected()));
        wizard.putProperty(GuiUtils.CHK_PROTECTED,
                           Boolean.valueOf(chkProtected.isSelected()));
        wizard.putProperty(GuiUtils.CHK_PACKAGE,
                           Boolean.valueOf(chkPackagePrivate.isSelected()));
        wizard.putProperty(GuiUtils.CHK_SETUP,
                           Boolean.valueOf(chkSetUp.isSelected()));
        wizard.putProperty(GuiUtils.CHK_TEARDOWN,
                           Boolean.valueOf(chkTearDown.isSelected()));
        wizard.putProperty(GuiUtils.CHK_METHOD_BODIES,
                           Boolean.valueOf(chkMethodBodies.isSelected()));
        wizard.putProperty(GuiUtils.CHK_JAVADOC,
                           Boolean.valueOf(chkJavadoc.isSelected()));
        wizard.putProperty(GuiUtils.CHK_HINTS,
                           Boolean.valueOf(chkHints.isSelected()));
    }
    
    public void addChangeListener(ChangeListener l) {
        if (changeListeners == null) {
            changeListeners = new ArrayList<ChangeListener>(4);
        }
        changeListeners.add(l);
    }
    
    public void removeChangeListener(ChangeListener l) {
        if (changeListeners != null) {
            if (changeListeners.remove(l) && changeListeners.isEmpty()) {
                changeListeners = null;
            }
        }
    }
    
    private void fireChange() {
        if (changeListeners != null) {
            ChangeEvent e = new ChangeEvent(this);
            for (ChangeListener l : changeListeners) {
                l.stateChanged(e);
            }
        }
    }
    
    /**
     */
    void setUp(final Utils utils) {
        final Project project = utils.getProject();
        
        if (project == this.project) {
            return;
        }
        
        this.project = project;
        this.sourcesToTestsMap = utils.getSourcesToTestsMap(true);
        
        int sourceGroupsCnt = sourcesToTestsMap.size();
        Set<Map.Entry<SourceGroup,Object[]>> mapEntries = sourcesToTestsMap.entrySet();
        List<SourceGroup> testGroups = new ArrayList<SourceGroup>(sourceGroupsCnt + 4);
        
        testableSourceGroups = new SourceGroup[sourceGroupsCnt];
        testableSourceGroupsRoots = new FileObject[sourceGroupsCnt];
        multipleSourceRoots = (sourceGroupsCnt > 1);
        
        Iterator<Map.Entry<SourceGroup,Object[]>> iterator = mapEntries.iterator();
        for (int i = 0; i < sourceGroupsCnt; i++) {
            Map.Entry<SourceGroup,Object[]> entry = iterator.next();
            SourceGroup srcGroup = entry.getKey();
            
            testableSourceGroups[i] = srcGroup;
            testableSourceGroupsRoots[i] = srcGroup.getRootFolder();
            
            Object[] testGroupsSubset = entry.getValue();
            for (int j = 0; j < testGroupsSubset.length; j++) {
                SourceGroup testGroup = (SourceGroup) testGroupsSubset[j];
                if (!testGroups.contains(testGroup)) {
                    testGroups.add(testGroup);
                }
            }
        }
        allTestSourceGroups = testGroups.toArray(
                                            new SourceGroup[testGroups.size()]);
        
        tfProjectName.setText(
                ProjectUtils.getInformation(project).getDisplayName());
        try {
            programmaticChange = true;
            
            ignoreClsNameChanges = true;
            tfClassToTest.setText("");                                  //NOI18N
            ignoreClsNameChanges = false;
            
            classNameLength = 0;
            classNameChanged();
            srcGroupNameDisplayed = false;
            setNavigationFilterEnabled(false);
        } finally {
            ignoreClsNameChanges = false;
            programmaticChange = false;
        }
        if (checkClassNameValidity()) {
            checkSelectedClassExists();
        } else {
            classExists = false;
        }
        setErrorMsg(msgStack.getDisplayedMessage());
        setValidity();
        
        //PENDING - if possible, we should pre-set the test source group
        //          corresponding to the currently selected node
        updateLocationComboBox();
        updateTargetFolderData();           //sets also 'testRootFolder'
        updateCreatedFileName();
        
        srcFile = null;
        
        if (!multipleSourceRoots) {
            setInteractionRestrictionsEnabled(false);
        } else {
            AbstractDocument doc = (AbstractDocument)
                                   tfClassToTest.getDocument();
            if (clsNameDocumentFilter == null) {
                clsNameDocumentFilter = new ClsNameDocumentFilter();
            }
            if (doc.getDocumentFilter() != clsNameDocumentFilter) {
                doc.setDocumentFilter(clsNameDocumentFilter);
            }
            setInteractionRestrictionsEnabled(true);
        }
    }
    
    /**
     */
    void cleanUp() {
        setInteractionRestrictionsEnabled(false);
    }
    
    /**
     * <!-- PENDING -->
     */
    private void setInteractionRestrictionsEnabled(boolean enabled) {
        if (enabled == interactionRestrictionsEnabled) {
            return;
        }
        
        class DisplayabilityListener implements HierarchyListener {
            public void hierarchyChanged(HierarchyEvent e) {
                long flags = e.getChangeFlags();
                if ((flags & HierarchyEvent.DISPLAYABILITY_CHANGED) != 0) {
                    if (visualComp.isDisplayable()) {
                        if (interactionRestrictionsEnabled) {
                            setInteractionRestrictionsActive(true);
                        }
                    } else {
                        setInteractionRestrictionsActive(false);
                    }
                }
            }
        }
        
        if (enabled) {
            this.interactionRestrictionsEnabled = true;
            
            assert displayabilityListener == null;
            displayabilityListener = new DisplayabilityListener();
            visualComp.addHierarchyListener(displayabilityListener);

            if (visualComp.isDisplayable()) {
                setInteractionRestrictionsActive(true);
            }
        } else {
            this.interactionRestrictionsEnabled = false;
            
            setInteractionRestrictionsActive(false);
            
            visualComp.removeHierarchyListener(displayabilityListener);
            displayabilityListener = null;
        }
    }
    
    /**
     * Activates or deactivates the focus detection mechanism.
     * <!-- PENDING -->
     */
01622     private void setInteractionRestrictionsActive(boolean active) {
        if (active == this.interactionRestrictionsActive) {
            return;
        }
        
        if (active) {
            tryActivateInteractionRestrictions();
        } else {
            deactivateInteractionRestrictions();
        }
    }
    
    /**
     */
    private void tryActivateInteractionRestrictions() {
        assert interactionRestrictionsActive == false;
        assert interactionRestrictionsEnabled;

        if (rootPane == null) {
            rootPane = SwingUtilities.getRootPane(visualComp);
        }
        
        if (rootPane != null) {
            defaultButton = rootPane.getDefaultButton();
            if (defaultButton != null) {
                activateInteractionRestrictions();
            }
        }
    }
    
    /**
     */
    private void activateInteractionRestrictions() {
        assert interactionRestrictionsActive == false;
        assert (rootPane != null) && (defaultButton != null);
        
        if ((mouseBlocked == null) || (mnemonicBlocked == null)) {
            findComponentsToBlock();
            assert (mouseBlocked != null) && (mnemonicBlocked != null);
        }
        blockDefaultRootPaneAction();
        blockMnemonics();
        setMouseClicksBlockingActive(!interactionRestrictionsSuspended);
        
        interactionRestrictionsActive = true;
    }
    
    /**
     */
    private void deactivateInteractionRestrictions() {
        assert interactionRestrictionsActive == true;
        assert (defaultButton != null) && (rootPane != null);
        
        setMouseClicksBlockingActive(false);
        unblockMnemonics();
        unblockDefaultRootPaneAction();
        
        defaultButton = null;
        rootPane = null;

        interactionRestrictionsActive = false;
        interactionRestrictionsSuspended = false;
    }
    
    /**
     */
    private void setInteractionRestrictionsSuspended(boolean suspended) {
        if (suspended != this.interactionRestrictionsSuspended) {
            setMouseClicksBlockingActive(interactionRestrictionsActive
                                         && !suspended);
            this.interactionRestrictionsSuspended = suspended;
        }
    }
    
    /**
     */
    private void setMouseClicksBlockingActive(boolean blockingActive) {
        if (blockingActive != this.mouseClicksBlocked) {
            if (blockingActive) {
                blockMouseClicks();
            } else {
                unblockMouseClicks();
            }
            this.mouseClicksBlocked = blockingActive;
        }
    }
    
    /**
     * Searches the visual component and collects components
     * on which mouse events or activation by mnemonics needs to be check
     * and evaluated.
     *
     * @see  #mouseBlocked
     * @see  #mnemonicBlocked
     */
01717     private void findComponentsToBlock() {
        assert rootPane != null;
        
        final Collection<Component> mouseBlocked
                = new ArrayList<Component>(20);
        final Collection<JComponent> mnemBlocked
                = new ArrayList<JComponent>(20);

        final List<Component> stack = new ArrayList<Component>(16);
        stack.add(rootPane.getContentPane());
        int lastIndex = 0;
        
        while (lastIndex != -1) {
            
            Component c = stack.remove(lastIndex--);
            
            if (!c.isVisible()) {
                continue;
            }
            
            if (c instanceof JLabel) {
                JLabel lbl = (JLabel) c;
                Component labelFor = lbl.getLabelFor();
                if ((labelFor != null) && (labelFor != tfClassToTest)
                        && (lbl.getDisplayedMnemonic() != 0)) {
                    mnemBlocked.add(lbl);
                }
            } else if (c instanceof AbstractButton) {
                if (c != btnBrowse) {
                    AbstractButton btn = (AbstractButton) c;
                    mouseBlocked.add(btn);
                    if (btn.getMnemonic() != 0) {
                        mnemBlocked.add(btn);
                    }
                }
            } else if (!(c instanceof Container)) {
                if (c.isFocusable() && (c != tfClassToTest)) {
                    mouseBlocked.add(c);
                }
            } else {
                Component[] content = ((Container) c).getComponents();
                switch (content.length) {
                    case 0:
                        break;
                    case 1:
                        stack.add(content[0]);
                        lastIndex++;
                        break;
                    default:
                        stack.addAll(Arrays.asList(content));
                        lastIndex += content.length;
                        break;
                }
            }
        }
        //mouseBlocked.add(defaultButton);
        //mnemBlocked.add(defaultButton);
        
        this.mouseBlocked = new Component[mouseBlocked.size()];
        if (mouseBlocked.size() != 0) {
            mouseBlocked.toArray(this.mouseBlocked);
        }
        this.mnemonicBlocked = new JComponent[mnemBlocked.size()];
        if (mnemBlocked.size() != 0) {
            mnemBlocked.toArray(this.mnemonicBlocked);
        }
    }
    
    /**
     */
    private void blockDefaultRootPaneAction() {
        assert (rootPane != null) && (defaultButton != null)
               && (rootPane.getDefaultButton() == defaultButton);
        
        final String actionKey1 = "press";                              //NOI18N
        final String actionKey2 = "pressed";                            //NOI18N
        String actionKey;
        
        ActionMap actionMap = rootPane.getActionMap();
        
        Action originalAction = actionMap.get(actionKey = actionKey1);
        if (originalAction == null) {
            originalAction = actionMap.get(actionKey = actionKey2);
        }
        assert originalAction != null;
        
        if (originalAction == null) {
            return;
        }
        
        actionMap.put(actionKey, new SelectSrcGrpAction(rootPane,
                                                        originalAction));
        rootPaneDefaultActionKey = actionKey;
        rootPaneDefaultAction = originalAction;
    }
    
    /**
     */
    private void unblockDefaultRootPaneAction() {
        assert rootPane != null;
        
        if (rootPaneDefaultAction == null) {
            
            /* blockDefaultRootPaneAction() did not pass */
            return;
        }
        
        rootPane.getActionMap().put(rootPaneDefaultActionKey,
                                    rootPaneDefaultAction);
        
        rootPaneDefaultActionKey = null;
        rootPaneDefaultAction = null;
    }
    
    /**
     * Modifies behaviour of the default button.
     */
01834     private void blockMnemonics() {
        assert rootPane != null;
        
        if (actionMappingInfo == null) {
            findActionMappings();
        }
        
        assert actionMappingInfo != null;
        assert actionMappingInfo.length == mnemonicBlocked.length;
        
        final JComponent[] comps = mnemonicBlocked;
        for (int i = 0; i < comps.length; i++) {
            ActionMappingInfo mappingInfo = actionMappingInfo[i];
            if (mappingInfo != null) {
                comps[i].getActionMap().put(
                        mappingInfo.actionKey,
                        new SelectSrcGrpAction(comps[i],
                                               mappingInfo.originalAction));
            } else if (comps[i] instanceof JLabel) {
                ActionMap map = new JLabelActionMap(comps[i]);
                map.setParent(comps[i].getActionMap());
                comps[i].setActionMap(map);
                continue;
            }
        }
    }
    
    /**
     */
    private void unblockMnemonics() {
        assert rootPane != null;
        
        if (actionMappingInfo == null) {
            
            /* blockMnemonics() did not pass */
            return;
        }
        
        assert actionMappingInfo.length == mnemonicBlocked.length;
        
        final JComponent[] comps = mnemonicBlocked;
        for (int i = 0; i < comps.length; i++) {
            ActionMappingInfo mappingInfo = actionMappingInfo[i];
            if (mappingInfo != null) {
                comps[i].getActionMap().put(
                        mappingInfo.actionKey,
                        mappingInfo.inProximateActionMap
                                ? mappingInfo.originalAction
                                : (Action) null);
            } else if (comps[i] instanceof JLabel) {
                comps[i].setActionMap(comps[i].getActionMap().getParent());
            }
        }
    }
    
    /**
     */
    private void findActionMappings() {
        assert mnemonicBlocked != null;
        
        final String actionKey1 = "pressed";                            //NOI18N
        final String actionKey2 = "press";                              //NOI18N
        
        actionMappingInfo = new ActionMappingInfo[mnemonicBlocked.length];
        
        final JComponent[] comps = mnemonicBlocked;
        for (int i = 0; i < comps.length; i++) {
            JComponent c = comps[i];
            
            ActionMap actionMap = comps[i].getActionMap();
            
            String primaryKey = actionKey1;
            String secondaryKey = actionKey2;
            
            if (c instanceof JLabel) {
                actionMappingInfo[i] = null;
                continue;
            }
            
            String actionKey;
            Action originalAction = actionMap.get(actionKey = primaryKey);
            if (originalAction == null) {
                originalAction = actionMap.get(actionKey = secondaryKey);
            }
            if (originalAction == null) {
                ErrorManager.getDefault()
                .log(ErrorManager.EXCEPTION,
                     "JUnitWizard - Test for Existing Class: "          //NOI18N
                     + "press action not found for a "                  //NOI18N
                     + c.getClass().getName() + " component");          //NOI18N
                actionMappingInfo[i] = null;
                continue;
            }
            
            ActionMappingInfo mappingInfo = new ActionMappingInfo();
            mappingInfo.actionKey = actionKey;
            mappingInfo.originalAction = originalAction;
            /*mappingInfo.inProximateActionMap = false;*/     //it's the default
            
            /* find whether the mapping is defined in the proximate ActionMap */
            final String keyToFind = actionKey;
            final Object[] keys = actionMap.keys();
            if (keys != null) {
                for (int j = 0; j < keys.length; j++) {
                    if (keyToFind.equals(keys[j])) {
                        mappingInfo.inProximateActionMap = true;
                        break;
                    }
                }
            }

            actionMappingInfo[i] = mappingInfo;
        }
    }
    
    /**
     * Contains information about <code>ActionMap</code> mapping
     * of a UI component.
     * There is one instance of this class per each JComponent
     * in the {@link #mnemonicBlocked} array.
     */
01955     private static class ActionMappingInfo {
        /** action key for action which activates the component */
01957         String actionKey;
        /** original action mapped to the actionKey */
01959         Action originalAction;
        /**
         * true if the mapping was defined in the component's
         * proximate ActionMap; false otherwise
         */
01964         boolean inProximateActionMap;
    }
    
    /**
     * <!-- PENDING -->
     */
    final class JLabelActionMap extends ActionMap {
        
        private final Component component;
        
        JLabelActionMap(Component comp) {
            super();
            this.component = comp;
        }
        
        @Override
        public Action get(Object key) {
            if (key.equals("press")) {                                  //NOI18N
                Action defaultAction = super.get(key);
                return (defaultAction != null)
                       ? new SelectSrcGrpAction(component, defaultAction)
                       : null;
            } else {
                return super.get(key);
            }
        }
        
    }
    
    /**
     * Sets up a glass pane - one part of the focus change detection mechanism.
     */
01996     private void blockMouseClicks() {
        assert rootPane != null;

        final Component glassPane = rootPane.getGlassPane();
        
        if (glassPaneListener == null) {
            glassPaneListener = new GlassPaneListener();
        }
        glassPane.addMouseListener(glassPaneListener);
        glassPane.addMouseMotionListener(glassPaneListener);
        glassPane.setVisible(true);
    }
    
    /**
     * Cleans up a glass pane - one part of the focus change detection
     * mechanism.
     */
02013     private void unblockMouseClicks() {
        assert rootPane != null;
        
        if (glassPaneListener == null) {
            return;
        }
        
        final Component glassPane = rootPane.getGlassPane();
        
        glassPane.setVisible(false);
        glassPane.removeMouseMotionListener(glassPaneListener);
        glassPane.removeMouseListener(glassPaneListener);
    }

    /**
     *
     */
    final class GlassPaneListener implements MouseInputListener {
        final Component glassPane = rootPane.getGlassPane();
        final Component layeredPane = rootPane.getLayeredPane();
        final Container contentPane = rootPane.getContentPane();
        
        public void mouseMoved(MouseEvent e) {
            redispatchEvent(e);
        }
        public void mouseDragged(MouseEvent e) {
            redispatchEvent(e);
        }
        public void mouseClicked(MouseEvent e) {
            redispatchEvent(e);
        }
        public void mouseEntered(MouseEvent e) {
            redispatchEvent(e);
        }
        public void mouseExited(MouseEvent e) {
            redispatchEvent(e);
        }
        public void mousePressed(MouseEvent e) {
            evaluateEvent(e);
        }
        public void mouseReleased(MouseEvent e) {
            redispatchEvent(e);
        }
        private void evaluateEvent(MouseEvent e) {
            assert multipleSourceRoots;
            
            Component component = getDeepestComponent(e);
            if (component == null) {
                return;
            }

            boolean isBlocked = false;
            if (SwingUtilities.isLeftMouseButton(e)) {
                final Component[] blocked = mouseBlocked;
                for (int i = 0; i < blocked.length; i++) {
                    if (component == blocked[i]) {
                        isBlocked = true;
                        break;
                    }
                }
            }
            
            boolean askUserToChoose;
            SourceGroup[] candidates = null;
            if (!isBlocked || interactionRestrictionsSuspended) {
                askUserToChoose = false;
            } else if (component == defaultButton) {
                candidates = findParentGroupCandidates();
                askUserToChoose = (candidates.length > 1);
            } else if (!SwingUtilities.isDescendingFrom(component,
                                                        visualComp)) {
                askUserToChoose = false;
            } else {
                candidates = findParentGroupCandidates();
                askUserToChoose = (candidates.length > 1);
            }
            
            assert (askUserToChoose == false) || (candidates.length > 1);
            
            if (askUserToChoose) {
                SourceGroup srcGroup = chooseSrcGroup(candidates);
                if (srcGroup != null) {
                    setSelectedSrcGroup(srcGroup);
                    focusGainAllowedFor = component;
                    component.requestFocus();
                }
            } else {
                if (candidates != null) {
                    assert candidates.length == 1;
                    
                    setSelectedSrcGroup(candidates[0]);
                }
                focusGainAllowedFor = component;
                try {
                    redispatchEvent(e, component);
                } finally {
                    clearFocusGainAllowedVar();
                }
            }
        }
        private void redispatchEvent(MouseEvent e) {
            Component deepestComp = getDeepestComponent(e);
            if (deepestComp != null) {
                redispatchEvent(e, deepestComp);
            }
        }
        private void redispatchEvent(MouseEvent e, Component component) {
            Point componentPoint
                    = SwingUtilities.convertPoint(glassPane,
                                                  e.getPoint(),
                                                  component);
            component.dispatchEvent(
                    new MouseEvent(component,
                                   e.getID(),
                                   e.getWhen(),
                                   e.getModifiers(),
                                   componentPoint.x,
                                   componentPoint.y,
                                   e.getClickCount(),
                                   e.isPopupTrigger()));
        }
        private Component getDeepestComponent(MouseEvent e) {
            Point contentPanePoint
                    = SwingUtilities.convertPoint(glassPane,
                                                  e.getPoint(),
                                                  contentPane);
            return SwingUtilities.getDeepestComponentAt(
                            contentPane,
                            contentPanePoint.x,
                            contentPanePoint.y);
        }
    }
    
    /**
     * Action that is activated by a mnemonic keystroke.
     */
02149     private class SelectSrcGrpAction extends AbstractAction {
        private final Component component;
        private final Action delegate;
        public SelectSrcGrpAction(Component comp, Action delegate) {
            this.component = comp;
            this.delegate = delegate;
        }
        public void actionPerformed(ActionEvent e) {
            assert multipleSourceRoots;
            
            boolean askUserToChoose;
            SourceGroup[] candidates = null;
            if (interactionRestrictionsSuspended) {
                askUserToChoose = false;
            } else if ((component == defaultButton)
                       || (component == rootPane)) {
                candidates = findParentGroupCandidates();
                askUserToChoose = (candidates.length > 1);
            } else if (!SwingUtilities.isDescendingFrom(component,
                                                        visualComp)) {
                askUserToChoose = false;
            } else {
                candidates = findParentGroupCandidates();
                askUserToChoose = (candidates.length > 1);
            }
            
            assert (askUserToChoose == false) || (candidates.length > 1);
            
            if (askUserToChoose) {
                SourceGroup srcGroup = chooseSrcGroup(candidates);
                if (srcGroup != null) {
                    setSelectedSrcGroup(srcGroup);
                    if (component == rootPane) {
                        defaultButton.requestFocus();
                    } else {
                        component.requestFocus();
                    }
                }
            } else {
                if (candidates != null) {
                    assert candidates.length == 1;
                    
                    setSelectedSrcGroup(candidates[0]);
                }
                redispatchEvent(e);
            }
        }
        private void redispatchEvent(ActionEvent e) {
            focusGainAllowedFor = component;
            try {
                delegate.actionPerformed(e);
            } finally {
                clearFocusGainAllowedVar();
            }
        }
        @Override
        public boolean isEnabled() {
            return delegate.isEnabled();
        }
    }
    
    /**
     * <!-- PENDING -->
     */
    private class ClsNameDocumentFilter extends DocumentFilter {
        public ClsNameDocumentFilter () {}
        
        @Override
        public void replace(DocumentFilter.FilterBypass bypass,
                            int offset,
                            int length,
                            String text,
                            AttributeSet attrs) throws BadLocationException {
            if (!programmaticChange && srcGroupNameDisplayed) {
                removeSrcGroupName(bypass);
            }
            super.replace(bypass, offset, length, text, attrs);
        }
        @Override
        public void insertString(
                            DocumentFilter.FilterBypass bypass,
                            int offset,
                            String string,
                            AttributeSet attr) throws BadLocationException {
            if (!programmaticChange && srcGroupNameDisplayed) {
                removeSrcGroupName(bypass);
            }
            super.insertString(bypass, offset, string, attr);
        }
        @Override
        public void remove(DocumentFilter.FilterBypass bypass,
                           int offset,
                           int length) throws BadLocationException {
            if (!programmaticChange && srcGroupNameDisplayed) {
                removeSrcGroupName(bypass);
            }
            super.remove(bypass, offset, length);
        }
        private void removeSrcGroupName(DocumentFilter.FilterBypass bypass)
                                                throws BadLocationException {
            bypass.remove(classNameLength,
                          tfClassToTest.getText().length() - classNameLength);
            srcGroupNameDisplayed = false;
            setNavigationFilterEnabled(false);
        }
    }
    
    /**
     * <!-- PENDING -->
     */
    private class ClsNameNavigationFilter extends NavigationFilter {
        public ClsNameNavigationFilter () {}
        
        @Override
        public void setDot(NavigationFilter.FilterBypass bypass,
                           int dot,
                           Position.Bias bias) {
            if (dot > classNameLength) {
                bypass.setDot(classNameLength, bias);
            } else {
                super.setDot(bypass, dot, bias);
            }
        }
        @Override
        public void moveDot(NavigationFilter.FilterBypass bypass,
                           int dot,
                           Position.Bias bias) {
            if (dot > classNameLength) {
                bypass.moveDot(classNameLength, bias);
            } else {
                super.moveDot(bypass, dot, bias);
            }
        }
        public void ensureCursorInRange() {
            if (srcGroupNameDisplayed) {
                if (tfClassToTest.getCaretPosition() > classNameLength) {
                    tfClassToTest.setCaretPosition(classNameLength);
                }
            }
        }
    }
    
    /**
     * Sends a request to clear the {@link #focusGainAllowedFor} variable
     * to the end of the event queue.
     */
02295     private void clearFocusGainAllowedVar() {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                focusGainAllowedFor = null;
            }
        });
    }
    
    /**
     */
    private static boolean checkObjChanged(Object oldObj, Object newObj) {
        return ((oldObj != null) || (newObj != null))
               && ((oldObj == null) || !oldObj.equals(newObj));
    }
    
    /* *
     * /
    private static void printCallstack(String header) {
        
        //PENDING - this method is not needed in the final version
        
        if (header != null) {
            System.out.println(header);
        }

        StackTraceElement[] frames = new Exception().getStackTrace();
        int count = Math.min(5, frames.length);
        for (int i = 1; i < frames.length; i++) {
            String methodName = frames[i].getMethodName();
            if (!methodName.startsWith("access$")) {
                System.out.println("   " + methodName + "(...)");
                if (--count == 0) {
                    break;
                }
            }
        }
        System.out.println();
    }
     */
    
}

Generated by  Doxygen 1.6.0   Back to index