0001: /*
0002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003: *
0004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * The contents of this file are subject to the terms of either the GNU
0007: * General Public License Version 2 only ("GPL") or the Common
0008: * Development and Distribution License("CDDL") (collectively, the
0009: * "License"). You may not use this file except in compliance with the
0010: * License. You can obtain a copy of the License at
0011: * http://www.netbeans.org/cddl-gplv2.html
0012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
0013: * specific language governing permissions and limitations under the
0014: * License. When distributing the software, include this License Header
0015: * Notice in each file and include the License file at
0016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
0017: * particular file as subject to the "Classpath" exception as provided
0018: * by Sun in the GPL Version 2 section of the License file that
0019: * accompanied this code. If applicable, add the following below the
0020: * License Header, with the fields enclosed by brackets [] replaced by
0021: * your own identifying information:
0022: * "Portions Copyrighted [year] [name of copyright owner]"
0023: *
0024: * Contributor(s):
0025: *
0026: * The Original Software is NetBeans. The Initial Developer of the Original
0027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
0028: * Microsystems, Inc. All Rights Reserved.
0029: *
0030: * If you wish your version of this file to be governed by only the CDDL
0031: * or only the GPL Version 2, indicate your decision by adding
0032: * "[Contributor] elects to include this software in this distribution
0033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
0034: * single choice of license, a recipient has the option to distribute
0035: * your version of this file under either the CDDL, the GPL Version 2 or
0036: * to extend the choice of license to its licensees as provided above.
0037: * However, if you add GPL Version 2 code and therefore, elected the GPL
0038: * Version 2 license, then the option applies only if the new code is
0039: * made subject to such option by the copyright holder.
0040: */
0041:
0042: package org.netbeans.modules.junit;
0043:
0044: import java.awt.BorderLayout;
0045: import java.awt.Color;
0046: import java.awt.Component;
0047: import java.awt.Container;
0048: import java.awt.Font;
0049: import java.awt.GridBagConstraints;
0050: import java.awt.GridBagLayout;
0051: import java.awt.Insets;
0052: import java.awt.event.ActionListener;
0053: import java.awt.event.ItemEvent;
0054: import java.awt.event.ItemListener;
0055: import java.util.ArrayList;
0056: import java.util.Iterator;
0057: import java.util.List;
0058: import java.util.ResourceBundle;
0059: import java.util.Stack;
0060: import javax.swing.BorderFactory;
0061: import javax.swing.Box;
0062: import javax.swing.BoxLayout;
0063: import javax.swing.DefaultComboBoxModel;
0064: import javax.swing.JButton;
0065: import javax.swing.JCheckBox;
0066: import javax.swing.JComboBox;
0067: import javax.swing.JComponent;
0068: import javax.swing.JLabel;
0069: import javax.swing.JList;
0070: import javax.swing.JPanel;
0071: import javax.swing.JTextArea;
0072: import javax.swing.ListCellRenderer;
0073: import javax.swing.UIManager;
0074: import javax.swing.border.Border;
0075: import javax.swing.border.CompoundBorder;
0076: import javax.swing.border.TitledBorder;
0077: import javax.swing.event.ChangeEvent;
0078: import javax.swing.event.ChangeListener;
0079: import javax.swing.plaf.UIResource;
0080: import org.netbeans.api.java.classpath.ClassPath;
0081: import org.netbeans.api.project.SourceGroup;
0082: import org.openide.DialogDescriptor;
0083: import org.openide.DialogDisplayer;
0084: import org.openide.awt.Mnemonics;
0085: import org.openide.filesystems.FileObject;
0086: import org.openide.filesystems.FileUtil;
0087: import org.openide.loaders.DataFolder;
0088: import org.openide.loaders.DataObject;
0089: import org.openide.nodes.Node;
0090: import org.openide.util.HelpCtx;
0091: import org.openide.util.Lookup;
0092: import org.openide.util.NbBundle;
0093:
0094: /**
0095: *
0096: * @author vstejskal
0097: * @author Marian Petras
0098: */
0099: @SuppressWarnings("serial")
0100: public final class JUnitCfgOfCreate extends SelfResizingPanel implements
0101: ChangeListener {
0102:
0103: /** suffix of test classes */
0104: private static final String TEST_CLASS_SUFFIX = "Test"; //NOI18N
0105:
0106: /**
0107: * nodes selected when the Create Tests action was invoked
0108: */
0109: private final Node[] nodes;
0110: /** whether the tests will be created for multiple classes */
0111: private final boolean multipleClasses;
0112: /** whether a single package/folder is selected */
0113: private boolean singlePackage;
0114: /** whether a single class is selected */
0115: private boolean singleClass;
0116: /** test class name specified in the form (or <code>null</code>) */
0117: private String testClassName;
0118: /** registered change listeners */
0119: private List<ChangeListener> changeListeners;
0120: /** */
0121: private String initialMessage;
0122:
0123: /**
0124: * is at least one target folder/source group available?
0125: *
0126: * @see #isAcceptable()
0127: */
0128: private boolean hasTargetFolders = false;
0129:
0130: /**
0131: * is combination of checkbox states acceptable?
0132: *
0133: * @see #isAcceptable()
0134: */
0135: private boolean checkBoxesOK;
0136:
0137: /**
0138: * message about invalid configuration of checkboxes
0139: * in the <em>Method Access Levels</em> group
0140: */
0141: private String msgChkBoxesInvalid;
0142:
0143: /**
0144: * is the entered class name non-empty and valid?
0145: *
0146: * @see #isAcceptable()
0147: */
0148: private boolean classNameValid;
0149:
0150: /**
0151: * is the current form contents acceptable?
0152: *
0153: * @see #isAcceptable()
0154: */
0155: private boolean isAcceptable;
0156:
0157: /** layer index for a message about an empty set of target folders */
0158: private static final int MSG_TYPE_NO_TARGET_FOLDERS = 0;
0159: /** layer index for a message about invalid configuration of checkboxes */
0160: private static final int MSG_TYPE_INVALID_CHKBOXES = 1;
0161: /** layer index for a message about invalid class name */
0162: private static final int MSG_TYPE_CLASSNAME_INVALID = 2;
0163: /** layer index for a message about non-default class name */
0164: private static final int MSG_TYPE_CLASSNAME_NOT_DEFAULT = 3;
0165: /** */
0166: private MessageStack msgStack = new MessageStack(4);
0167:
0168: /**
0169: * Creates a JUnit configuration panel.
0170: *
0171: * @param nodes nodes selected when the Create Tests action was invoked
0172: */
0173: JUnitCfgOfCreate(Node[] nodes) {
0174: assert (nodes != null) && (nodes.length != 0);
0175:
0176: this .nodes = nodes;
0177: multipleClasses = checkMultipleClasses();
0178:
0179: initBundle();
0180: try {
0181: initComponents();
0182: setBorder(BorderFactory.createEmptyBorder(12, 12, 0, 11));
0183: addAccessibleDescriptions();
0184: initializeCheckBoxStates();
0185: fillFormData();
0186: checkAcceptability();
0187: setupUserInteraction();
0188:
0189: /*
0190: * checkAcceptability() must not be called
0191: * before initializeCheckBoxStates() and fillFormData()
0192: * setupUserInteraction must not be called
0193: * before initializeCheckBoxStates()
0194: */
0195:
0196: } finally {
0197: unlinkBundle();
0198: }
0199: }
0200:
0201: private void addAccessibleDescriptions() {
0202:
0203: // window
0204: this .getAccessibleContext().setAccessibleDescription(
0205: bundle.getString("JUnitCfgOfCreate.AD"));
0206:
0207: // text-field and combo-box
0208:
0209: if (this .tfClassName != null) {
0210: this .tfClassName.setToolTipText(bundle
0211: .getString("JUnitCfgOfCreate.clsName.toolTip"));//NOI18N
0212: this .tfClassName.getAccessibleContext().setAccessibleName(
0213: bundle.getString("JUnitCfgOfCreate.clsName.AN")); //NOI18N
0214: this .tfClassName
0215: .getAccessibleContext()
0216: .setAccessibleDescription(
0217: bundle
0218: .getString("JUnitCfgOfCreate.clsName.AD")); //NOI18N
0219: }
0220:
0221: this .cboxLocation.setToolTipText(bundle
0222: .getString("JUnitCfgOfCreate.location.toolTip")); //NOI18N
0223: this .cboxLocation.getAccessibleContext().setAccessibleName(
0224: bundle.getString("JUnitCfgOfCreate.location.AN")); //NOI18N
0225: this .cboxLocation
0226: .getAccessibleContext()
0227: .setAccessibleDescription(
0228: bundle
0229: .getString("JUnitCfgOfCreate.location.AD")); //NOI18N
0230:
0231: // check boxes
0232: this .chkPublic.setToolTipText(bundle
0233: .getString("JUnitCfgOfCreate.chkPublic.toolTip"));
0234: this .chkPublic.getAccessibleContext().setAccessibleDescription(
0235: bundle.getString("JUnitCfgOfCreate.chkPublic.AD"));
0236:
0237: this .chkProtected.setToolTipText(bundle
0238: .getString("JUnitCfgOfCreate.chkProtected.toolTip"));
0239: this .chkProtected
0240: .getAccessibleContext()
0241: .setAccessibleDescription(
0242: bundle
0243: .getString("JUnitCfgOfCreate.chkProtected.AD"));
0244:
0245: this .chkPackage.setToolTipText(bundle
0246: .getString("JUnitCfgOfCreate.chkPackage.toolTip"));
0247: this .chkPackage
0248: .getAccessibleContext()
0249: .setAccessibleDescription(
0250: bundle
0251: .getString("JUnitCfgOfCreate.chkPackage.AD"));
0252:
0253: this .chkComments.setToolTipText(bundle
0254: .getString("JUnitCfgOfCreate.chkComments.toolTip"));
0255: this .chkComments
0256: .getAccessibleContext()
0257: .setAccessibleDescription(
0258: bundle
0259: .getString("JUnitCfgOfCreate.chkComments.AD"));
0260:
0261: this .chkContent.setToolTipText(bundle
0262: .getString("JUnitCfgOfCreate.chkContent.toolTip"));
0263: this .chkContent
0264: .getAccessibleContext()
0265: .setAccessibleDescription(
0266: bundle
0267: .getString("JUnitCfgOfCreate.chkContent.AD"));
0268:
0269: this .chkJavaDoc.setToolTipText(bundle
0270: .getString("JUnitCfgOfCreate.chkJavaDoc.toolTip"));
0271: this .chkJavaDoc
0272: .getAccessibleContext()
0273: .setAccessibleDescription(
0274: bundle
0275: .getString("JUnitCfgOfCreate.chkJavaDoc.AD"));
0276:
0277: if (multipleClasses) {
0278: this .chkExceptions
0279: .setToolTipText(bundle
0280: .getString("JUnitCfgOfCreate.chkExceptions.toolTip"));
0281: this .chkExceptions
0282: .getAccessibleContext()
0283: .setAccessibleDescription(
0284: bundle
0285: .getString("JUnitCfgOfCreate.chkExceptions.AD"));
0286:
0287: this .chkAbstractImpl
0288: .setToolTipText(bundle
0289: .getString("JUnitCfgOfCreate.chkAbstractImpl.toolTip"));
0290: this .chkAbstractImpl
0291: .getAccessibleContext()
0292: .setAccessibleDescription(
0293: bundle
0294: .getString("JUnitCfgOfCreate.chkAbstractImpl.AD"));
0295:
0296: this .chkPackagePrivateClasses
0297: .setToolTipText(bundle
0298: .getString("JUnitCfgOfCreate.chkPackagePrivateClasses.toolTip"));
0299: this .chkPackagePrivateClasses
0300: .getAccessibleContext()
0301: .setAccessibleDescription(
0302: bundle
0303: .getString("JUnitCfgOfCreate.chkPackagePrivateClasses.AD"));
0304:
0305: this .chkGenerateSuites
0306: .setToolTipText(bundle
0307: .getString("JUnitCfgOfCreate.chkGenerateSuites.toolTip"));
0308: this .chkGenerateSuites
0309: .getAccessibleContext()
0310: .setAccessibleDescription(
0311: bundle
0312: .getString("JUnitCfgOfCreate.chkGenerateSuites.AD"));
0313: }
0314:
0315: }
0316:
0317: /**
0318: * Checks whether multiple classes may be selected.
0319: * It also detects whether exactly one package/folder or exactly one class
0320: * is selected and sets values of variables {@link #singlePackage}
0321: * and {@link #singleClass} accordingly.
0322: *
0323: * @return <code>false</code> if there is exactly one node selected
0324: * and the node represents a single <code>DataObject</code>,
0325: * not a folder or another <code>DataObject</code> container;
0326: * <code>true</code> otherwise
0327: */
0328: private boolean checkMultipleClasses() {
0329: if (nodes.length > 1) {
0330: return true;
0331: }
0332:
0333: Lookup nodeLookup = nodes[0].getLookup();
0334: if (nodeLookup.lookup(DataObject.Container.class) != null) {
0335: singlePackage = nodeLookup.lookup(DataFolder.class) != null;
0336: return true;
0337: }
0338:
0339: singleClass = false;
0340: DataObject dataObj = nodeLookup.lookup(DataObject.class);
0341: if (dataObj == null) {
0342: return true;
0343: }
0344:
0345: singleClass = dataObj.getPrimaryFile().isData();
0346: return !singleClass;
0347: }
0348:
0349: /**
0350: * Displays a configuration dialog and updates JUnit options according
0351: * to the user's settings.
0352: *
0353: * @param nodes nodes selected when the Create Test action was invoked
0354: */
0355: boolean configure() {
0356:
0357: // create and display the dialog:
0358: String title = NbBundle.getMessage(JUnitCfgOfCreate.class,
0359: "JUnitCfgOfCreate.Title"); //NOI18N
0360: ChangeListener changeListener;
0361: final JButton btnOK = new JButton(NbBundle.getMessage(
0362: JUnitCfgOfCreate.class, "LBL_OK")); //NOI18N
0363: btnOK.getAccessibleContext().setAccessibleName(
0364: NbBundle.getMessage(JUnitCfgOfCreate.class, "AN_OK"));
0365: btnOK.getAccessibleContext().setAccessibleDescription(
0366: NbBundle.getMessage(JUnitCfgOfCreate.class, "AD_OK"));
0367: btnOK.setEnabled(isAcceptable());
0368: addChangeListener(changeListener = new ChangeListener() {
0369: public void stateChanged(ChangeEvent e) {
0370: btnOK.setEnabled(isAcceptable());
0371: }
0372: });
0373:
0374: Object returned = DialogDisplayer.getDefault().notify(
0375: new DialogDescriptor(this ,
0376: title,
0377: true, //modal
0378: new Object[] { btnOK,
0379: DialogDescriptor.CANCEL_OPTION },
0380: btnOK, //initial value
0381: DialogDescriptor.DEFAULT_ALIGN, new HelpCtx(
0382: JUnitCfgOfCreate.class),
0383: (ActionListener) null));
0384: removeChangeListener(changeListener);
0385:
0386: if (returned == btnOK) {
0387: rememberCheckBoxStates();
0388: testClassName = (tfClassName != null) ? tfClassName
0389: .getText() : null;
0390: return true;
0391: }
0392: return false;
0393: }
0394:
0395: /**
0396: * Returns whether a test for a single class is to be created.
0397: *
0398: * @return true if there is only one node selected and the node
0399: * represents a class
0400: */
0401: boolean isSingleClass() {
0402: return singleClass;
0403: }
0404:
0405: /**
0406: * Returns the class name entered in the text-field.
0407: *
0408: * @return class name entered in the form,
0409: * or <code>null</code> if the form did not contain
0410: * the field for entering class name
0411: */
0412: String getTestClassName() {
0413: return testClassName;
0414: }
0415:
0416: /** resource bundle used during initialization of this panel */
0417: public ResourceBundle bundle;
0418:
0419: /**
0420: * Reads JUnit settings and initializes checkboxes accordingly.
0421: *
0422: * @see #rememberCheckBoxStates
0423: */
0424: private void initializeCheckBoxStates() {
0425: final JUnitSettings settings = JUnitSettings.getDefault();
0426:
0427: chkPublic.setSelected(settings.isMembersPublic());
0428: chkProtected.setSelected(settings.isMembersProtected());
0429: chkPackage.setSelected(settings.isMembersPackage());
0430: chkComments.setSelected(settings.isBodyComments());
0431: chkContent.setSelected(settings.isBodyContent());
0432: chkJavaDoc.setSelected(settings.isJavaDoc());
0433: if (multipleClasses) {
0434: chkGenerateSuites.setSelected(settings
0435: .isGenerateSuiteClasses());
0436: chkPackagePrivateClasses.setSelected(settings
0437: .isIncludePackagePrivateClasses());
0438: chkAbstractImpl.setSelected(settings
0439: .isGenerateAbstractImpl());
0440: chkExceptions.setSelected(settings
0441: .isGenerateExceptionClasses());
0442: }
0443: chkSetUp.setSelected(settings.isGenerateSetUp());
0444: chkTearDown.setSelected(settings.isGenerateTearDown());
0445:
0446: checkChkBoxesStates();
0447: }
0448:
0449: /**
0450: * Stores settings given by checkbox states to JUnit settings.
0451: *
0452: * @see #initializeCheckBoxStatesf
0453: */
0454: private void rememberCheckBoxStates() {
0455: final JUnitSettings settings = JUnitSettings.getDefault();
0456:
0457: settings.setMembersPublic(chkPublic.isSelected());
0458: settings.setMembersProtected(chkProtected.isSelected());
0459: settings.setMembersPackage(chkPackage.isSelected());
0460: settings.setBodyComments(chkComments.isSelected());
0461: settings.setBodyContent(chkContent.isSelected());
0462: settings.setJavaDoc(chkJavaDoc.isSelected());
0463: if (multipleClasses) {
0464: settings.setGenerateSuiteClasses(chkGenerateSuites
0465: .isSelected());
0466: settings
0467: .setIncludePackagePrivateClasses(chkPackagePrivateClasses
0468: .isSelected());
0469: settings.setGenerateAbstractImpl(chkAbstractImpl
0470: .isSelected());
0471: settings.setGenerateExceptionClasses(chkExceptions
0472: .isSelected());
0473: }
0474: settings.setGenerateSetUp(chkSetUp.isSelected());
0475: settings.setGenerateTearDown(chkTearDown.isSelected());
0476: }
0477:
0478: /**
0479: * Loads a resource bundle so that it can be used during intialization
0480: * of this panel.
0481: *
0482: * @see #unlinkBundle
0483: */
0484: private void initBundle() {
0485: bundle = NbBundle.getBundle(JUnitCfgOfCreate.class);
0486: }
0487:
0488: /**
0489: * Nulls the resource bundle so that it is not held in memory when it is
0490: * not used.
0491: *
0492: * @see #initBundle
0493: */
0494: private void unlinkBundle() {
0495: bundle = null;
0496: }
0497:
0498: /**
0499: * This method is called from within the constructor to initialize the form.
0500: */
0501: private void initComponents() {
0502: setLayout(new BorderLayout(0, 12));
0503:
0504: add(createNameAndLocationPanel(), BorderLayout.NORTH);
0505: add(createMessagePanel(), BorderLayout.CENTER);
0506: add(createCodeGenPanel(), BorderLayout.SOUTH);
0507: }
0508:
0509: /**
0510: */
0511: private void setupUserInteraction() {
0512: final ItemListener listener = new CheckBoxListener();
0513:
0514: chkPublic.addItemListener(listener);
0515: chkProtected.addItemListener(listener);
0516: chkPackage.addItemListener(listener);
0517: }
0518:
0519: /**
0520: */
0521: private void checkChkBoxesStates() {
0522: checkBoxesOK = chkPublic.isSelected()
0523: || chkProtected.isSelected() || chkPackage.isSelected();
0524: if (checkBoxesOK) {
0525: setMessage(null, MSG_TYPE_INVALID_CHKBOXES);
0526: } else {
0527: if (msgChkBoxesInvalid == null) {
0528: //PENDING - text of the message:
0529: msgChkBoxesInvalid = NbBundle.getMessage(
0530: JUnitCfgOfCreate.class,
0531: "MSG_AllMethodTypesDisabled"); //NOI18N
0532: }
0533: setMessage(msgChkBoxesInvalid, MSG_TYPE_INVALID_CHKBOXES);
0534: }
0535: }
0536:
0537: /**
0538: * Listener object that listens on state changes of some check-boxes.
0539: */
0540: private final class CheckBoxListener implements ItemListener {
0541: public CheckBoxListener() {
0542: }
0543:
0544: public void itemStateChanged(ItemEvent e) {
0545: final Object source = e.getSource();
0546:
0547: assert source == chkPublic || source == chkProtected
0548: || source == chkPackage;
0549: checkChkBoxesStates();
0550: checkAcceptability();
0551: }
0552:
0553: }
0554:
0555: /**
0556: */
0557: private Component createNameAndLocationPanel() {
0558: JPanel panel = new JPanel();
0559:
0560: final boolean askForClassName = singleClass;
0561:
0562: JLabel lblClassToTest = new JLabel();
0563: JLabel lblClassName = askForClassName ? new JLabel() : null;
0564: JLabel lblLocation = new JLabel();
0565:
0566: String classToTestKey = singlePackage ? "LBL_PackageToTest" //NOI18N
0567: : singleClass ? "LBL_ClassToTest" //NOI18N
0568: : "LBL_MultipleClassesSelected"; //NOI18N
0569:
0570: Mnemonics.setLocalizedText(lblClassToTest, NbBundle.getMessage(
0571: getClass(), classToTestKey));
0572: if (askForClassName) {
0573: Mnemonics.setLocalizedText(lblClassName, NbBundle
0574: .getMessage(getClass(), "LBL_ClassName")); //NOI18N
0575: }
0576: Mnemonics.setLocalizedText(lblLocation, NbBundle.getMessage(
0577: getClass(), "LBL_Location")); //NOI18N
0578:
0579: if (singlePackage || singleClass) {
0580: lblClassToTestValue = new JLabel();
0581: }
0582: if (askForClassName) {
0583: tfClassName = new ClassNameTextField();
0584: tfClassName.setChangeListener(this );
0585: }
0586: cboxLocation = new JComboBox();
0587:
0588: if (askForClassName) {
0589: lblClassName.setLabelFor(tfClassName);
0590: }
0591: lblLocation.setLabelFor(cboxLocation);
0592:
0593: if (lblClassToTestValue != null) {
0594: Font labelFont = javax.swing.UIManager.getDefaults()
0595: .getFont("TextField.font"); //NOI18N
0596: if (labelFont != null) {
0597: lblClassToTestValue.setFont(labelFont);
0598: }
0599: }
0600:
0601: panel.setLayout(new GridBagLayout());
0602:
0603: GridBagConstraints gbcLeft = new GridBagConstraints();
0604: gbcLeft.anchor = GridBagConstraints.WEST;
0605: gbcLeft.insets.bottom = 12;
0606: gbcLeft.insets.right = 6;
0607:
0608: GridBagConstraints gbcRight = new GridBagConstraints();
0609: gbcRight.anchor = GridBagConstraints.WEST;
0610: gbcRight.insets.bottom = 12;
0611: gbcRight.weightx = 1.0f;
0612: gbcRight.fill = GridBagConstraints.BOTH;
0613: gbcRight.gridwidth = GridBagConstraints.REMAINDER;
0614:
0615: if (lblClassToTestValue != null) {
0616: panel.add(lblClassToTest, gbcLeft);
0617: panel.add(lblClassToTestValue, gbcRight);
0618: } else {
0619: panel.add(lblClassToTest, gbcRight);
0620: }
0621: if (askForClassName) {
0622: panel.add(lblClassName, gbcLeft);
0623: panel.add(tfClassName, gbcRight);
0624: }
0625: gbcLeft.insets.bottom = 0;
0626: gbcRight.insets.bottom = 0;
0627: panel.add(lblLocation, gbcLeft);
0628: panel.add(cboxLocation, gbcRight);
0629:
0630: return panel;
0631: }
0632:
0633: /**
0634: */
0635: private void checkClassNameValidity() {
0636: if (tfClassName == null) {
0637: classNameValid = true;
0638: return;
0639: }
0640:
0641: String key = null;
0642: final int state = tfClassName.getStatus();
0643: switch (state) {
0644: case ClassNameTextField.STATUS_EMPTY:
0645: //PENDING - polish the message:
0646: key = "MSG_ClassnameMustNotBeEmpty"; //NOI18N
0647: break;
0648: case ClassNameTextField.STATUS_INVALID:
0649: //PENDING - polish the message:
0650: key = "MSG_InvalidClassName"; //NOI18N
0651: break;
0652: case ClassNameTextField.STATUS_VALID_NOT_DEFAULT:
0653: //PENDING - polish the message:
0654: key = "MSG_ClassNameNotDefault"; //NOI18N
0655: break;
0656: }
0657: if (state != ClassNameTextField.STATUS_VALID_NOT_DEFAULT) {
0658: setMessage(null, MSG_TYPE_CLASSNAME_NOT_DEFAULT);
0659: }
0660: setMessage((key != null) ? NbBundle.getMessage(getClass(), key)
0661: : null, MSG_TYPE_CLASSNAME_INVALID);
0662:
0663: classNameValid = (state == ClassNameTextField.STATUS_VALID)
0664: || (state == ClassNameTextField.STATUS_VALID_NOT_DEFAULT);
0665: }
0666:
0667: /**
0668: * This method gets called if status of contents of the Class Name
0669: * text field changes. See <code>STATUS_xxx</code> constants
0670: * in class <code>ClassNameTextField</code>.
0671: *
0672: * @param e event describing the state change event
0673: * (unused in this method)
0674: */
0675: public void stateChanged(ChangeEvent e) {
0676: checkClassNameValidity();
0677: checkAcceptability();
0678: }
0679:
0680: /**
0681: */
0682: private void checkAcceptability() {
0683: final boolean wasAcceptable = isAcceptable;
0684: isAcceptable = hasTargetFolders && classNameValid
0685: && checkBoxesOK;
0686: if (isAcceptable != wasAcceptable) {
0687: fireStateChange();
0688: }
0689: }
0690:
0691: /**
0692: * Are the values filled in the form acceptable?
0693: *
0694: * @see #addChangeListener
0695: */
0696: private boolean isAcceptable() {
0697: return isAcceptable;
0698: }
0699:
0700: /**
0701: * This method is called the first time this panel's children are painted.
0702: * By default, this method just calls {@link #adjustWindowSize()}.
0703: *
0704: * @param g <code>Graphics</code> used to paint this panel's children
0705: */
0706: @Override
0707: protected void paintedFirstTime(java.awt.Graphics g) {
0708: if (initialMessage != null) {
0709: displayMessage(initialMessage);
0710: initialMessage = null;
0711: }
0712: }
0713:
0714: /**
0715: * Displays a given message in the message panel and resizes the dialog
0716: * if necessary. If the message cannot be displayed immediately,
0717: * because of this panel not displayed (painted) yet, displaying the message
0718: * is deferred until this panel is painted.
0719: *
0720: * @param message message to be displayed, or <code>null</code> if
0721: * the currently displayed message (if any) should be
0722: * removed
0723: */
0724: private void setMessage(final String message, final int msgType) {
0725: String msgToDisplay = msgStack.setMessage(msgType, message);
0726: if (msgToDisplay == null) {
0727: return; //no change
0728: }
0729:
0730: /* display the message: */
0731: if (!isPainted()) {
0732: initialMessage = msgToDisplay;
0733: } else {
0734: displayMessage(msgToDisplay);
0735: }
0736: }
0737:
0738: /**
0739: * Displays a given message in the message panel and resizes the dialog
0740: * if necessary.
0741: *
0742: * @param message message to be displayed, or <code>null</code> if
0743: * the currently displayed message (if any) should be
0744: * removed
0745: * @see #adjustWindowSize()
0746: */
0747: private void displayMessage(String message) {
0748: if (message == null) {
0749: message = ""; //NOI18N
0750: }
0751:
0752: txtAreaMessage.setText(message);
0753: adjustWindowSize();
0754: }
0755:
0756: /**
0757: */
0758: private Component createMessagePanel() {
0759: txtAreaMessage = (JTextArea) GuiUtils.createMultilineLabel(""); //NOI18N
0760:
0761: Color color = UIManager.getColor("nb.errorForeground"); //NOI18N
0762: if (color == null) {
0763: color = new Color(89, 79, 191); //RGB suggested by Bruce in #28466
0764: }
0765: txtAreaMessage.setForeground(color);
0766:
0767: return txtAreaMessage;
0768: }
0769:
0770: /**
0771: * Creates a panel containing controls for settings code generation options.
0772: *
0773: * @return created panel
0774: */
0775: private Component createCodeGenPanel() {
0776:
0777: /* create the components: */
0778: String[] chkBoxIDs;
0779: JCheckBox[] chkBoxes;
0780: if (multipleClasses) {
0781: chkBoxIDs = new String[] { GuiUtils.CHK_PUBLIC,
0782: GuiUtils.CHK_PROTECTED, GuiUtils.CHK_PACKAGE,
0783: GuiUtils.CHK_PACKAGE_PRIVATE_CLASSES,
0784: GuiUtils.CHK_ABSTRACT_CLASSES,
0785: GuiUtils.CHK_EXCEPTION_CLASSES,
0786: GuiUtils.CHK_SUITES, GuiUtils.CHK_SETUP,
0787: GuiUtils.CHK_TEARDOWN, GuiUtils.CHK_METHOD_BODIES,
0788: GuiUtils.CHK_JAVADOC, GuiUtils.CHK_HINTS };
0789: } else {
0790: chkBoxIDs = new String[] {
0791: GuiUtils.CHK_PUBLIC,
0792: GuiUtils.CHK_PROTECTED,
0793: GuiUtils.CHK_PACKAGE,
0794: null, // CHK_PACKAGE_PRIVATE_CLASSES,
0795: null, // CHK_ABSTRACT_CLASSES,
0796: null, // CHK_EXCEPTION_CLASSES,
0797: null, // CHK_SUITES,
0798: GuiUtils.CHK_SETUP, GuiUtils.CHK_TEARDOWN,
0799: GuiUtils.CHK_METHOD_BODIES, GuiUtils.CHK_JAVADOC,
0800: GuiUtils.CHK_HINTS };
0801: }
0802: chkBoxes = GuiUtils.createCheckBoxes(chkBoxIDs);
0803: int i = 0;
0804: chkPublic = chkBoxes[i++];
0805: chkProtected = chkBoxes[i++];
0806: chkPackage = chkBoxes[i++];
0807: chkPackagePrivateClasses = chkBoxes[i++]; //may be null
0808: chkAbstractImpl = chkBoxes[i++]; //may be null
0809: chkExceptions = chkBoxes[i++]; //may be null
0810: chkGenerateSuites = chkBoxes[i++]; //may be null
0811: chkSetUp = chkBoxes[i++];
0812: chkTearDown = chkBoxes[i++];
0813: chkContent = chkBoxes[i++];
0814: chkJavaDoc = chkBoxes[i++];
0815: chkComments = chkBoxes[i++];
0816:
0817: /* create groups of checkboxes: */
0818: JComponent methodAccessLevels = GuiUtils
0819: .createChkBoxGroup(
0820: bundle
0821: .getString("JUnitCfgOfCreate.groupAccessLevels"), //NOI18N
0822: new JCheckBox[] { chkPublic, chkProtected,
0823: chkPackage });
0824: JComponent classTypes = null;
0825: JComponent optionalClasses = null;
0826: if (multipleClasses) {
0827: classTypes = GuiUtils.createChkBoxGroup(bundle
0828: .getString("JUnitCfgOfCreate.groupClassTypes"), //NOI18N
0829: new JCheckBox[] { chkPackagePrivateClasses,
0830: chkAbstractImpl, chkExceptions });
0831: optionalClasses = GuiUtils.createChkBoxGroup(bundle
0832: .getString("JUnitCfgOfCreate.groupOptClasses"), //NOI18N
0833: new JCheckBox[] { chkGenerateSuites });
0834: }
0835: JComponent optionalCode = GuiUtils.createChkBoxGroup(bundle
0836: .getString("JUnitCfgOfCreate.groupOptCode"), //NOI18N
0837: new JCheckBox[] { chkSetUp, chkTearDown, chkContent });
0838: JComponent optionalComments = GuiUtils.createChkBoxGroup(bundle
0839: .getString("JUnitCfgOfCreate.groupOptComments"), //NOI18N
0840: new JCheckBox[] { chkJavaDoc, chkComments });
0841:
0842: /* create the left column of options: */
0843: Box leftColumn = Box.createVerticalBox();
0844: leftColumn.add(methodAccessLevels);
0845: if (multipleClasses) {
0846: leftColumn.add(Box.createVerticalStrut(11));
0847: leftColumn.add(classTypes);
0848: } else {
0849: /*
0850: * This strut ensures that width of the left column is not limited.
0851: * If it was limited, the rigth column would not move when the
0852: * dialog is horizontally resized.
0853: */
0854: leftColumn.add(Box.createVerticalStrut(0));
0855: }
0856: leftColumn.add(Box.createVerticalGlue());
0857:
0858: /* create the right column of options: */
0859: Box rightColumn = Box.createVerticalBox();
0860: if (multipleClasses) {
0861: rightColumn.add(optionalClasses);
0862: rightColumn.add(Box.createVerticalStrut(11));
0863: }
0864: rightColumn.add(optionalCode);
0865: rightColumn.add(Box.createVerticalStrut(11));
0866: rightColumn.add(optionalComments);
0867: rightColumn.add(Box.createVerticalGlue());
0868:
0869: /* compose the final panel: */
0870: //JPanel jpCodeGen = new SizeRestrictedPanel(false, true);
0871: JPanel jpCodeGen = new JPanel();
0872: jpCodeGen.setLayout(new BoxLayout(jpCodeGen, BoxLayout.X_AXIS));
0873: jpCodeGen.add(leftColumn);
0874: jpCodeGen.add(Box.createHorizontalStrut(24));
0875: jpCodeGen.add(rightColumn);
0876:
0877: /* decorate the panel: */
0878: addTitledBorder(jpCodeGen, new Insets(12, 12, 11, 12), bundle
0879: .getString("JUnitCfgOfCreate.jpCodeGen.title"));//NOI18N
0880:
0881: /* tune the layout: */
0882: methodAccessLevels.setAlignmentX(0.0f);
0883: if (multipleClasses) {
0884: classTypes.setAlignmentX(0.0f);
0885: optionalClasses.setAlignmentX(0.0f);
0886: }
0887: optionalCode.setAlignmentX(0.0f);
0888: optionalComments.setAlignmentX(0.0f);
0889:
0890: return jpCodeGen;
0891: }
0892:
0893: /**
0894: * Adds a border and a title around a given component.
0895: * If the component already has some border, it is overridden (not kept).
0896: *
0897: * @param component component the border and title should be added to
0898: * @param insets insets between the component and the titled border
0899: * @param title text of the title
0900: */
0901: private static void addTitledBorder(JComponent component,
0902: Insets insets, String title) {
0903: Border insideBorder = BorderFactory.createEmptyBorder(
0904: insets.top, insets.left, insets.bottom, insets.right);
0905: Border outsideBorder = new TitledBorder(BorderFactory
0906: .createEtchedBorder(), title);
0907: component.setBorder(new CompoundBorder(outsideBorder,
0908: insideBorder));
0909: }
0910:
0911: /**
0912: */
0913: FileObject getTargetFolder() {
0914: Object selectedLocation = cboxLocation.getSelectedItem();
0915:
0916: if (selectedLocation == null) {
0917: return null;
0918: }
0919:
0920: if (selectedLocation instanceof SourceGroup) {
0921: return ((SourceGroup) selectedLocation).getRootFolder();
0922: }
0923: assert selectedLocation instanceof FileObject; //root folder
0924: return (FileObject) selectedLocation;
0925: }
0926:
0927: /**
0928: * Initializes form in the Test Settings panel of the dialog.
0929: */
0930: private void fillFormData() {
0931: final DataObject dataObj = nodes[0].getLookup().lookup(
0932: DataObject.class);
0933: final FileObject fileObj = dataObj.getPrimaryFile();
0934:
0935: if (singleClass) {
0936: assert nodes.length == 1;
0937:
0938: ClassPath cp = ClassPath.getClassPath(fileObj,
0939: ClassPath.SOURCE);
0940: String className = cp.getResourceName(fileObj, '.', false);
0941: lblClassToTestValue.setText(className);
0942:
0943: if (tfClassName != null) {
0944: String prefilledName = className + TEST_CLASS_SUFFIX;
0945: tfClassName.setText(prefilledName);
0946: tfClassName.setDefaultText(prefilledName);
0947: tfClassName.setCaretPosition(prefilledName.length());
0948: }
0949: } else if (singlePackage) {
0950: assert nodes.length == 1;
0951:
0952: ClassPath cp = ClassPath.getClassPath(fileObj,
0953: ClassPath.SOURCE);
0954: String packageName = cp.getResourceName(fileObj, '.', true);
0955: if (packageName.length() == 0) {
0956: packageName = NbBundle.getMessage(getClass(),
0957: "DefaultPackageName"); //NOI18N
0958: }
0959: lblClassToTestValue.setText(packageName);
0960: } else {
0961: //PENDING
0962: }
0963:
0964: setupLocationChooser(fileObj);
0965:
0966: checkClassNameValidity();
0967: }
0968:
0969: /**
0970: */
0971: private void setupLocationChooser(FileObject refFileObject) {
0972: Object[] targetFolders = TestUtil.getTestTargets(refFileObject);
0973: if (targetFolders.length != 0) {
0974: hasTargetFolders = true;
0975: cboxLocation.setModel(new DefaultComboBoxModel(
0976: targetFolders));
0977: cboxLocation.setRenderer(new LocationChooserRenderer());
0978: } else {
0979: hasTargetFolders = false;
0980: //PENDING - message text:
0981: String msgNoTargetsFound = NbBundle.getMessage(getClass(),
0982: refFileObject.isFolder() ? "MSG_NoTestTarget_Fo" //NOI18N
0983: : "MSG_NoTestTarget_Fi",//NOI18N
0984: refFileObject.getNameExt());
0985: setMessage(msgNoTargetsFound, MSG_TYPE_NO_TARGET_FOLDERS);
0986: disableComponents();
0987: }
0988: }
0989:
0990: /**
0991: * Renderer which specially handles values of type
0992: * <code>SourceGroup</code> and <code>FileObject</code>.
0993: * It displays display names of these objects, instead of their default
0994: * string representation (<code>toString()</code>).
0995: *
0996: * @see SourceGroup#getDisplayName()
0997: * @see FileUtil#getFileDisplayName(FileObject)
0998: */
0999: private final class LocationChooserRenderer extends JLabel
1000: implements ListCellRenderer, UIResource {
1001:
1002: public LocationChooserRenderer() {
1003: setOpaque(true);
1004: }
1005:
1006: public Component getListCellRendererComponent(JList list,
1007: Object value, int index, boolean isSelected,
1008: boolean cellHasFocus) {
1009: // #93658: GTK needs name to render cell renderer "natively"
1010: setName("ComboBox.listRenderer"); // NOI18N
1011:
1012: String text = value instanceof SourceGroup ? ((SourceGroup) value)
1013: .getDisplayName()
1014: : value instanceof FileObject ? FileUtil
1015: .getFileDisplayName((FileObject) value)
1016: : value.toString();
1017: setText(text);
1018:
1019: if (isSelected) {
1020: setBackground(list.getSelectionBackground());
1021: setForeground(list.getSelectionForeground());
1022: } else {
1023: setBackground(list.getBackground());
1024: setForeground(list.getForeground());
1025: }
1026:
1027: return this ;
1028: }
1029:
1030: // #93658: GTK needs name to render cell renderer "natively"
1031: @Override
1032: public String getName() {
1033: String name = super .getName();
1034: return name == null ? "ComboBox.renderer" : name; // NOI18N
1035: }
1036:
1037: }
1038:
1039: /**
1040: * Registers a change listener.
1041: * Registered change listeners are notified when acceptability
1042: * of values in the form changes.
1043: *
1044: * @param l listener to be registered
1045: * @see #isAcceptable
1046: * @see #removeChangeListener
1047: */
1048: private void addChangeListener(ChangeListener l) {
1049: if (changeListeners == null) {
1050: changeListeners = new ArrayList<ChangeListener>(3);
1051: }
1052: changeListeners.add(l);
1053: }
1054:
1055: /**
1056: * Unregisters the given change listener.
1057: * If the given listener has not been registered before, calling this
1058: * method does not have any effect.
1059: *
1060: * @param l change listener to be removed
1061: * @see #addChangeListener
1062: */
1063: private void removeChangeListener(ChangeListener l) {
1064: if (changeListeners != null && changeListeners.remove(l)
1065: && changeListeners.isEmpty()) {
1066: changeListeners = null;
1067: }
1068: }
1069:
1070: /**
1071: * Notifies all registered change listeners about a change.
1072: *
1073: * @see #addChangeListener
1074: */
1075: private void fireStateChange() {
1076: if (changeListeners != null) {
1077: ChangeEvent e = new ChangeEvent(this );
1078: for (Iterator i = changeListeners.iterator(); i.hasNext();) {
1079: ((ChangeListener) i.next()).stateChanged(e);
1080: }
1081: }
1082: }
1083:
1084: /**
1085: * Disables all interactive visual components of this dialog
1086: * except the OK, Cancel and Help buttons.
1087: */
1088: private void disableComponents() {
1089: final Stack<Container> stack = new Stack<Container>();
1090: stack.push(this );
1091:
1092: while (!stack.empty()) {
1093: Container container = stack.pop();
1094: Component comps[] = container.getComponents();
1095: for (int i = 0; i < comps.length; i++) {
1096: final java.awt.Component comp = comps[i];
1097:
1098: if (comp == txtAreaMessage) {
1099: continue;
1100: }
1101: if (comp instanceof JPanel) {
1102: JPanel panel = (JPanel) comp;
1103: stack.push(panel);
1104:
1105: final Border border = panel.getBorder();
1106: if (border != null) {
1107: disableBorderTitles(border);
1108: }
1109: continue;
1110: }
1111: comp.setEnabled(false);
1112: if (comp instanceof java.awt.Container) {
1113: Container nestedCont = (Container) comp;
1114: if (nestedCont.getComponentCount() != 0) {
1115: stack.push(nestedCont);
1116: }
1117: }
1118: }
1119: }
1120: }
1121:
1122: /**
1123: */
1124: private static void disableBorderTitles(Border border) {
1125:
1126: if (border instanceof TitledBorder) {
1127: disableBorderTitle((TitledBorder) border);
1128: return;
1129: }
1130:
1131: if (!(border instanceof CompoundBorder)) {
1132: return;
1133: }
1134:
1135: Stack<CompoundBorder> stack = new Stack<CompoundBorder>();
1136: stack.push((CompoundBorder) border);
1137: while (!stack.empty()) {
1138: CompoundBorder cb = stack.pop();
1139:
1140: Border b;
1141: b = cb.getOutsideBorder();
1142: if (b instanceof CompoundBorder) {
1143: stack.push((CompoundBorder) b);
1144: } else if (b instanceof TitledBorder) {
1145: disableBorderTitle((TitledBorder) b);
1146: }
1147:
1148: b = cb.getInsideBorder();
1149: if (b instanceof CompoundBorder) {
1150: stack.push((CompoundBorder) b);
1151: } else if (b instanceof TitledBorder) {
1152: disableBorderTitle((TitledBorder) b);
1153: }
1154: }
1155: }
1156:
1157: /**
1158: */
1159: private static void disableBorderTitle(TitledBorder border) {
1160: final Color color = UIManager
1161: .getColor("Label.disabledForeground"); //NOI18N
1162: if (color != null) {
1163: border.setTitleColor(color);
1164: }
1165: }
1166:
1167: private JLabel lblClassToTestValue;
1168: private ClassNameTextField tfClassName;
1169: private JTextArea txtAreaMessage;
1170: private JComboBox cboxLocation;
1171: private JCheckBox chkAbstractImpl;
1172: private JCheckBox chkComments;
1173: private JCheckBox chkContent;
1174: private JCheckBox chkExceptions;
1175: private JCheckBox chkGenerateSuites;
1176: private JCheckBox chkJavaDoc;
1177: private JCheckBox chkPackage;
1178: private JCheckBox chkPackagePrivateClasses;
1179: private JCheckBox chkProtected;
1180: private JCheckBox chkPublic;
1181: private JCheckBox chkSetUp;
1182: private JCheckBox chkTearDown;
1183:
1184: }
|