001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.collections4.bloomfilter;
018
019import java.util.ArrayList;
020import java.util.List;
021import java.util.Objects;
022import java.util.function.BiPredicate;
023import java.util.function.Predicate;
024
025/**
026 * Produces Bloom filters from a collection (e.g. LayeredBloomFilter).
027 *
028 * @since 4.5
029 */
030@FunctionalInterface
031public interface BloomFilterExtractor {
032
033    /**
034     * Creates a BloomFilterExtractor from an array of Bloom filters.
035     *
036     * <ul>
037     * <li>The asBloomFilterArray() method returns a copy of the original array
038     * with references to the original filters.</li>
039     * <li>The forEachBloomFilterPair() method uses references to the original filters.</li>
040     * </ul>
041     * <p><em>All modifications to the Bloom filters are reflected in the original filters</em></p>
042     *
043     * @param filters The filters to be returned by the extractor.
044     * @return THe BloomFilterExtractor containing the filters.
045     */
046    static BloomFilterExtractor fromBloomFilterArray(final BloomFilter... filters) {
047        Objects.requireNonNull(filters, "filters");
048        return new BloomFilterExtractor() {
049            /**
050             * This implementation returns a copy the original array, the contained Bloom filters
051             * are references to the originals, any modifications to them are reflected in the original
052             * filters.
053             */
054            @Override
055            public BloomFilter[] asBloomFilterArray() {
056                return filters.clone();
057            }
058
059            @Override
060            public boolean processBloomFilters(final Predicate<BloomFilter> predicate) {
061                for (final BloomFilter filter : filters) {
062                    if (!predicate.test(filter)) {
063                        return false;
064                    }
065                }
066                return true;
067            }
068
069            /**
070             * This implementation uses references to the original filters.  Any modifications to the
071             * filters are reflected in the originals.
072             */
073            @Override
074            public boolean processBloomFilterPair(final BloomFilterExtractor other,
075                                                  final BiPredicate<BloomFilter, BloomFilter> func) {
076                final CountingPredicate<BloomFilter> p = new CountingPredicate<>(filters, func);
077                return other.processBloomFilters(p) && p.processRemaining();
078            }
079        };
080    }
081
082    /**
083     * Return an array of the Bloom filters in the collection.
084     * <p><em>Implementations should specify if the array contains deep copies, immutable instances,
085     * or references to the filters in the collection.</em></p>
086     * <p>The default method returns a deep copy of the enclosed filters.</p>
087     *
088     * @return An array of Bloom filters.
089     */
090    default BloomFilter[] asBloomFilterArray() {
091        final List<BloomFilter> filters = new ArrayList<>();
092        processBloomFilters(f -> filters.add(f.copy()));
093        return filters.toArray(new BloomFilter[0]);
094    }
095
096    /**
097     * Create a standard (non-layered) Bloom filter by merging all of the layers. If
098     * the filter is empty this method will return an empty Bloom filter.
099     *
100     * @return the merged bloom filter.
101     */
102    default BloomFilter flatten() {
103        final BloomFilter[] bf = {null};
104        processBloomFilters(x -> {
105            if (bf[0] == null) {
106                bf[0] = new SimpleBloomFilter( x.getShape());
107            }
108            return bf[0].merge( x );
109        });
110        return bf[0];
111    }
112
113    /**
114     * Executes a Bloom filter Predicate on each Bloom filter in the collection. The
115     * ordering of the Bloom filters is not specified by this interface.
116     *
117     * @param bloomFilterPredicate the predicate to evaluate each Bloom filter with.
118     * @return {@code false} when the first filter fails the predicate test. Returns
119     *         {@code true} if all filters pass the test.
120     */
121    boolean processBloomFilters(Predicate<BloomFilter> bloomFilterPredicate);
122
123    /**
124     * Applies the {@code func} to each Bloom filter pair in order. Will apply all
125     * of the Bloom filters from the other BloomFilterExtractor to this extractor. If
126     * either {@code this} extractor or {@code other} extractor has fewer BloomFilters
127     * ths method will provide {@code null} for all excess calls to the {@code func}.
128     *
129     * <p><em>This implementation returns references to the Bloom filter.  Other implementations
130     * should specify if the array contains deep copies, immutable instances,
131     * or references to the filters in the collection.</em></p>
132     *
133     * @param other The other BloomFilterExtractor that provides the y values in the
134     *              (x,y) pair.
135     * @param func  The function to apply.
136     * @return {@code true} if the {@code func} returned {@code true} for every pair,
137     *         {@code false} otherwise.
138     */
139    default boolean processBloomFilterPair(final BloomFilterExtractor other,
140                                           final BiPredicate<BloomFilter, BloomFilter> func) {
141        final CountingPredicate<BloomFilter> p = new CountingPredicate<>(asBloomFilterArray(), func);
142        return other.processBloomFilters(p) && p.processRemaining();
143    }
144}