/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// Package
///////////////
package org.apache.jena.util;


// Imports
///////////////
import java.util.*;

import junit.framework.TestCase;



/**
 * <p>
 * Unit tests for one-to-many map
 * </p>
 */
public class TestOneToManyMap 
    extends TestCase
{
    // Constants
    //////////////////////////////////

    // Static variables
    //////////////////////////////////

    // Instance variables
    //////////////////////////////////

    private String s0 = "s0";
    private String s1 = "s1";
    private String s2 = "s2";
    private String s3 = "s3";
    private String s4 = "s4";

    
    // Constructors
    //////////////////////////////////

    // External signature methods
    //////////////////////////////////

    @Override public void setUp() {}
    
    @Override public void tearDown() {}
    
    public void testConstruct0() {
        // the types of these maps
        OneToManyMap<String, Integer> map0 = new OneToManyMap<>();
        assertNotNull( map0 );
        
        assertTrue( map0.isEmpty() );
        
        OneToManyMap<String, Integer> map1 = new OneToManyMap<>( map0 );
        assertNotNull( map1 );
        assertTrue( map1.isEmpty() );
    }
    
    public void testConstruct1() {
        OneToManyMap<String, String> map0 = new OneToManyMap<>();
        
        map0.put( s0, s1 );
        assertTrue( map0.contains( s0, s1 ) );
        
        OneToManyMap<String, String> map1 = new OneToManyMap<>( map0 );
        assertTrue( map1.contains( s0, s1 ) );
        
        map0.put( s0, s2 );
        assertTrue( map0.contains( s0, s2 ) );
        assertFalse( map1.contains( s0, s2 ) );
        assertFalse( map0.contains( s1, s2 ) );
    }
    
    public void testClear() {
        OneToManyMap<String, String> map0 = new OneToManyMap<>();
        
        map0.put( s0, s1 );
        assertTrue( map0.contains( s0, s1 ) );
        assertFalse( map0.isEmpty() );
        
        map0.clear();
        assertFalse( map0.contains( s0, s1 ) );
        assertTrue( map0.isEmpty() );
    }

    public void testContainsKey() {
        OneToManyMap<String, String> map0 = new OneToManyMap<>();
        assertFalse( map0.containsKey( s0 ) );
        assertFalse( map0.containsKey( s1 ) );
        map0.put( s0, s1 );
        assertTrue( map0.containsKey( s0 ) );
        assertFalse( map0.containsKey( s1 ) );
    }
    
    public void testContainsValue() {
        OneToManyMap<String, String> map0 = new OneToManyMap<>();
        assertFalse( map0.containsValue( s0 ) );
        assertFalse( map0.containsValue( s1 ) );
        assertFalse( map0.containsValue( s2 ) );
        map0.put( s0, s1 );
        assertFalse( map0.containsValue( s0 ) );
        assertTrue( map0.containsValue( s1 ) );
        assertFalse( map0.containsValue( s2 ) );
        map0.put( s0, s2 );
        assertFalse( map0.containsValue( s0 ) );
        assertTrue( map0.containsValue( s1 ) );
        assertTrue( map0.containsValue( s2 ) );
    }
    
    public void testContains() {
        OneToManyMap<String, String> map0 = new OneToManyMap<>();
        assertFalse( map0.contains( s0, s1 ) );
        assertFalse( map0.contains( s0, s2 ) );
        assertFalse( map0.contains( s1, s2 ) );
        map0.put( s0, s1 );
        assertTrue( map0.contains( s0, s1 ) );
        assertFalse( map0.contains( s0, s2 ) );
        assertFalse( map0.contains( s1, s2 ) );
        map0.put( s0, s2 );
        assertTrue( map0.contains( s0, s1 ) );
        assertTrue( map0.contains( s0, s2 ) );
        assertFalse( map0.contains( s1, s2 ) );
    }
    
    public void testEntrySet() {
        OneToManyMap<String, String> map0 = new OneToManyMap<>();
        map0.put( s0, s1 );
        map0.put( s0, s2 );
        map0.put( s3, s4 );
        
        boolean s0s1 = false;
        boolean s0s2 = false;
        boolean s3s4 = false;

        for ( Map.Entry<String, String> e : map0.entrySet() )
        {
            if ( e.getKey().equals( s0 ) && e.getValue().equals( s1 ) )
            {
                s0s1 = true;
            }
            else if ( e.getKey().equals( s0 ) && e.getValue().equals( s2 ) )
            {
                s0s2 = true;
            }
            else if ( e.getKey().equals( s3 ) && e.getValue().equals( s4 ) )
            {
                s3s4 = true;
            }
            else
            {
                throw new IllegalArgumentException( "unexpected: " + e );
            }
        }
        
        assertTrue( s0s1 );
        assertTrue( s0s2 );
        assertTrue( s3s4 );
    }
    
    public void testEquals() {
        OneToManyMap<String, String> map0 = new OneToManyMap<>();
        map0.put( s0, s1 );
        map0.put( s0, s2 );
        map0.put( s3, s4 );
        OneToManyMap<String, String> map1 = new OneToManyMap<>();
        map1.put( s3, s4 );
        map1.put( s0, s1 );
        map1.put( s0, s2 );
        OneToManyMap<String, String> map2 = new OneToManyMap<>();
        map2.put( s0, s2 );
        map2.put( s3, s4 );
        
        assertTrue( map0.equals( map1 ) );
        assertTrue( map1.equals( map0 ) );
        assertTrue( map0.hashCode() == map1.hashCode() );
        
        assertFalse( map0.equals( map2 ));
        assertFalse( map2.equals( map0 ));
    }
    
    public void testGet() {
        OneToManyMap<String, String> map0 = new OneToManyMap<>();
        assertNull( map0.get( s0 ));
        map0.put( s0, s1 );
        assertEquals( s1, map0.get( s0 ));
        map0.put( s0, s2 );
        assertTrue( map0.get( s0 ).equals( s1 ) ||
                    map0.get( s0 ).equals( s2 ));
    }
    
    public void testGetAll() {
        OneToManyMap<String, String> map0 = new OneToManyMap<>();
        Iterator<String> i = map0.getAll(s0);
        assertNotNull( i );
        assertFalse( i.hasNext() );

        map0.put( s0, s1 );
        i = map0.getAll(s0);
        assertNotNull( i );
        assertTrue( i.hasNext() );
        assertEquals( s1, i.next() );
        assertFalse( i.hasNext() );
        
        map0.put( s0, s2 );
        i = map0.getAll(s0);
        assertNotNull( i );
        boolean founds1 = false, founds2 = false;
        while (i.hasNext()) {
            Object x = i.next();
            if (x.equals(s1)) {
                founds1 = true;
            }
            else if (x.equals(s2)) {
                founds2 = true;
            }
            else {
                throw new IllegalArgumentException( x.toString() );
            }
        }
        assertTrue( founds1 );
        assertTrue( founds2 );
    }
    
    public void testKeySet() {
        OneToManyMap<String, String> map0 = new OneToManyMap<>();
        Set<String> keys = new HashSet<>();
        assertEquals( keys, map0.keySet() );
        
        map0.put( s0, s1 );
        keys.add( s0 );
        assertEquals( keys, map0.keySet() );
        
        map0.put( s2, s1 );
        keys.add( s2 );
        assertEquals( keys, map0.keySet() );   
    }
    
    public void testPutAll0() {
        OneToManyMap<String, String> map0 = new OneToManyMap<>();
        map0.put( s0, s1 );
        map0.put( s0, s2 );
        map0.put( s3, s4 );
        
        OneToManyMap<String, String> map1 = new OneToManyMap<>();
        map1.put( s0, s2 );
        map1.put( s3, s4 );
        map1.put( s0, s1 );

        OneToManyMap<String, String> map2 = new OneToManyMap<>();
        map2.putAll( map1 );
        assertEquals( map0, map2 );
    }
    
    public void testPutAll1() {
        OneToManyMap<String, String> map0 = new OneToManyMap<>();
        map0.put( s0, s1 );
        map0.put( s3, s4 );
        
        Map<String, String> map1 = new HashMap<>();
        map1.put( s3, s4 );
        map1.put( s0, s1 );

        OneToManyMap<String, String> map2 = new OneToManyMap<>();
        map2.putAll( map1 );
        assertEquals( map0, map2 );
    }
    
    public void testRemove0() {
        OneToManyMap<String, String> map0 = new OneToManyMap<>();
        map0.put( s0, s1 );
        map0.put( s3, s4 );
        
        map0.remove( s0 );
        map0.remove( s3 );
        
        assertTrue( map0.isEmpty() );
    }
    
    public void testRemove1() {
        OneToManyMap<String, String> map0 = new OneToManyMap<>();
        map0.put( s0, s1 );
        map0.put( s0, s2 );
        map0.put( s3, s4 );
        
        map0.remove( s0, s2 );
        map0.remove( s3, s4 );
        
        assertFalse( map0.isEmpty() );
        
        map0.remove( s0, s1 );
        assertTrue( map0.isEmpty() );
    }
    
    public void testSize() {
        OneToManyMap<String, String> map0 = new OneToManyMap<>();
        assertEquals( 0, map0.size() );
        map0.put( s0, s1 );
        assertEquals( 1, map0.size() );
        map0.put( s0, s2 );
        assertEquals( 2, map0.size() );
        map0.put( s3, s4 );
        assertEquals( 3, map0.size() );
        map0.remove( s0, s2 );
        assertEquals( 2, map0.size() );
        map0.remove( s3, s4 );
        assertEquals( 1, map0.size() );
        map0.remove( s0, s1 );
        assertEquals( 0, map0.size() );
    }

    public void testValues() {
        OneToManyMap<String, String> map0 = new OneToManyMap<>();
        Set<String> vals = new HashSet<>();
        assertEquals( vals, map0.values() );
        
        map0.put( s0, s1 );
        vals.add( s1 );
        assertEquals( vals, map0.values() );
        
        map0.put( s2, s1 );
        assertEquals( vals, map0.values() );   
        
        map0.put( s2, s3 );
        vals.add( s3 );
        assertEquals( vals, map0.values() );   
    }
    
    // Internal implementation methods
    //////////////////////////////////

    //==============================================================================
    // Inner class definitions
    //==============================================================================

}
