/*
 * @(#)PathRegistryUTest.java      0.9.0 15-MAR-2001 - 18:27
 *
 * Copyright (C) 2001,,2003 2002 Matt Albrecht
 * groboclown@users.sourceforge.net
 * http://groboutils.sourceforge.net
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a
 *  copy of this software and associated documentation files (the "Software"),
 *  to deal in the Software without restriction, including without limitation
 *  the rights to use, copy, modify, merge, publish, distribute, sublicense,
 *  and/or sell copies of the Software, and to permit persons to whom the 
 *  Software is furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included in 
 *  all copies or substantial portions of the Software. 
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL 
 *  THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
 *  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
 *  DEALINGS IN THE SOFTWARE.
 */

package net.sourceforge.groboutils.util.datastruct.v1;

import net.sourceforge.groboutils.junit.v1.MultiThreadedTestRunner;

import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;


/**
 * 
 *
 * @author    Matt Albrecht <a href="mailto:groboclown@users.sourceforge.net">groboclown@users.sourceforge.net</a>
 * @since     March 15, 2001 (Alpha 0.9.0)
 * @version   $Date: 2003/02/10 22:52:44 $
 */
public class PathRegistryUTest extends TestCase
{
    private static final Class THIS_CLASS = PathRegistryUTest.class;
    
    public PathRegistryUTest( String name )
    {
        super( name );
    }
    
    public static Test suite()
    {
        TestSuite suite = new TestSuite( THIS_CLASS );
        
        return suite;
    }
    
    public static void main( String[] args )
    {
        String[] name = { THIS_CLASS.getName() };
        
        // junit.textui.TestRunner.main( name );
        // junit.swingui.TestRunner.main( name );
        
        junit.textui.TestRunner.main( name );
    }
    
    protected void setUp() throws Exception
    {
        super.setUp();
        
        // set ourself up
    }
    
    
    protected void tearDown() throws Exception
    {
        // tear ourself down
        
        super.tearDown();
    }
    
    
    public void testInstantiate()
    {
        new PathRegistry( '.', true );
        new PathRegistry( '.', false );
    }
    
    
    public void testAddCaseSensitiveNotRecursive()
            throws PathAlreadyRegisteredException
    {
        PathRegistry pr = new PathRegistry( '/', true );

        // now try to get something not added
        notRegisteredTest( pr, "root" );

        Object o1 = new Object();
        pr.register( "root", o1, false, true );
        registeredRightTest( pr, "root", o1 );
        Object o2;
        
        // now try to get the path with wrong case
        caseNotRegisteredTest( pr, "root", '/' );
        
        // now try to get a sub path
        notRegisteredTest( pr, "root/empty" );
        
        // now try to get something not added
        notRegisteredTest( pr, "unknown" );

        // now try to get something not added
        notRegisteredTest( pr, "/unknown" );
        
        // now add a sub-path
        notRegisteredTest( pr, "root/deep" );
        Object o1a = new Object();
        pr.register( "root/deep", o1a, false, true );
        registeredRightTest( pr, "root/deep", o1a );
        
        // now try to get the path with wrong case
        caseNotRegisteredTest( pr, "root/deep", '/' );
        
        // now try to get a bad sub path
        notRegisteredTest( pr, "root/empty" );
        notRegisteredTest( pr, "root/deep/empty" );
        
        // now try to get something not added
        notRegisteredTest( pr, "unknown" );
    }
    
    
    public void testAddTwiceException()
            throws PathAlreadyRegisteredException
    {
        PathRegistry pr = new PathRegistry( '/', true );
        Object o1 = new Object();
        notRegisteredTest( pr, "root" );

        pr.register( "root", o1, false, true );
        registeredRightTest( pr, "root", o1 );
        
        // now try to register the same path again
        alreadyRegisteredTest( pr, "root" );
    }
    
    
    public void testAddCaseInsensitiveNotRecursive()
            throws PathAlreadyRegisteredException
    {
        PathRegistry pr = new PathRegistry( '/', true );
        notRegisteredTest( pr, "root" );

        Object o1 = new Object();
        pr.register( "root", o1, false, false );

        // now try to get with variations
        caseRegisteredRightTest( pr, "root", o1, '/' );
        
        // now try to get a sub path
        caseNotRegisteredTest( pr, "root/unknown", '/' );
    }
    
    
    public void testAddTwiceExceptionCaseInsensitive()
            throws PathAlreadyRegisteredException
    {
        PathRegistry pr = new PathRegistry( '/', true );
        notRegisteredTest( pr, "root" );
        Object o1 = new Object();
        pr.register( "root", o1, false, false );
        registeredRightTest( pr, "root", o1 );
        
        // now try to register the same path again
        caseAlreadyRegisteredTest( pr, "root", '/' );
    }
    
    
    public void testAddCaseSensitiveRecursive()
            throws PathAlreadyRegisteredException
    {
        PathRegistry pr = new PathRegistry( '/', true );
        notRegisteredTest( pr, "root" );
        Object o1 = new Object();
        pr.register( "root", o1, true, true );
        registeredRightTest( pr, "root", o1 );
        
        
        // now try to get the path with wrong case
        caseNotRegisteredTest( pr, "root", '/' );
        
        // now try to get a sub path
        registeredRightTest( pr, "root/empty", o1 );
        
        // now try to get something not added
        notRegisteredTest( pr, "unknown" );
        
        // now try to get something not added
        notRegisteredTest( pr, "unknown/subpath" );
    }
    
    
    public void testAddTwiceExceptionRecursive()
            throws PathAlreadyRegisteredException
    {
        PathRegistry pr = new PathRegistry( '/', true );
        notRegisteredTest( pr, "root" );
        Object o1 = new Object();
        pr.register( "root", o1, true, true );
        registeredRightTest( pr, "root", o1 );
        
        alreadyRegisteredTest( pr, "root" );
        alreadyRegisteredTest( pr, "root/empty" );
        alreadyRegisteredTest( pr, "root/whatever" );
        alreadyRegisteredTest( pr, "root/empty/whatever" );
    }

    
    public void testRemoveCaseSensitiveNotResursive()
            throws PathAlreadyRegisteredException,
            NoRegisteredComponentException
    {
        PathRegistry pr = new PathRegistry( '/', true );
        notRegisteredTest( pr, "root" );
        Object o1 = new Object();
        pr.register( "root", o1, true, true );
        registeredRightTest( pr, "root", o1 );
        
        cantRemoveEntryTest( pr, "unknown" );
        
        caseCantRemoveEntryTest( pr, "root", '/' );
        cantRemoveEntryTest( pr, "root/empty" );
        
        pr.remove( "root" );
        
        cantRemoveEntryTest( pr, "root" );
    }
    
    
    
    //----------------------------------------------------------
    // Protected helper methods
    
    
    /**
     * Assert that the given path has nothing registered to it.
     */
    protected void notRegisteredTest( PathRegistry pr, String path )
    {
        Object o = pr.get( path );
        assertTrue( "Must return null on bad path and sub-path '"+path+
            "', but found ["+o+"].", o == null );
    }
    
    
    /**
     * Assert that the given path's case variations are not registered
     * (however, the original path may be registered).
     */
    protected void caseNotRegisteredTest( PathRegistry pr, String path,
            char separator )
    {
        String[] variations = getCaseVariations( path, separator );
        for (int i = 0; i < variations.length; i++)
        {
            if (!path.equals( variations[i] ))
            {
                notRegisteredTest( pr, variations[i] );
            }
        }
    }
    
    
    /**
     * Assert that the given path is registered to the given object.
     */
    protected void registeredRightTest( PathRegistry pr, String path,
            Object expectedObj )
    {
        Object o2 = pr.get( path );
        assertNotNull( "Path must be registered.", o2 );
        assertEquals( "What gets put in, needs to equal what gets put out.",
            expectedObj, o2 );
    }
   
   
    /**
     * Assert that the given path and its case variations are registered/
     */
    protected void caseRegisteredRightTest( PathRegistry pr, String path,
            Object expectedObj, char separator )
    {
        registeredRightTest( pr, path, expectedObj );
        String[] variations = getCaseVariations( path, separator );
        for (int i = 0; i < variations.length; i++)
        {
            registeredRightTest( pr, variations[i], expectedObj );
        }
    }
    
    
    /**
     * Attempt to register an object at the given path.  The test succeeds
     * if it is already registered, and fails if it does not throw an
     * exception.
     */
    protected void alreadyRegisteredTest( PathRegistry pr, String path )
    {
        try
        {
            pr.register( path, new Object(), false, false );
        }
        catch (PathAlreadyRegisteredException pare)
        {
            // good check!
            return;
        }
        fail( "PathRegistry should have thrown an exception" );
    }
    
    
    /**
     * Pass if the correct exception is thrown when trying to remove
     * an entry that doesn't exist.
     */
    protected void cantRemoveEntryTest( PathRegistry pr, String path )
    {
        try
        {
            pr.remove( path );
        }
        catch (NoRegisteredComponentException nrce)
        {
            // good check!
            return;
        }
        fail( "PathRegistry should have thrown an exception" );
    }
    
    
    protected void caseCantRemoveEntryTest( PathRegistry pr, String path,
            char separator )
    {
        String[] variations = getCaseVariations( path, separator );
        for (int i = 0; i < variations.length; i++)
        {
            if (!path.equals( variations[i] ))
            {
                cantRemoveEntryTest( pr, variations[i] );
            }
        }
    }
    
    /**
     * Attempt to register the given path and its case variations.
     */
    protected void caseAlreadyRegisteredTest( PathRegistry pr, String path,
            char separator )
    {
        alreadyRegisteredTest( pr, path );
        String[] variations = getCaseVariations( path, separator );
        for (int i = 0; i < variations.length; i++)
        {
            alreadyRegisteredTest( pr, variations[i] );
        }
    }
    
    
    private static java.util.Random s_rand = new java.util.Random();
    private static synchronized int nextInt( int n )
    {
        // almost identical to JDK 1.2's Random.nextInt( n )
        if (n <= 0) throw new IllegalArgumentException("n must be > 0");
        
        if ((n & -n) == n) // n is a power of 2
        {
            // mask by 7fff... to force it to be positive
            return (int)((n * ((long)s_rand.nextInt() & 0x7fffffffL) ) >> 31);
        }
        
        int bits, val;
        do {
            // mask by 7fff... to force it to be positive
            bits = s_rand.nextInt() & 0x7fffffff;
            val = bits % n;
        } while(bits - val + (n-1) < 0);
        return val;
    }
    
    /**
     * Get a set of variations in case upper / lower of the given
     * path.
     */
    protected String[] getCaseVariations( String orig, char separator )
    {
        orig = orig.toLowerCase();
        char buff[] = orig.toCharArray();
        int len = buff.length;
        if (len <= 0)
        {
            return new String[0];
        }
        if (len == 1)
        {
            return new String[] { orig, orig.toUpperCase() };
        }
        
        java.util.Vector var = new java.util.Vector();
        for (int i = 0; i < len; i++)
        {
            int pos1 = nextInt( len );
            int pos2 = nextInt( len );
            char origChar1 = buff[ pos1 ];
            char origChar2 = buff[ pos2 ];
            if (origChar1 != separator)
                buff[ pos1 ] = Character.toUpperCase( origChar1 );
            String s = new String( buff );
            if (!s.equals( orig ))
                var.addElement( s );
            if (origChar2 != separator)
                buff[ pos2 ] = Character.toUpperCase( origChar2 );
            if (!s.equals( orig ))
                var.addElement( s );
            if (origChar1 != separator)
                buff[ pos1 ] = Character.toLowerCase( origChar1 );
            if (!s.equals( orig ))
                var.addElement( s );
            if (origChar2 != separator)
                buff[ pos2 ] = Character.toLowerCase( origChar2 );
            if (!s.equals( orig ))
                var.addElement( s );
            buff[ pos1 ] = origChar1;
            buff[ pos2 ] = origChar2;
        }
        String ss[] = new String[ var.size() ];
        var.copyInto( ss );
        return ss;
    }
}
