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-2003 Sun
 * Microsystems, Inc. All Rights Reserved.
 */

package org.openide.nodes;

import java.util.*;
import junit.framework.*;
import junit.textui.TestRunner;
import org.openide.nodes.*;
import org.openide.util.actions.SystemAction;

import org.netbeans.junit.*;
import org.openide.util.RequestProcessor;

/** A test to check behaviour of filter node. 
 *
 * @author Jaroslav Tulach
 */
public class FilterNodeTest extends NbTestCase {
    
    public FilterNodeTest(String name) {
        super(name);
    }
    
    public static void main(String[] args) {
        TestRunner.run(new NbTestSuite(FilterNodeTest.class));
    }
    

    /** Demonstates a bug in FilterNode.changeOriginal.
     */
    public void testChangeOriginalLeafToArray () {
        AbstractNode a = new AbstractNode (Children.LEAF);
        AbstractNode b = new AbstractNode (Children.LEAF);
        AbstractNode c = new AbstractNode (new Children.Array ());
    
        
        FN fn = new FN (a);
        fn.changeCh (b, true);
        fn.changeCh (c, true);
    }
    
    public void testYouCannotBeYourOwnOriginal () {
        FilterNode node = new FilterNode (Node.EMPTY);
        try {
            // this should not be allowed, because otherwise...
            node.changeOriginal (node, false);
            // ... these methods fail
            node.hashCode ();
            node.getName ();
        } catch (IllegalArgumentException ex) {
            // illegal argument exception is fine
        }
    }

    public void testHashCodeStackOverflowDetectionToFindOutTheProblemOfIssue46993 () {
        FilterNode node = new FilterNode (Node.EMPTY, null, org.openide.util.Lookup.EMPTY);
        FilterNode n2 = new FilterNode (node);
        FilterNode n3 = new FilterNode (n2);
        
        
        
        class FireL extends NodeAdapter {
            private boolean thrown;
            public void propertyChange (java.beans.PropertyChangeEvent ev) {
                thrown = true;
                throw new IllegalStateException ("my");
            }
        }
        FireL f = new FireL ();
        // trick for proper listener ordering
        n3.addNodeListener (f);
        n3.removeNodeListener (f);
        node.addNodeListener (f);
        

        try {
            node.changeOriginal (n3, false);
        } catch (IllegalStateException ex) {
            assertTrue ("Really thrown ex from my listener", f.thrown);
            assertEquals ("my", ex.getMessage ());
        }
        
        try {
            n3.hashCode ();
            fail ("This has to throw an error");
        } catch (/*StackOverflow*/Error err) {
            if (! (err instanceof StackOverflowError)) {
                fail ("Wrong class: " + err.getClass ());
            }
            assertIdentityHashCode (err.getMessage (), node);
            assertIdentityHashCode (err.getMessage (), n2);
            assertIdentityHashCode (err.getMessage (), n3);
        }
    }
    
    private static void assertIdentityHashCode (String msg, Object obj) {
        String hex = Integer.toString (System.identityHashCode (obj), 16);
        int indx = msg != null ? msg.indexOf (hex) : -1;
        if (indx == -1) {
            fail ("Message <" + msg + "> should contain identityHashCode of " + obj + " which is " + hex + "\nAre you sure you are running tests with assertions enabled!?");
        }
    }
    
    
    /** Demonstates a bug in FilterNode.changeOriginal.
     */
    public void testChangeOriginalArrayToLeaf () {
        AbstractNode a = new AbstractNode (Children.LEAF);
        AbstractNode b = new AbstractNode (new Children.Array ());
        AbstractNode c = new AbstractNode (new Children.Array ());
    
        
        FN fn = new FN (c);
        fn.changeCh (b, true);
        fn.changeCh (a, true);
    }

    /** See issue #28198. */
    public void testEquals() {
        Node original = new AbstractNode(Children.LEAF);
        FilterNode filter = new FilterNode(original);

        // Both has to return same value.
        assertTrue("Equals of filter node and its original is not symmetric",
            (filter.equals(original)) == (original.equals(filter)));
    }
    
    public void testHashCodeAndEquals () {
        Node original = new AbstractNode(Children.LEAF);
        FilterNode filter = new FilterNode(original);
        
        assertTrue ("They are equal", filter.equals (original));
        assertTrue ("In both directions", original.equals (filter));
        assertEquals ("Have the same hashcode", original.hashCode(), filter.hashCode ());
    }
    
    /** Get actions are correctly propagated.
     */
    public void testGetActions () {
        final ArrayList contextActions = new ArrayList ();
        final ArrayList actions = new ArrayList ();
        
        class AA extends javax.swing.AbstractAction {
            public void actionPerformed (java.awt.event.ActionEvent ev) {
            }
        }
        
        contextActions.add (new AA ());
        contextActions.add (null);
        contextActions.add (new AA ());
        
        actions.add (new AA ());
        actions.add (new AA ());
        
        final javax.swing.Action pref = new AA ();
        
        AbstractNode n = new AbstractNode (Children.LEAF) {
            public javax.swing.Action[] getActions (boolean context) {
                ArrayList l = context ? contextActions : actions;
                return (javax.swing.Action[])l.toArray (
                    new javax.swing.Action[0]
                );
            }
            
            public javax.swing.Action getPreferredAction () {
                return pref;
            }
        };
        
        FilterNode fn = new FilterNode (n);
        
        
        assertEquals ("Same context actions", contextActions, Arrays.asList (fn.getActions(true)));
        assertEquals ("Same actions", actions, Arrays.asList (fn.getActions(false)));
        assertEquals ("Same preffered action", pref, fn.getPreferredAction());
        
        
        fn = new FilterNode (n) {
            public SystemAction getDefaultAction () {
                return SystemAction.get (org.openide.actions.OpenAction.class);
            }
            
            public SystemAction[] getActions () {
                return new SystemAction[] { getDefaultAction () };
            }
        };
        
        assertEquals ("Overriding getDefaultAction wins", fn.getDefaultAction (), fn.getPreferredAction());
        assertEquals ("Overriding getActions wins", Arrays.asList (fn.getActions()), Arrays.asList (fn.getActions(false)));
        assertEquals ("Same context actions", contextActions, Arrays.asList (fn.getActions(true)));
        
        
        fn = new FilterNode (n) {
            public SystemAction getDefaultAction () {
                return SystemAction.get (org.openide.actions.OpenAction.class);
            }
            
            public SystemAction[] getContextActions () {
                return new SystemAction[] { getDefaultAction () };
            }
        };
        
        assertEquals ("Overriding getDefaultAction wins", fn.getDefaultAction (), fn.getPreferredAction());
        assertEquals ("Same actions", actions, Arrays.asList (fn.getActions(false)));
        assertEquals ("Overriding getContextActions wins", Arrays.asList (fn.getContextActions()), Arrays.asList (fn.getActions (true)));
    }
    
    public void testUpdateLeaf () {
        AbstractNode a = new AbstractNode (Children.LEAF);
        FilterNode fn = new FilterNode (a);
        assertEquals ("Children is leaf", Children.LEAF, fn.getChildren ());
    
        a.setChildren(new Children.Array ());
        assertFalse("Children of FilterNode not updated", fn.isLeaf());
        assertTrue ("Children are not leaf", fn.getChildren () != Children.LEAF);
        
        class Counter extends NodeAdapter {
            public int cnt;
            
            public void propertyChange (java.beans.PropertyChangeEvent ev) {
                if (Node.PROP_LEAF.equals (ev.getPropertyName())) {
                    cnt++;
                }
            }
        }
        
        Counter counter = new Counter ();
        
        fn.addNodeListener(counter);
        a.setChildren (Children.LEAF);
      
        assertEquals ("Children is leaf", Children.LEAF, fn.getChildren ());
        assertTrue ("Now it is LEAF again", fn.isLeaf ());
        assertEquals ("One change", 1, counter.cnt);
        
        a.setChildren (new Children.Array ());
        assertFalse ("Again has children", fn.isLeaf ());
        assertEquals ("Another change", 2, counter.cnt);
        
    }

    
    public void testUpdateLeafWithProvidedChildren () {
        AbstractNode node = new AbstractNode (Children.LEAF);
        FilterNode fn = new FilterNode (node, new Children.Array ());
        
        assertFalse ("filter node is not leaf, it has Array children", fn.isLeaf ());
        
        
        node = new AbstractNode (new Children.Array ());
        fn = new FilterNode (node, Children.LEAF);
        
        assertTrue ("filter node is leaf as children were provided", fn.isLeaf ());
    }
    
    public void testIsLeafCanBeCalledWhenAnotherThreadHoldsALock () throws Exception {
        final FilterNode fn = new FilterNode (Node.EMPTY);
        final RequestProcessor rp = new RequestProcessor ("Will deadlock");
        
        assertTrue ("Is leaf", fn.isLeaf ());
        
        class BlockInReadAccess implements Runnable {
            int cnt;
            
            public synchronized void run () {
                if (cnt++ == 0) {
                    Children.MUTEX.readAccess(this);
                    return;
                }
                
                try {
                    notify ();
                    wait ();
                } catch (InterruptedException ex) {
                    ex.printStackTrace();
                }
                cnt = -1;
                notify ();
            }
        }
            
        BlockInReadAccess b = new BlockInReadAccess ();
        synchronized (b) {
            rp.post (b);
            b.wait ();
            // now the task is blocked in read access
        }
        
        assertTrue ("Is leaf can be called", fn.isLeaf ());
        
        synchronized (b) {
            b.notify ();
            b.wait ();
        }
        
        assertEquals ("B finished", -1, b.cnt);
    }

    public void testIsLeafDoesNotChangeWhileInReadAcccess () throws Exception {
	AbstractNode a = new AbstractNode (Children.LEAF);
        final FilterNode fn = new FilterNode (a);
        final RequestProcessor rp = new RequestProcessor ("Will deadlock");
        
        assertTrue ("Is leaf", fn.isLeaf ());

	// change the original children so it have to be updated in fn
        a.setChildren(new Children.Array ());
        
        class ReadAccess implements Runnable {
            public void run () {
                assertTrue (
                    "It still claims that it is leaf because it cannot call setChildren to update, " +
                    " because that would upgrade the lock from read to write and that is not allowed", fn.isLeaf ());
            }
        }
        Children.MUTEX.readAccess (new ReadAccess ());
        
            
        assertFalse ("But as soon as the read access ends it is updated", fn.isLeaf ());
    }

    public void testGetSetValue() {
        AbstractNode node = new AbstractNode (Children.LEAF);

        FN fn_no = new FN (node);
        fn_no.disableDel(FN.DELEGATE_SET_VALUE | FN.DELEGATE_GET_VALUE);

        FN fn_get = new FN (node);
        fn_get.disableDel(FN.DELEGATE_SET_VALUE);

        FN fn_set = new FN (node);
        fn_set.disableDel(FN.DELEGATE_GET_VALUE);

        FilterNode fn_both = new FilterNode (node);
        
        node.setValue("val1", "item1");
        assertTrue("Should not delegate getValue", fn_no.getValue("val1") == null);
        assertEquals("Should delegate getValue", "item1", fn_get.getValue("val1"));
        assertTrue("Should not delegate getValue", fn_set.getValue("val1") == null);
        assertEquals("Should delegate getValue", "item1", fn_both.getValue("val1"));

        fn_no.setValue("val1", "xxx");
        assertEquals("Should have the value", "xxx", fn_no.getValue("val1"));
        assertEquals("Should not propagate setValue", "item1", node.getValue("val1"));

        fn_get.setValue("val1", "xxx");
        assertEquals("Should still detegate getValue", "item1", fn_get.getValue("val1"));
        assertEquals("Should not propagate setValue", "item1", node.getValue("val1"));

        fn_set.setValue("val1", "item2");
        assertTrue("Should not delegate getValue", fn_set.getValue("val1") == null);
        assertEquals("Should propagate setValue", "item2", node.getValue("val1"));

        fn_both.setValue("val1", "item3");
        assertTrue("Should still detegate getValue", fn_both.getValue("val3") == null);
        assertEquals("Should propagate setValue", "item3", node.getValue("val1"));
    }
    
    /** A class that allows access to protected methods.
     */
    private static final class FN extends FilterNode {
        public FN (Node orig) {
            super (orig);
        }
        
        public void changeCh (Node n, boolean children) {
            changeOriginal (n, children);
        }
        
        public void disableDel (int mask) {
            disableDelegation(mask);
        }

    }
}

... 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.