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.Arrays; 021import java.util.List; 022import java.util.Objects; 023import java.util.concurrent.atomic.AtomicReference; 024import java.util.function.BiPredicate; 025import java.util.function.Predicate; 026 027/** 028 * Produces Bloom filters from a collection (for example, {@link LayeredBloomFilter}). 029 * 030 * @since 4.5.0-M2 031 */ 032@FunctionalInterface 033public interface BloomFilterExtractor { 034 035 /** 036 * Creates a BloomFilterExtractor from an array of Bloom filters. 037 * 038 * <ul> 039 * <li>The asBloomFilterArray() method returns a copy of the original array with references to the original filters.</li> 040 * <li>The forEachBloomFilterPair() method uses references to the original filters.</li> 041 * </ul> 042 * <p> 043 * <em>All modifications to the Bloom filters are reflected in the original filters</em> 044 * </p> 045 * 046 * @param <T> The BloomFilter type. 047 * @param filters The filters to be returned by the extractor. 048 * @return THe BloomFilterExtractor containing the filters. 049 */ 050 static <T extends BloomFilter<T>> BloomFilterExtractor fromBloomFilterArray(final BloomFilter<?>... filters) { 051 Objects.requireNonNull(filters, "filters"); 052 return new BloomFilterExtractor() { 053 054 /** 055 * This implementation returns a copy the original array, the contained Bloom filters are references to the originals, any modifications to them are 056 * reflected in the original filters. 057 */ 058 @Override 059 public BloomFilter[] asBloomFilterArray() { 060 return filters.clone(); 061 } 062 063 /** 064 * This implementation uses references to the original filters. Any modifications to the filters are reflected in the originals. 065 */ 066 @Override 067 public boolean processBloomFilterPair(final BloomFilterExtractor other, final BiPredicate<BloomFilter, BloomFilter> func) { 068 final CountingPredicate<BloomFilter> p = new CountingPredicate<>(filters, func); 069 return other.processBloomFilters(p) && p.processRemaining(); 070 } 071 072 @Override 073 public boolean processBloomFilters(final Predicate<BloomFilter> predicate) { 074 return Arrays.stream(filters).allMatch(predicate); 075 } 076 }; 077 } 078 079 /** 080 * Return an array of the Bloom filters in the collection. 081 * <p> 082 * <em>Implementations should specify if the array contains deep copies, immutable instances, or references to the filters in the collection.</em> 083 * </p> 084 * <p> 085 * The default method returns a deep copy of the enclosed filters. 086 * </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 the filter is empty this method will return an empty Bloom filter. 098 * 099 * @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}