All Downloads are FREE. Search and download functionalities are using the official Maven repository.

eu.lucaventuri.collections.ClassifiedMap Maven / Gradle / Ivy

There is a newer version: 3.0.1
Show newest version
package eu.lucaventuri.collections;

import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;

import static eu.lucaventuri.collections.NodeLinkedList.Node;

/**
 * This object contains a map grouped by class and a list; so it can be accessed in both ways.
 * This object in intended to be used to filter messages based on their class and on some other attribute
 */
public class ClassifiedMap {
    private final NodeLinkedList list = new NodeLinkedList<>();
    private final ConcurrentHashMap>> mapByClass = new ConcurrentHashMap<>();

    public boolean addToTail(Object obj) {
        assert obj != null;
        verify();

        try {
            if (obj == null)
                return false;

            Node node = list.addToTail(obj);

            mapByClass.computeIfAbsent(obj.getClass(), k -> new NodeLinkedList<>()).addToTail(node);

            return true;
        } finally {
            verify();
        }
    }

    public boolean addToTailConverted(Object obj, Class convertedClass) {
        assert obj != null;
        verify();

        try {
            if (obj == null)
                return false;

            Node node = list.addToTail(obj);

            mapByClass.computeIfAbsent(convertedClass, k -> new NodeLinkedList<>()).addToTail(node);

            return true;
        } finally {
            verify();
        }
    }

    public  T peekHead() {
        verify();

        return (T) list.peekHead();
    }

    public  T removeHead() {
        verify();

        try {
            Node n = list.removeHeadNode();

            if (n == null)
                return null;

            NodeLinkedList> classList = mapByClass.get(n.value.getClass());

            if (classList != null) {
                classList.removeFirstByValue(n);
                if (classList.isEmpty())
                    mapByClass.remove(n.value.getClass());
            }

            return (T) n.value;
        } finally {
            verify();
        }
    }

    public  T removeHeadConverted(Function converter) {
        verify();

        try {
            Node n = list.removeHeadNode();

            if (n == null)
                return null;

            var convertedClass = converter.apply((T) n.value).getClass();
            NodeLinkedList> classList = mapByClass.get(convertedClass);

            if (classList != null) {
                classList.removeFirstByValue(n);
                if (classList.isEmpty())
                    mapByClass.remove(convertedClass);
            }

            return (T) n.value;
        } finally {
            verify();
        }
    }

    private void showDebug(String descr, Object value) {
        System.out.println(descr + " - Sizes: " + mapByClass.size() + " vs " + list.asListFromHead().size() + " - Class: " + value.getClass());

        for (var clz : mapByClass.keySet())
            System.out.println("  " + clz);
    }

    private void verify() {
        assert (mapByClass.isEmpty() && list.isEmpty()) || (!mapByClass.isEmpty() && !list.isEmpty());
    }

    void deepVerify() {
        assert (mapByClass.size() == list.asListFromHead().size());
    }

    public  T scanAndChoose(Class cls, Predicate filter) {
        verify();

        try {
            T value = scanList(filter, mapByClass.get(cls));

            if (value != null)
                return value;

            for (Class clz : mapByClass.keySet()) {
                if (cls != clz && cls.isAssignableFrom(clz)) {
                    value = scanList(filter, mapByClass.get(clz));

                    if (value != null)
                        return value;
                }
            }

            return null;
        } finally {
            verify();
        }
    }

    private  T scanList(Predicate filter, NodeLinkedList> listByClass) {
        try {
            if (listByClass == null)
                return null;

            /// use AsNodeIterator
            for (Node> n : listByClass.asNodesIterable()) {
                if (filter.test((T) n.value.value)) {
                    listByClass.remove(n);
                    //list.remove(n);
                    // TODO: Can it become faster?
                    list.removeFirstByValue(n.value);
                    return (T) n.value.value;
                }
            }

            return null;
        } finally {
            verify();
        }
    }

    public boolean isEmpty() {
        //verify();

        return list.isEmpty();
    }

    public  K scanAndChooseAndConvert(Class cls, Predicate filter, Function converter) {
        verify();

        try {
            K value = scanAndCovertList(cls, filter, converter, mapByClass.get(cls));

            if (value != null)
                return value;

            for (Class clz : mapByClass.keySet()) {
                if (cls.isAssignableFrom(clz)) {
                    value = scanAndCovertList(clz, filter, converter, mapByClass.get(clz));

                    if (value != null)
                        return value;
                }
            }

            return null;
        } finally {
            verify();
        }
    }

    private  K scanAndCovertList(Class cls, Predicate filter, Function converter, NodeLinkedList> listByClass) {
        if (listByClass == null)
            return null;

        try {
            for (Node> n : listByClass.asNodesIterable()) {
                K valueConverted = converter.apply((T) n.value.value);

                if (valueConverted != null && filter.test((E) valueConverted)) {
                    listByClass.remove(n);
                    //list.remove(n);
                    list.removeFirstByValue(n.value);
                    return valueConverted;
                }
            }

            return null;
        } finally {
            verify();
        }
    }
}