package editor.util;

import java.util.Collection;
import java.util.HashMap;
import java.util.function.BiPredicate;

/**
 * This enumeration represents a way that elements from one #Collection can be contained in another.
 *
 * @author Alec Roelke
 */
public enum Containment implements BiPredicate<Collection<?>, Collection<?>>
{
    /**
     * The first collection contains all of the elements in the second collection.
     * Opposite of {@link #CONTAINS_NOT_ALL_OF}.
     */
    CONTAINS_ALL_OF,
    /**
     * The first collection contains any element in the second collection.  A collection always contains
     * an empty collection.
     * Opposite of {@link #CONTAINS_NONE_OF}.
     */
    CONTAINS_ANY_OF,
    /**
     * The first collection contains exactly the same elements as the second collection.
     * Opposite of {@link #CONTAINS_NOT_EXACTLY}.
     */
    CONTAINS_EXACTLY,
    /**
     * The first collection contains no elements from the second collection.
     * Opposite of {@link #CONTAINS_ANY_OF}.
     */
    CONTAINS_NONE_OF,
    /**
     * The first collection contains some, but not all, of the elements in the second collection.
     * Opposite if {@link #CONTAINS_ALL_OF}.
     */
    CONTAINS_NOT_ALL_OF,
    /**
     * The first collection contains some or none of the elements as the second collection, but is not
     * exactly the same.  Opposite of {@link #CONTAINS_EXACTLY}.
     */
    CONTAINS_NOT_EXACTLY;

    /**
     * @param contain String representation of the desired Containment
     * @return The Containment corresponding to the given String.
     */
    public static Containment parseContainment(String contain)
    {
        return Containment.valueOf(contain.toUpperCase().replace(' ', '_'));
    }

    @Override
    public boolean test(Collection<?> a, Collection<?> b)
    {
        switch (this)
        {
        case CONTAINS_ANY_OF:
            if (b.isEmpty())
                return true;
            for (Object o : b)
                if (a.contains(o))
                    return true;
            return false;
        case CONTAINS_NONE_OF:
            for (Object o : b)
                if (a.contains(o))
                    return false;
            return true;
        case CONTAINS_ALL_OF:
            return a.containsAll(b);
        case CONTAINS_NOT_ALL_OF:
            return CONTAINS_ANY_OF.test(a, b) && !a.containsAll(b);
        case CONTAINS_EXACTLY:
            var aMap = new HashMap<Object, Integer>();
            for (Object o : a)
                aMap.compute(o, (k, v) -> v == null ? 1 : v + 1);
            var bMap = new HashMap<Object, Integer>();
            for (Object o : b)
                bMap.compute(o, (k, v) -> v == null ? 1 : v + 1);
            return aMap.equals(bMap);
        case CONTAINS_NOT_EXACTLY:
            for (Object o : a)
                if (!b.contains(o))
                    return true;
            for (Object o : b)
                if (!a.contains(o))
                    return true;
            return false;
        default:
            throw new IllegalArgumentException("Illegal Containment " + this);
        }
    }

    /**
     * {@inheritDoc}
     * The String representation of a Containment is its name in lower case with
     * _ replaced with a space.
     */
    @Override
    public String toString()
    {
        return super.toString().replace("_", " ").toLowerCase();
    }
}

@@ -61,23 +61,36 @@
 va.util.
-HashMap
+function.BiPredicate
 ;%0Aimport
@@ -109,27 +109,60 @@
 unction.
-BiPredicate
+Function;%0Aimport java.util.stream.Collectors
 ;%0A%0A/**%0A 
@@ -1879,16 +1879,23 @@
         
+return 
 switch (
@@ -1903,19 +1903,15 @@
 his)
-%0A   
+ %7B%0A
     
- %7B%0A
     
@@ -1938,397 +1938,168 @@
 Y_OF
-:%0A            if (b.isEmpty())%0A                return true;%0A            for (Object o : b)%0A                if (a.contains(o))%0A                    return true;%0A            return false;%0A        case CONTAINS_NONE_OF:%0A            for (Object o : b)%0A                if (a.contains(o))%0A                    return false;%0A            return true;%0A        case CONTAINS_ALL_OF:%0A            return
+ -%3E b.isEmpty() %7C%7C b.stream().anyMatch(a::contains);%0A            case CONTAINS_NONE_OF -%3E b.stream().noneMatch(a::contains);%0A            case CONTAINS_ALL_OF -%3E
  a.c
@@ -2105,32 +2105,36 @@
 containsAll(b);%0A
+    
         case CON
@@ -2153,28 +2153,11 @@
 L_OF
-:%0A            return
+ -%3E
  CON
@@ -2202,32 +2202,36 @@
 All(b);%0A        
+    
 case CONTAINS_EX
@@ -2239,706 +2239,359 @@
 CTLY
-:%0A            var aMap = new HashMap%3CObject, Integer%3E();%0A            for (Object o : a)%0A                aMap.compute(o, (k, v) -%3E v == null ? 1 : v + 1);%0A            var bMap = new HashMap%3CObject, Integer%3E();%0A            for (Object o : b)%0A                bMap.compute(o, (k, v) -%3E v == null ? 1 : v + 1);%0A            return aMap.equals(bMap);%0A        case CONTAINS_NOT_EXACTLY:%0A            for (Object o : a)%0A                if (!b.contains(o))%0A                    return true;%0A            for (Object o : b)%0A                if (!a.contains(o))%0A                    return true;%0A            return false;%0A        default:%0A            throw new IllegalArgumentException(%22Illegal Containment %22 + this
+ -%3E a.stream().collect(Collectors.groupingBy(Function.identity(), Collectors.counting())).equals(%0A                                     b.stream().collect(Collectors.groupingBy(Function.identity(), Collectors.counting())));%0A            case CONTAINS_NOT_EXACTLY -%3E a.stream().anyMatch((o) -%3E !b.contains(o)) %7C%7C b.stream().anyMatch((o) -%3E !a.contains(o)
 );%0A 
@@ -2598,16 +2598,17 @@
        %7D
+;
 %0A    %7D%0A%0A

Use switch expression for containment tests