alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

What this is

This file is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Other links

The source code

/*
 *                 Sun Public License Notice
 *
 * The contents of this file are subject to the Sun Public License
 * Version 1.0 (the "License"). You may not use this file except in
 * compliance with the License. A copy of the License is available at
 * http://www.sun.com/
 *
 * The Original Code is NetBeans. The Initial Developer of the Original
 * Code is Sun Microsystems, Inc. Portions Copyright 1997-2004 Sun
 * Microsystems, Inc. All Rights Reserved.
 */
/*
 * CreateTestAction.java
 *
 * Created on January 19, 2001, 1:00 PM
 */

package org.netbeans.modules.junit;

import java.io.IOException;
import java.net.URL;
import java.text.MessageFormat;
import java.util.*;
import javax.swing.Action;
import org.netbeans.api.java.classpath.ClassPath;
import org.netbeans.api.java.queries.UnitTestForSourceQuery;
import org.netbeans.spi.java.classpath.support.ClassPathSupport;
import org.openide.DialogDisplayer;
import org.openide.ErrorManager;
import org.openide.NotifyDescriptor;
import org.openide.NotifyDescriptor.Message;
import org.openide.cookies.SaveCookie;
import org.openide.cookies.SourceCookie;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileUtil;
import org.openide.filesystems.Repository;
import org.openide.loaders.DataFolder;
import org.openide.loaders.DataObject;
import org.openide.loaders.DataObjectNotFoundException;
import org.openide.nodes.Node;
import org.openide.util.HelpCtx;
import org.openide.util.NbBundle;
import org.openide.util.actions.CookieAction;
import org.netbeans.modules.javacore.api.JavaModel;
import org.netbeans.jmi.javamodel.*;
import org.openide.cookies.EditCookie;
import org.openide.cookies.EditorCookie;
import org.openide.util.Utilities;



/** Action sensitive to some cookie that does something useful.
 *
 * @author  vstejskal, David Konecny
 * @author  Marian Petras
 * @author  Ondrej Rypacek
 */
public class CreateTestAction extends CookieAction {
    
    /* public members */
    public String getName() {
        return NbBundle.getMessage(CreateTestAction.class, "LBL_Action_CreateTest");
    }
    
    public HelpCtx getHelpCtx() {
        return new HelpCtx(CreateTestAction.class);
    }
    
    /* protected members */
    protected Class[] cookieClasses() {
        return new Class[] { DataFolder.class, SourceCookie.class };
    }
    
    /** Perform special enablement check in addition to the normal one.
     * protected boolean enable (Node[] nodes) {
     * if (! super.enable (nodes)) return false;
     * if (...) ...;
     * }
     */
    /*
    protected boolean enable (Node[] nodes) {
        if (nodes.length == 0) {
            return false;
        }
        for (int i=0; i < nodes.length; i++) {
            Cookie cookie = nodes[i].getCookie(type
        }
    }
     **/
    
    protected void initialize() {
        super.initialize();
        putProperty(Action.SHORT_DESCRIPTION, NbBundle.getMessage(CreateTestAction.class, "HINT_Action_CreateTest"));
    }
    
//      protected String iconResource() {
//          return "org/netbeans/modules/junit/resources/CreateTestActionIcon.gif";
//     }
    
    protected int mode() {
        return MODE_ANY;    // allow creation of tests for multiple selected nodes (classes, packages)
    }
    
    public boolean asynchronous() {
        return true; // yes, this action should run asynchronously
        // would be better to rewrite it to synchronous (running in AWT thread),
        // just replanning test generation to RequestProcessor
    }

    protected void performAction(Node[] nodes) {
        boolean folderSelected = isFolderSelected(nodes);
        
        DataObject doTestTempl = null;
        DataObject doSuiteTempl = null;
        
        // show configuration dialog
        // when dialog is canceled, escape the action
        if (!JUnitCfgOfCreate.configure(folderSelected))
            return;
        
        String temp = null;
        try {
            // get the Suite class template
            temp = NbBundle.getMessage(CreateTestAction.class,
                                       "PROP_testSuiteTemplate");       //NOI18N
            FileObject fo = Repository.getDefault().getDefaultFileSystem().findResource(temp);
            doSuiteTempl = DataObject.find(fo);
            
            // get the Test class template
            temp = NbBundle.getMessage(CreateTestAction.class,
                                       "PROP_testClassTemplate");       //NOI18N
            fo = Repository.getDefault().getDefaultFileSystem().findResource(temp);
            doTestTempl = DataObject.find(fo);
        }
        catch (DataObjectNotFoundException e) {
            String msg = NbBundle.getMessage(CreateTestAction.class, "MSG_template_not_found", temp);
            NotifyDescriptor descr = new Message(msg, NotifyDescriptor.ERROR_MESSAGE);
            DialogDisplayer.getDefault().notify(descr);
            return;
        }
        
        TestCreator.initialize();
        
        ProgressIndicator progress = new ProgressIndicator();
        progress.show();
        
        String msg = NbBundle.getMessage(CreateTestAction.class,
            "MSG_StatusBar_CreateTest_Begin"); //NOI18N
        progress.displayStatusText(msg);
        
        // results will be accumulated here
        CreationResults results = new CreationResults();

        try {

            // go through all nodes
            for(int nodeIdx = 0; nodeIdx < nodes.length; nodeIdx++) {
                if (hasParentAmongNodes(nodes, nodeIdx)) {
                    continue;
                }
                FileObject fo = TestUtil.getFileObjectFromNode(nodes[nodeIdx]);
                if (fo == null) {
                    TestUtil.notifyUser(NbBundle.getMessage(CreateTestAction.class, "MSG_file_from_node_failed"));
                    continue;
                }
                ClassPath cp = ClassPath.getClassPath(fo, ClassPath.SOURCE);
                if (cp == null) {
                    TestUtil.notifyUser(NbBundle.getMessage(CreateTestAction.class,
                        "MSG_no_project", fo));
                    continue;
                }
                ClassPath testClassPath = null;

                FileObject packageRoot = cp.findOwnerRoot(fo);
                String resource = cp.getResourceName(fo, '/', false);
                
                URL testRoot = UnitTestForSourceQuery.findUnitTest(packageRoot);
                if (testRoot == null) {
                    testClassPath = cp;
//                     TestUtil.notifyUser(NbBundle.getMessage(CreateTestAction.class,
//                         "MSG_no_tests_in_project", fo));
//                     continue;
                } else {
                    ArrayList cpItems = new ArrayList();
                    cpItems.add(ClassPathSupport.createResource(testRoot));
                    testClassPath = ClassPathSupport.createClassPath(cpItems);
                }

                try {
                    results.combine(createTests(testClassPath, fo, doTestTempl, doSuiteTempl, null, progress));
                } catch (CreationError e) {}

            }

        } finally {
            progress.hide();
        }


        if (!results.getSkipped().isEmpty()) {
            // something was skipped
            if (results.getSkipped().size()==1) {
                // one class? report it
                TestUtil.notifyUser
                    (NbBundle.getMessage(CreateTestAction.class,
                                         "MSG_skipped_class",
                                         ((JavaClass)results.getSkipped().iterator().next()).getName()),
                     NotifyDescriptor.INFORMATION_MESSAGE );

            } else {
                // more classes, report a general error
                TestUtil.notifyUser
                    (NbBundle.getMessage(CreateTestAction.class,
                                         "MSG_skipped_classes"),
                     NotifyDescriptor.INFORMATION_MESSAGE);
            }
                        
        } else if (results.getCreated().size()==1) {
            // created exactly one class, highlight it in the explorer
            // and open it in the editor
            DataObject dobj = (DataObject)results.getCreated().iterator().next();
            EditorCookie ec = (EditorCookie)dobj.getCookie(EditorCookie.class);
            if (ec != null) {
                ec.open();
            }
        }
    }
    
    /**
     * Detects whether at least one of the given nodes represents
     * a DataFolder.
     *
     * @return  true if at least one of the nodes represents
     *          a DataFolder; false otherwise
     */
    private static boolean isFolderSelected(Node[] nodes) {
        for (int i = 0; i < nodes.length; i++) {
            if (nodes[i].getCookie(DataFolder.class) != null) {
                return true;
            }
        }
        return false;
    }
    
    public static DataObject createSuiteTest(ClassPath testClassPath, 
                                             DataFolder folder,
                                             String suiteName,
                                             LinkedList suite, 
                                             DataObject doSuiteT, 
                                             LinkedList parentSuite,
                                             ProgressIndicator progress) 
    {
        
        // find correct package name
        FileObject fo = folder.getPrimaryFile();
        ClassPath cp = ClassPath.getClassPath(fo, ClassPath.SOURCE);
        assert cp != null : "SOURCE classpath was not found for "+fo;
        if (cp == null) {
            return null;
        }
        String pkg = cp.getResourceName(fo, '/', false);
        String dotPkg = pkg.replace('/', '.');
        String fullSuiteName = (suiteName != null)
                               ? pkg + '/' + suiteName
                               : TestUtil.convertPackage2SuiteName(pkg);
        
        // find the suite class, if it exists or create one from active template
        DataObject doTarget = getTestClass(testClassPath, fullSuiteName, doSuiteT);

        // generate the test suite for all listed test classes
        Collection targetClasses = TestUtil.getAllClassesFromFile(doTarget.getPrimaryFile());

        Iterator tcit = targetClasses.iterator();
        while (tcit.hasNext()) {
            JavaClass targetClass = (JavaClass)tcit.next();

            if (progress != null) {
                progress.setMessage(getCreatingMsg(targetClass.getName()), false);
            }

            try {
                TestCreator.createTestSuite(suite, dotPkg, targetClass);
                save(doTarget);
            } catch (Exception e) {
                ErrorManager.getDefault().log(ErrorManager.ERROR, e.toString());
                return null;
            }

            // add the suite class to the list of members of the parent
            if (null != parentSuite) {
                parentSuite.add(targetClass.getName());
            }
        }
        return doTarget;
    }
    
    private CreationResults createTests(ClassPath testClassPath, FileObject foSource, 
            DataObject doTestT, DataObject doSuiteT, LinkedList parentSuite,
            ProgressIndicator progress) throws CreationError {

                
        if (foSource.isFolder()) {
            // recurse to subfolders
            FileObject  childs[] = foSource.getChildren();
            LinkedList  mySuite = new LinkedList();
            progress.setMessage(getScanningMsg(foSource.getName()), false);

            CreationResults results = new CreationResults();

            for( int i = 0; i < childs.length; i++) {

                if (progress.isCanceled()) {
                    results.setAbborted();
                    break;
                }

                if (childs[i].isData() && !("java".equals(childs[i].getExt()))) {
                    continue;
                }

                results.combine(createTests(testClassPath, childs[i], doTestT, doSuiteT, mySuite, progress));
                if (results.isAbborted()) {
                    break;
                }

            }
            
            // if everything went ok, and the option is enabled,
            // create a suite for the folder . 
            if (!results.isAbborted() && ((0 < mySuite.size())&(JUnitSettings.getDefault().isGenerateSuiteClasses()))) {
                createSuiteTest(testClassPath, DataFolder.findFolder(foSource), (String) null, mySuite, doSuiteT, parentSuite, progress);
            }

            return results;

        } else {
            return createSingleTest(testClassPath, foSource, doTestT, doSuiteT, parentSuite, progress, true);
        }
    }
    
    public static CreationResults createSingleTest(ClassPath testClassPath,  
                                                   FileObject foSource, 
                                                   DataObject doTestT, 
                                                   DataObject doSuiteT, 
                                                   LinkedList parentSuite,
                                                   ProgressIndicator progress, 
                                                   boolean skipNonTestable) 
        throws CreationError
    {
        // create tests for all classes in the source 
        Resource srcRc = JavaModel.getResource(foSource);
        CreationResults result = new CreationResults(srcRc.getChildren().size());

        List srcChildren = srcRc.getChildren();
        Iterator scit = srcChildren.iterator();
        while (scit.hasNext()) {
            Element el = (Element)scit.next();
            if (el instanceof JavaClass) {
                JavaClass theClass = (JavaClass)el;
                if (!skipNonTestable || TestCreator.isClassTestable(theClass)) {
                    // find the test class, if it exists or create one
                    // from active template
                    DataObject doTarget 
                        = getTestClass(testClassPath, 
                                       TestUtil.getTestClassFullName(theClass.getSimpleName(),
                                                                     packageName(theClass.getName())), 
                                       doTestT);

                    // generate the test of current node
                    Resource tgtRc = JavaModel.getResource(doTarget.getPrimaryFile());
                    JavaClass targetClass = TestUtil.getMainJavaClass(tgtRc);
                    
                    if (progress != null) {
                        progress.setMessage(getCreatingMsg(targetClass.getName()), false);
                    }                    

                    TestCreator.createTestClass(srcRc, theClass, tgtRc, targetClass);
                    try {
                        save(doTarget);
                    } catch (java.io.IOException e) { throw new CreationError(e);}

                    result.addCreated(doTarget);

                    // add the test class to the parent's suite
                    if (null != parentSuite) {
                        parentSuite.add(targetClass.getName());
                    }
                } else {
                    if (progress != null) {
                        // ignoring because untestable
                        progress.setMessage(getIgnoringMsg(theClass.getName()), false);
                        result.addSkipped(theClass);
                    }
                }
            }
        }

        return result;

    }
    
    private static String packageName(String fullName) {
        int i = fullName.lastIndexOf('.');
        return fullName.substring(0, i > 0 ? i : 0);
    }

    private static DataObject getTestClass(ClassPath cp, String testClassName, DataObject doTemplate) {
        FileObject fo = cp.findResource(testClassName+".java");
        if (fo != null) {
            try {
                return DataObject.find(fo);
            } catch (DataObjectNotFoundException e) {
                ErrorManager.getDefault().log(ErrorManager.ERROR, e.toString());
                return null;
            }
        } else {
            // test file does not exist yet so create it:
            assert cp.getRoots().length == 1;
            FileObject root = cp.getRoots()[0];
            int index = testClassName.lastIndexOf('/');
            String pkg = index > -1 ? testClassName.substring(0, index) : "";
            String clazz = index > -1 ? testClassName.substring(index+1) : testClassName;
            try {
                // create package if it does not exist
                if (pkg.length() > 0) {
                    root = FileUtil.createFolder(root, pkg);
                }
                // instantiate template into the package
                return doTemplate.createFromTemplate(DataFolder.findFolder(root), clazz);
            } catch (IOException e) {
                ErrorManager.getDefault().log(ErrorManager.ERROR, e.toString());
                return null;
            }
        }
    }
    
    private boolean hasParentAmongNodes(Node[] nodes, int idx) {
        Node node;
        
        node = nodes[idx].getParentNode();
        while (null != node) {
            for(int i = 0; i < nodes.length; i++) {
                if (i == idx)
                    continue;
                if (node == nodes[i])
                    return true;
            }
            node = node.getParentNode();
        }
        return false;
    }
    
    private static void save(DataObject dO) throws IOException {
        SaveCookie sc = (SaveCookie) dO.getCookie(SaveCookie.class);
        if (null != sc)
            sc.save();
    }

    private static String getCreatingMsg(String className) {
        String fmt = NbBundle.getMessage(CreateTestAction.class,
                                         "FMT_generator_status_creating"); // NOI18N
        return MessageFormat.format(fmt, new Object[] { className });
    }

    private static String getScanningMsg(String sourceName) {
        String fmt = NbBundle.getMessage(CreateTestAction.class,
                                         "FMT_generator_status_scanning"); // NOI18N
        return MessageFormat.format(fmt, new Object[] { sourceName });
    }

    private static String getIgnoringMsg(String sourceName) {
        String fmt = NbBundle.getMessage(CreateTestAction.class,
                                         "FMT_generator_status_ignoring"); // NOI18N
        return MessageFormat.format(fmt, new Object[] { sourceName });
    }


    
    /**
     * Error thrown by failed test creation. 
     */
    public static final class CreationError extends Exception {
        public CreationError() {};
        public CreationError(Throwable cause) {
            super(cause);
        }
    };

    /**
     * Utility class representing the results of a test creation
     * process. It gatheres all tests (as DataObject) created and all
     * classes (as JavaClasses) for which no test was created.
     */
    public static class CreationResults {
        
        Set created; // Set< createdTest : DataObject >
        Set skipped; // Set< sourceClass : JavaClass >
        boolean abborted = false;

        public CreationResults() { this(20);}

        public CreationResults(int expectedSize) {
            created = new HashSet(expectedSize * 2 , 0.5f);
            skipped = new HashSet(expectedSize * 2 , 0.5f);
        }

        public void setAbborted() { 
            abborted = true;
        }
        
        /**
         * Returns true if the process of creation was abborted. The
         * result contains the results gathered so far.
         */
        public boolean isAbborted() { 
            return abborted;
        }


        /**
         * Adds a new entry to the set of created tests.
         * @return true if it was added, false if it was present before
         */
        public boolean addCreated(DataObject test) {
            return created.add(test);
        }

        /**
         * Adds a new JavaClass to the collection of
         * skipped classes.
         * @return true if it was added, false if it was present before
         */
        public boolean addSkipped(JavaClass c) {
            return skipped.add(c);
        }

        /**
         * Returns a set of classes that were skipped in the process.
         * @return Set
         */
        public Set getSkipped() {
            return skipped;
        }

        /**
         * Returns a set of test data objects created.
         * @return Set
         */ 
        public Set getCreated() {
            return created;
        }

        /**
         * Combines two results into one. If any of the results is an
         * abborted result, the combination is also abborted. The
         * collections of created and skipped classes are unified.
         * @param rhs the other CreationResult to combine into this
         */
        public void combine(CreationResults rhs) {
            if (rhs.abborted) {
                this.abborted = true;
            }

            this.created.addAll(rhs.created);
            this.skipped.addAll(rhs.skipped);
        }

    }
        
}
... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.