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 */
030public interface BloomFilterProducer {
031
032    /**
033     * Creates a BloomFilterProducer from an array of Bloom filters.
034     *
035     * <ul>
036     * <li>The asBloomFilterArray() method returns a copy of the original array
037     * with references to the original filters.</li>
038     * <li>The forEachBloomFilterPair() method uses references to the original filters.</li>
039     * </ul>
040     * <p><em>All modifications to the Bloom filters are reflected in the original filters</em></p>
041     *
042     * @param filters The filters to be returned by the producer.
043     * @return THe BloomFilterProducer containing the filters.
044     */
045    static BloomFilterProducer fromBloomFilterArray(BloomFilter... filters) {
046        Objects.requireNonNull(filters, "filters");
047        return new BloomFilterProducer() {
048            /**
049             * This implementation returns a copy the original array, the contained Bloom filters
050             * are references to the originals, any modifications to them are reflected in the original
051             * filters.
052             */
053            @Override
054            public BloomFilter[] asBloomFilterArray() {
055                return filters.clone();
056            }
057
058            @Override
059            public boolean forEachBloomFilter(final Predicate<BloomFilter> predicate) {
060                for (final BloomFilter filter : filters) {
061                    if (!predicate.test(filter)) {
062                        return false;
063                    }
064                }
065                return true;
066            }
067
068            /**
069             * This implementation uses references to the original filters.  Any modifications to the
070             * filters are reflected in the originals.
071             */
072            @Override
073            public boolean forEachBloomFilterPair(final BloomFilterProducer other,
074                    final BiPredicate<BloomFilter, BloomFilter> func) {
075                final CountingPredicate<BloomFilter> p = new CountingPredicate<>(filters, func);
076                return other.forEachBloomFilter(p) && p.forEachRemaining();
077            }
078        };
079    }
080
081    /**
082     * Return an array of the Bloom filters in the collection.
083     * <p><em>Implementations should specify if the array contains deep copies, immutable instances,
084     * or references to the filters in the collection.</em></p>
085     * <p>The default method returns a deep copy of the enclosed filters.</p>
086     *
087     * @return An array of Bloom filters.
088     */
089    default BloomFilter[] asBloomFilterArray() {
090        final List<BloomFilter> filters = new ArrayList<>();
091        forEachBloomFilter(f -> filters.add(f.copy()));
092        return filters.toArray(new BloomFilter[0]);
093    }
094
095    /**
096     * Create a standard (non-layered) Bloom filter by merging all of the layers. If
097     * the filter is empty this method will return an empty Bloom filter.
098     *
099     * @return the merged bloom filter.
100     */
101    default BloomFilter flatten() {
102        BloomFilter[] bf = {null};
103        forEachBloomFilter( x -> {
104            if (bf[0] == null) {
105                bf[0] = new SimpleBloomFilter( x.getShape());
106            }
107            return bf[0].merge( x );
108        });
109        return bf[0];
110    }
111
112    /**
113     * Executes a Bloom filter Predicate on each Bloom filter in the collection. The
114     * ordering of the Bloom filters is not specified by this interface.
115     *
116     * @param bloomFilterPredicate the predicate to evaluate each Bloom filter with.
117     * @return {@code false} when the first filter fails the predicate test. Returns
118     *         {@code true} if all filters pass the test.
119     */
120    boolean forEachBloomFilter(Predicate<BloomFilter> bloomFilterPredicate);
121
122    /**
123     * Applies the {@code func} to each Bloom filter pair in order. Will apply all
124     * of the Bloom filters from the other BloomFilterProducer to this producer. If
125     * either {@code this} producer or {@code other} producer has fewer BloomFilters
126     * ths method will provide {@code null} for all excess calls to the {@code func}.
127     *
128     * <p><em>This implementation returns references to the Bloom filter.  Other implementations
129     * should specify if the array contains deep copies, immutable instances,
130     * or references to the filters in the collection.</em></p>
131     *
132     * @param other The other BloomFilterProducer that provides the y values in the
133     *              (x,y) pair.
134     * @param func  The function to apply.
135     * @return {@code true} if the {@code func} returned {@code true} for every pair,
136     *         {@code false} otherwise.
137     */
138    default boolean forEachBloomFilterPair(final BloomFilterProducer other,
139            final BiPredicate<BloomFilter, BloomFilter> func) {
140        final CountingPredicate<BloomFilter> p = new CountingPredicate<>(asBloomFilterArray(), func);
141        return other.forEachBloomFilter(p) && p.forEachRemaining();
142    }
143}