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