View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.collections4.bloomfilter;
18  
19  import java.util.ArrayList;
20  import java.util.Arrays;
21  import java.util.List;
22  import java.util.Objects;
23  import java.util.concurrent.atomic.AtomicReference;
24  import java.util.function.BiPredicate;
25  import java.util.function.Predicate;
26  
27  /**
28   * Produces Bloom filters from a collection (for example, {@link LayeredBloomFilter}).
29   *
30   * @since 4.5.0-M2
31   */
32  @FunctionalInterface
33  public interface BloomFilterExtractor {
34  
35      /**
36       * Creates a BloomFilterExtractor from an array of Bloom filters.
37       *
38       * <ul>
39       * <li>The asBloomFilterArray() method returns a copy of the original array with references to the original filters.</li>
40       * <li>The forEachBloomFilterPair() method uses references to the original filters.</li>
41       * </ul>
42       * <p>
43       * <em>All modifications to the Bloom filters are reflected in the original filters</em>
44       * </p>
45       *
46       * @param <T>     The BloomFilter type.
47       * @param filters The filters to be returned by the extractor.
48       * @return THe BloomFilterExtractor containing the filters.
49       */
50      static <T extends BloomFilter<T>> BloomFilterExtractor fromBloomFilterArray(final BloomFilter<?>... filters) {
51          Objects.requireNonNull(filters, "filters");
52          return new BloomFilterExtractor() {
53  
54              /**
55               * This implementation returns a copy the original array, the contained Bloom filters are references to the originals, any modifications to them are
56               * reflected in the original filters.
57               */
58              @Override
59              public BloomFilter[] asBloomFilterArray() {
60                  return filters.clone();
61              }
62  
63              /**
64               * This implementation uses references to the original filters. Any modifications to the filters are reflected in the originals.
65               */
66              @Override
67              public boolean processBloomFilterPair(final BloomFilterExtractor other, final BiPredicate<BloomFilter, BloomFilter> func) {
68                  final CountingPredicate<BloomFilter> p = new CountingPredicate<>(filters, func);
69                  return other.processBloomFilters(p) && p.processRemaining();
70              }
71  
72              @Override
73              public boolean processBloomFilters(final Predicate<BloomFilter> predicate) {
74                  return Arrays.stream(filters).allMatch(predicate);
75              }
76          };
77      }
78  
79      /**
80       * Return an array of the Bloom filters in the collection.
81       * <p>
82       * <em>Implementations should specify if the array contains deep copies, immutable instances, or references to the filters in the collection.</em>
83       * </p>
84       * <p>
85       * The default method returns a deep copy of the enclosed filters.
86       * </p>
87       *
88       * @return An array of Bloom filters.
89       */
90      default BloomFilter[] asBloomFilterArray() {
91          final List<BloomFilter> filters = new ArrayList<>();
92          processBloomFilters(f -> filters.add(f.copy()));
93          return filters.toArray(new BloomFilter[0]);
94      }
95  
96      /**
97       * Create a standard (non-layered) Bloom filter by merging all of the layers. If the filter is empty this method will return an empty Bloom filter.
98       *
99       * @return the merged bloom filter, never null.
100      * @throws NullPointerException if this call did not process any filters.
101      */
102     default BloomFilter flatten() {
103         final AtomicReference<BloomFilter> ref = new AtomicReference<>();
104         processBloomFilters(x -> {
105             if (ref.get() == null) {
106                 ref.set(new SimpleBloomFilter(x.getShape()));
107             }
108             return ref.get().merge(x);
109         });
110         return Objects.requireNonNull(ref.get(), "No filters.");
111     }
112 
113     /**
114      * Applies the {@code func} to each Bloom filter pair in order. Will apply all of the Bloom filters from the other BloomFilterExtractor to this extractor.
115      * If either {@code this} extractor or {@code other} extractor has fewer BloomFilters the method will provide {@code null} for all excess calls to the
116      * {@code func}.
117      *
118      * <p>
119      * <em>This implementation returns references to the Bloom filter. Other implementations should specify if the array contains deep copies, immutable
120      * instances, or references to the filters in the collection.</em>
121      * </p>
122      *
123      * @param other The other BloomFilterExtractor that provides the y values in the (x,y) pair.
124      * @param func  The function to apply.
125      * @return {@code true} if the {@code func} returned {@code true} for every pair, {@code false} otherwise.
126      */
127     default boolean processBloomFilterPair(final BloomFilterExtractor other, final BiPredicate<BloomFilter, BloomFilter> func) {
128         final CountingPredicate<BloomFilter> p = new CountingPredicate<>(asBloomFilterArray(), func);
129         return other.processBloomFilters(p) && p.processRemaining();
130     }
131 
132     /**
133      * Executes a Bloom filter Predicate on each Bloom filter in the collection. The ordering of the Bloom filters is not specified by this interface.
134      *
135      * @param bloomFilterPredicate the predicate to evaluate each Bloom filter with.
136      * @return {@code false} when the first filter fails the predicate test. Returns {@code true} if all filters pass the test.
137      */
138     boolean processBloomFilters(Predicate<BloomFilter> bloomFilterPredicate);
139 }