/*
 * [TestConvertSet.java]
 *
 * Summary: Convert a Set.
 *
 * Copyright: (c) 2012-2017 Roedy Green, Canadian Mind Products, http://mindprod.com
 *
 * Licence: This software may be copied and used freely for any purpose but military.
 *          http://mindprod.com/contact/nonmil.html
 *
 * Requires: JDK 1.8+
 *
 * Created with: JetBrains IntelliJ IDEA IDE http://www.jetbrains.com/idea/
 *
 * Version History:
 *  1.0 2012-02-02
 */
package com.mindprod.example;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

import static java.lang.System.*;

interface Dog
    {
    /**
     * what is this dog called?
     */
    String getName();
    }

/**
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 * <p/>
 * Convert a Set.
 *
 * @author Roedy Green, Canadian Mind Products
 * @version 1.0 2012-02-02
 * @since 2012-02-02
 */
public final class TestConvertSet
    {
    /**
     * copy elements of set from to set to
     * Would let you copy Set<Dalmatian> to Set<Dog> or Set<Dog> to Set<Mammal>
     *
     * @param from source Set
     * @param to   target Set
     */
    private static void copy( Set<? extends Dog> from, Set<? super Dog> to )
        {
        to.addAll( from );
        }

    /**
     * list a Set of Dogs
     */
    private static void showDogs1( Set<Dog> dogs )
        {
        out.println( "--showdogs1--" );
        for ( Dog dog : dogs )
            {
            out.println( dog.getName() );
            }
        }

    /**
     * list a Set of Dogs, alternate
     */
    private static void showDogs2( Set<? extends Dog> dogs )
        {
        out.println( "--showdogs2--" );
        for ( Dog dog : dogs )
            {
            out.println( dog.getName() );
            }
        }

    /**
     * Demonstrate two ways to change the type of a generic set.
     *
     * @param args not used
     */
    public static void main( String[] args )
        {
        // We have a Set of Dalmatians each of which implements the Dog interface.
        // We have showDogs1 method that wants a Set of Dogs, as a parameter and will not accept a Set of Dalmatians.
        // How can we convert our Set of Dalmatians to a Set of Dogs?
        Set<Dalmatian> someDalmatians = new HashSet<>( Arrays.asList(
                new Dalmatian( "Spot", 45 ),
                new Dalmatian( "Sheldon", 149 ),
                new Dalmatian( "Peter", 17 ) ) );
        // showDogs1( someDalmatians );  // will not compile
        // This conversion method creates a new HashSet and adds the elements one by one to it.
        // May not collapse to new HashSet<>.
        Set<Dog> someDogs1 = new HashSet<Dog>( someDalmatians );
        // someDogs currently contains only Dalmatians, but there is nothing so stop you from adding other breeds to
        // the set.
        showDogs1( someDogs1 );
        // The following conversion method does not create a new HashSet. It just gives you a different way of
        // looking at the original
        // Set of Dalmatians.  someDogs2 is composed purely of some type (unspecified but actually Dalmatian) that
        // implements Dog.
        // You cannot pollute the original Dalmatian set with other breeds, thus someDogs2 becomes read-only.
        // It would be too complicated for the compiler to track whether you added only Dalmatians.
        Set<? extends Dog> someDogs2 = someDalmatians;
        // Unfortunately, someDogs2 Set<? extends Dog> is not exactly the same as Set<Dog>, so you cannot
        // pass someDogs2 to showDogs1, but you can pass it to showDogs2
        showDogs2( someDogs2 );
        // and surprise, you can also pass someDogs1 to showDogs2
        showDogs2( someDogs1 );
        // You can only use this showDogs2 conversion technique if you can slightly modify the consumer
        // method to accept Set<? extends Dog> dogs
        // It is actually even simpler than this:
        showDogs2( someDalmatians );  // works fine without the intermediate someDogs2.
        // Use of super
        Set<Dalmatian> from = someDalmatians;
        Set<Dog> to = new HashSet<>( 5 );
        copy( from, to );
        showDogs1( to );
        }
    }

class Dalmatian implements Dog
    {
    /**
     * what is this dog called?
     */
    private final String name;

    /**
     * how many spots does this Dalmatian have?
     */
    private final int spots;

    /**
     * constructor
     *
     * @param spots how many spots the dog has
     */
    Dalmatian( String name, int spots )
        {
        this.name = name;
        this.spots = spots;
        }

    /**
     * how many spots does this Dalmatian have?
     */
    int getSpots()
        {
        return spots;
        }

    /**
     * What is this dog called?  Implements pubic Dog.getName method.
     */
    public String getName()
        {
        return name;
        }
    }