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

pascal.taie.util.collection.Views Maven / Gradle / Ivy

The newest version!
/*
 * Tai-e: A Static Analysis Framework for Java
 *
 * Copyright (C) 2022 Tian Tan 
 * Copyright (C) 2022 Yue Li 
 *
 * This file is part of Tai-e.
 *
 * Tai-e is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation, either version 3
 * of the License, or (at your option) any later version.
 *
 * Tai-e is distributed in the hope that it will be useful,but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
 * Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with Tai-e. If not, see .
 */

package pascal.taie.util.collection;

import javax.annotation.Nonnull;
import java.util.AbstractCollection;
import java.util.AbstractSet;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Predicate;

/**
 * Provides unmodifiable view collections.
 * TODO: doc complexity of specific operations.
 */
public final class Views {

    private Views() {
    }

    /**
     * Given a mapper function, creates an immutable view collection
     * for given collection. The elements of the resulting collection
     * are mapped from given collection.
     *
     * @param c        the backing collection
     * @param mapper   the function maps elements in backing collection to
     *                 the ones in view collection
     * @param contains the predicate function that check if view collection
     *                 contains given object.
     * @param       type of elements in backing collection
     * @param       type of elements in view collection
     * @return an immutable view collection.
     */
    public static  Collection toMappedCollection(
            Collection c, Function mapper, Predicate contains) {
        return Collections.unmodifiableCollection(
                new MappedCollectionView<>(c, mapper, contains));
    }

    /**
     * Given a mapper function, creates an immutable view collection
     * for given collection. The elements of the resulting collection
     * are mapped from given collection.
     *
     * @param c      the backing collection
     * @param mapper the function maps elements in backing collection to
     *               the ones in view collection
     * @param     type of elements in backing collection
     * @param     type of elements in view collection
     * @return an immutable view collection.
     */
    public static  Collection toMappedCollection(
            Collection c, Function mapper) {
        return Collections.unmodifiableCollection(
                new MappedCollectionView<>(c, mapper));
    }

    private static class MappedCollectionView extends AbstractCollection {

        private final Collection backing;

        private final Function mapper;

        private final Predicate contains;

        private MappedCollectionView(Collection backing,
                                     Function mapper, Predicate contains) {
            this.backing = backing;
            this.mapper = mapper;
            this.contains = contains;
        }

        private MappedCollectionView(Collection backing, Function mapper) {
            this.backing = backing;
            this.mapper = mapper;
            this.contains = super::contains;
        }

        @Override
        public boolean contains(Object o) {
            return contains.test(o);
        }

        @Override
        @Nonnull
        public Iterator iterator() {
            return new Iterator<>() {

                private final Iterator it = backing.iterator();

                @Override
                public boolean hasNext() {
                    return it.hasNext();
                }

                @Override
                public R next() {
                    return mapper.apply(it.next());
                }
            };
        }

        @Override
        public int size() {
            return backing.size();
        }
    }

    /**
     * Given a mapper function, creates an immutable view set for
     * given collection.
     * 

* WARNING: the uniqueness of elements in the resulting set view is * guaranteed by given collection {@code c} and function {@code mapper}, * not by the resulting set view itself. * * @param c the backing collection * @param mapper the function maps elements in backing collection to * the ones in view collection * @param contains the predicate function that check if view collection * contains given object * @param type of elements in backing collection * @param type of elements in view collection * @return an immutable view set. */ public static Set toMappedSet( Collection c, Function mapper, Predicate contains) { return Collections.unmodifiableSet( new MappedSetView<>(c, mapper, contains)); } /** * Given a mapper function, creates an immutable view set for * given collection. *

* WARNING: the uniqueness of elements in the resulting set view is * guaranteed by given collection {@code c} and function {@code mapper}, * not by the resulting set view itself. * * @param c the backing collection * @param mapper the function maps elements in backing collection to * the ones in view collection * @param type of elements in backing collection * @param type of elements in view collection * @return an immutable view set. */ public static Set toMappedSet(Collection c, Function mapper) { return Collections.unmodifiableSet(new MappedSetView<>(c, mapper)); } private static class MappedSetView extends MappedCollectionView implements Set { private MappedSetView(Collection backing, Function mapper, Predicate contains) { super(backing, mapper, contains); } private MappedSetView(Collection backing, Function mapper) { super(backing, mapper); } @Override public boolean equals(Object o) { if (o == this) { return true; } if (!(o instanceof Set)) { return false; } Collection c = (Collection) o; if (c.size() != size()) { return false; } try { return containsAll(c); } catch (ClassCastException | NullPointerException unused) { return false; } } @Override public int hashCode() { int h = 0; for (R obj : this) { h += Objects.hashCode(obj); } return h; } } /** * Given a filter function, creates an immutable view collection for * given collection. The elements in the original collection that do not * satisfy the filter function will be absent in the resulting collection. * * @param backing the backing collection * @param filter the function that decides which elements stay in the * resulting collection * @param type of elements in the resulting collection * @return an immutable view collection. */ public static Collection toFilteredCollection( Collection backing, Predicate filter) { return Collections.unmodifiableCollection( new FilteredCollectionView<>(backing, filter)); } private static class FilteredCollectionView extends AbstractCollection { private final Collection backing; private final Predicate filter; private FilteredCollectionView(Collection backing, Predicate filter) { this.backing = backing; this.filter = filter; } @Override public boolean contains(Object o) { return backing.contains(o) && filter.test((T) o); } @Override @Nonnull public Iterator iterator() { return new FilteredIterator(); } private class FilteredIterator implements Iterator { private final Iterator it = backing.iterator(); private T next; private FilteredIterator() { advance(); } private void advance() { next = null; T e; while (it.hasNext()) { e = it.next(); if (filter.test(e)) { next = e; break; } } } @Override public boolean hasNext() { return next != null; } @Override public T next() { T e = next; if (e == null) { throw new NoSuchElementException(); } advance(); return e; } } @Override public int size() { int size = 0; for (T e : backing) { if (filter.test(e)) { ++size; } } return size; } } /** * Given two sets, creates an immutable view set consisting of elements * of the two sets. *

* WARNING: this implementation simply treats two sets as one set and * does not guarantee the uniqueness of elements. It is responsibility of * the client code to guarantee that the elements in {@code set1} and * {@code set2} are disjoint. * * @param set1 the first set to combine * @param set2 the second set to combine * @param type of elements * @return an immutable view set containing elements of {@code set1} * and {@code set2} */ public static Set toCombinedSet( Set set1, Set set2) { return Collections.unmodifiableSet(new CombinedSetView<>(set1, set2)); } private static class CombinedSetView extends AbstractSet { private final Set set1; private final Set set2; private CombinedSetView(Set set1, Set set2) { this.set1 = set1; this.set2 = set2; } @Override public boolean contains(Object o) { return set1.contains(o) || set2.contains(o); } @Override @Nonnull public Iterator iterator() { return new Iterator<>() { private final Iterator it1 = set1.iterator(); private final Iterator it2 = set2.iterator(); @Override public boolean hasNext() { return it1.hasNext() || it2.hasNext(); } @Override public T next() { if (it1.hasNext()) { return it1.next(); } else if (it2.hasNext()) { return it2.next(); } else { throw new NoSuchElementException(); } } }; } @Override public boolean isEmpty() { return set1.isEmpty() && set2.isEmpty(); } @Override public int size() { return set1.size() + set2.size(); } } }