import java.util.List;
import java.util.ArrayList;
import java.util.ListIterator;
import java.util.Arrays;
import java.util.Collections;
import java.util.Scanner;

/**
 * Demonstrate some of the ways of making unmodifiable lists.
 * 
 * @author Mark Young
 */
public class FixedLists {
    
    public static void main(String[] args) {
        // create variables
        List<Integer> nums;
        List<String> words;
        ListIterator<String> it;
        
        // show that normal lists can be modified
        System.out.println("Normal Lists are fully modifiable");
        words = new ArrayList<>();
        words.add("words");
        words.add("here");
        words.add("too");
        System.out.println("Normal List: " + words);
        it = words.listIterator();
        try {
            String next = it.next();
            System.out.println("--> " + next);
            it.add("NEW");
            System.out.println("added NEW");
        } catch (UnsupportedOperationException uoe) {
            System.out.println("add FAILED: " + uoe);
        }
        try {
            String next = it.next();
            System.out.println("--> " + next);
            it.set(next.toUpperCase());
            System.out.println("set to uppercase");
        } catch (UnsupportedOperationException uoe) {
            System.out.println("set FAILED: " + uoe);
        }
        try {
            String next = it.next();
            System.out.println("--> " + next);
            it.remove();
            System.out.println("removed");
        } catch (UnsupportedOperationException uoe) {
            System.out.println("remove FAILED: " + uoe);
        }
        System.out.println("Normal List: " + words);
        pause();

        // create and show a couple of immutable lists
        System.out.println("But sometimes we want lists that can't be changed."
                + "\nThere are several ways of doing that.");
        pause();
        
        // create some unmodificable lists using List.of
        nums = List.of(1, 2, 3, 4, 5);
        words = List.of("some", "words", "here");
        System.out.println("Immutable List created using List.of: " + nums);
        System.out.println("Immutable List created using List.of: " + words);
        pause();

        // show that no changes are allowed in List.of lists
        System.out.println("No changes allowed to immutable lists");
        it = words.listIterator();
        try {
            String next = it.next();
            System.out.println("--> " + next);
            it.add("NEW");
            System.out.println("added NEW");
        } catch (UnsupportedOperationException uoe) {
            System.out.println("add FAILED: " + uoe);
        }
        try {
            String next = it.next();
            System.out.println("--> " + next);
            it.set(next.toUpperCase());
            System.out.println("set to uppercase");
        } catch (UnsupportedOperationException uoe) {
            System.out.println("set FAILED: " + uoe);
        }
        try {
            String next = it.next();
            System.out.println("--> " + next);
            it.remove();
            System.out.println("removed");
        } catch (UnsupportedOperationException uoe) {
            System.out.println("remove FAILED: " + uoe);
        }
        System.out.println("Immutable List created using List.of: " + words);
        pause();

        // show no changes allowed in Collections.unmodifiableList lists
        System.out.println("No changes allowed in unmodifiable Lists, either.");
        words = new ArrayList<>();
        words.add("words");
        words.add("here");
        words.add("too");
        words = Collections.unmodifiableList(words);
        System.out.println("Unmodifiable List created using "
                + "Collections.unmodifiableList: " + words);
        it = words.listIterator();
        try {
            String next = it.next();
            System.out.println("--> " + next);
            it.add("NEW");
            System.out.println("added NEW");
        } catch (UnsupportedOperationException uoe) {
            System.out.println("add FAILED: " + uoe);
        }
        try {
            String next = it.next();
            System.out.println("--> " + next);
            it.set(next.toUpperCase());
            System.out.println("set to uppercase");
        } catch (UnsupportedOperationException uoe) {
            System.out.println("set FAILED: " + uoe);
        }
        try {
            String next = it.next();
            System.out.println("--> " + next);
            it.remove();
            System.out.println("removed");
        } catch (UnsupportedOperationException uoe) {
            System.out.println("remove FAILED: " + uoe);
        }
        System.out.println("Unmodifiable List: " + words);
        pause();
        
        // introduce Arrays.asList
        System.out.println("Arrays.asList just wraps around an array.\n"
                + "You can modify list elements, but not add/remove them.");
        pause();
        
        // show that Arrays.asList allows no adds/removes
        String[] array = {"more", "words", "here"};
        words = Arrays.asList(array);
        System.out.println("Unmodifiable List created using "
                + "Arrays.asList: " + words);
        System.out.println(" -> the array: " + Arrays.toString(array));
        System.out.println(" -> the List: " + words);
        it = words.listIterator();
        try {
            String next = it.next();
            System.out.println("--> " + next);
            it.add("NEW");
            System.out.println("added NEW");
        } catch (UnsupportedOperationException uoe) {
            System.out.println("add FAILED: " + uoe);
        }
        try {
            String next = it.next();
            System.out.println("--> " + next);
            it.set(next.toUpperCase());
            System.out.println("set to uppercase");
        } catch (UnsupportedOperationException uoe) {
            System.out.println("set FAILED: " + uoe);
        }
        try {
            String next = it.next();
            System.out.println("--> " + next);
            it.remove();
            System.out.println("removed");
        } catch (UnsupportedOperationException uoe) {
            System.out.println("remove FAILED: " + uoe);
        }
        System.out.println(" -> the array: " + Arrays.toString(array));
        System.out.println(" -> the List: " + words);
        pause();
        
        // show that the array and list share memory
        System.out.println("You can modify the Arrays.asList "
                + "by changing the array:");
        System.out.println("--> array[0] = \"different\"");
        array[0] = "different";
        System.out.println(" -> the array: " + Arrays.toString(array));
        System.out.println(" -> the List: " + words);
        pause();
    }

    private static final Scanner kbd = new Scanner(System.in);
    private static void pause() {
        System.out.println();
        System.out.print("...press enter...");
        kbd.nextLine();
        System.out.println();
    }

}
