/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.brooklyn.util.collections; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Set; import javax.annotation.Nullable; import org.apache.brooklyn.util.collections.QuorumCheck.QuorumChecks; import com.google.common.base.Function; import com.google.common.base.Functions; import com.google.common.base.Preconditions; import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.common.base.Supplier; import com.google.common.collect.Iterables; import com.google.common.collect.Sets; /** things which it seems should be in guava, but i can't find * @author alex */ public class CollectionFunctionals { private static final class EqualsSetPredicate implements Predicate<Iterable<?>> { private final Iterable<?> target; private EqualsSetPredicate(Iterable<?> target) { this.target = target; } @Override public boolean apply(@Nullable Iterable<?> input) { if (input==null) return false; return Sets.newHashSet(target).equals(Sets.newHashSet(input)); } } private static final class KeysOfMapFunction<K> implements Function<Map<K, ?>, Set<K>> { @Override public Set<K> apply(Map<K, ?> input) { if (input==null) return null; return input.keySet(); } @Override public String toString() { return "keys"; } } private static final class SizeSupplier implements Supplier<Integer> { private final Iterable<?> collection; private SizeSupplier(Iterable<?> collection) { this.collection = collection; } @Override public Integer get() { return Iterables.size(collection); } @Override public String toString() { return "sizeSupplier("+collection+")"; } } public static final class SizeFunction implements Function<Iterable<?>, Integer> { private final Integer valueIfInputNull; private SizeFunction(Integer valueIfInputNull) { this.valueIfInputNull = valueIfInputNull; } @Override public Integer apply(Iterable<?> input) { if (input==null) return valueIfInputNull; return Iterables.size(input); } @Override public String toString() { return "sizeFunction"; } } public static Supplier<Integer> sizeSupplier(final Iterable<?> collection) { return new SizeSupplier(collection); } public static Function<Iterable<?>, Integer> sizeFunction() { return sizeFunction(null); } public static Function<Iterable<?>, Integer> sizeFunction(final Integer valueIfInputNull) { return new SizeFunction(valueIfInputNull); } public static final class FirstElementFunction<T> implements Function<Iterable<? extends T>, T> { public FirstElementFunction() { } @Override public T apply(Iterable<? extends T> input) { if (input==null || Iterables.isEmpty(input)) return null; return Iterables.get(input, 0); } @Override public String toString() { return "firstElementFunction"; } } public static <T> Function<Iterable<? extends T>, T> firstElement() { return new FirstElementFunction<T>(); } public static <K> Function<Map<K,?>,Set<K>> keys() { return new KeysOfMapFunction<K>(); } public static <K> Function<Map<K, ?>, Integer> mapSize() { return mapSize(null); } public static <K> Function<Map<K, ?>, Integer> mapSize(Integer valueIfNull) { return Functions.compose(CollectionFunctionals.sizeFunction(valueIfNull), CollectionFunctionals.<K>keys()); } /** default guava Equals predicate will reflect order of target, and will fail when matching against a list; * this treats them both as sets */ public static Predicate<Iterable<?>> equalsSetOf(Object... target) { return equalsSet(Arrays.asList(target)); } public static Predicate<Iterable<?>> equalsSet(final Iterable<?> target) { return new EqualsSetPredicate(target); } public static Predicate<Iterable<?>> sizeEquals(int targetSize) { return Predicates.compose(Predicates.equalTo(targetSize), CollectionFunctionals.sizeFunction()); } public static Predicate<Iterable<?>> empty() { return sizeEquals(0); } public static Predicate<Iterable<?>> notEmpty() { return Predicates.not(empty()); } public static <K> Predicate<Map<K,?>> mapSizeEquals(int targetSize) { return Predicates.compose(Predicates.equalTo(targetSize), CollectionFunctionals.<K>mapSize()); } public static <T,I extends Iterable<T>> Function<I, List<T>> limit(final int max) { return new LimitFunction<T,I>(max); } private static final class LimitFunction<T, I extends Iterable<T>> implements Function<I, List<T>> { private final int max; private LimitFunction(int max) { this.max = max; } @Override public List<T> apply(I input) { if (input==null) return null; MutableList<T> result = MutableList.of(); for (T i: input) { result.add(i); if (result.size()>=max) return result; } return result; } } // --------- public static <I,T extends Collection<I>> Predicate<T> contains(I item) { return new CollectionContains<I,T>(item); } private static final class CollectionContains<I,T extends Collection<I>> implements Predicate<T> { private final I item; private CollectionContains(I item) { this.item = item; } @Override public boolean apply(T input) { if (input==null) return false; return input.contains(item); } @Override public String toString() { return "contains("+item+")"; } } // --------- /** * Returns a predicate for a collection which is true if * all elements in the collection given to the predicate * which satisfies the predicate given here. * <p> * This will return true for the empty set. * To require additionally that there is at least one * use {@link #quorum(QuorumCheck, Predicate)} with * {@link QuorumChecks#allAndAtLeastOne()}. */ public static <T,TT extends Iterable<T>> Predicate<TT> all(Predicate<T> attributeSatisfies) { return quorum(QuorumChecks.all(), attributeSatisfies); } /** Returns a predicate for a collection which is true if * there is at least one element in the collection given to the predicate * which satisfies the predicate given here. */ public static <T,TT extends Iterable<T>> Predicate<TT> any(Predicate<T> attributeSatisfies) { // implementation could be more efficient -- ie succeed fast return quorum(QuorumChecks.atLeastOne(), attributeSatisfies); } /** Returns a predicate for a collection which is true if * the number of elements in the collection satisfying the predicate given here * passes the {@link QuorumCheck} given here. */ public static <T,TT extends Iterable<T>> Predicate<TT> quorum(QuorumCheck quorumCheck, Predicate<T> attributeSatisfies) { return new QuorumSatisfies<T, TT>(quorumCheck, attributeSatisfies); } private static final class QuorumSatisfies<I,T extends Iterable<I>> implements Predicate<T> { private final Predicate<I> itemCheck; private final QuorumCheck quorumCheck; private QuorumSatisfies(QuorumCheck quorumCheck, Predicate<I> itemCheck) { this.itemCheck = Preconditions.checkNotNull(itemCheck, "itemCheck"); this.quorumCheck = Preconditions.checkNotNull(quorumCheck, "quorumCheck"); } @Override public boolean apply(T input) { if (input==null) return false; int sizeHealthy = 0, totalSize = 0; for (I item: input) { totalSize++; if (itemCheck.apply(item)) sizeHealthy++; } return quorumCheck.isQuorate(sizeHealthy, totalSize); } @Override public String toString() { return quorumCheck.toString()+"("+itemCheck+")"; } } }