/*
* Copyright 2010 Google Inc. Copyright 2016 Manfred Tremmel
*
* Licensed 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 de.knightsoftnet.validators.rebind;
import com.google.gwt.thirdparty.guava.common.base.Function;
import com.google.gwt.thirdparty.guava.common.base.Functions;
import com.google.gwt.thirdparty.guava.common.base.Predicate;
import com.google.gwt.thirdparty.guava.common.collect.ImmutableList;
import com.google.gwt.thirdparty.guava.common.collect.ImmutableSet;
import com.google.gwt.thirdparty.guava.common.collect.Iterables;
import com.google.gwt.thirdparty.guava.common.collect.Lists;
import com.google.gwt.thirdparty.guava.common.collect.Sets;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
/**
* Static utilities for the validation rebind package.
*/
final class Util {
private Util() {
super();
}
/**
* Creates a Predicate that returns false if source contains an associated class that is a super
* type of the class associated with the tested T.
*
* @param <T> the type to test
* @param source the set of <T> to look for class matches.
* @param toClass Function from T to Class
* @return newly create predicate.
*/
static <T> Predicate<T> createMostSpecificMatchPredicate(final Iterable<T> source,
final Function<T, Class<?>> toClass) {
return new Predicate<T>() {
@Override
public boolean apply(final T input) {
final Class<?> inputClass = toClass.apply(input);
for (final Class<?> match : Iterables.transform(source, toClass)) {
if (!inputClass.equals(match) && inputClass.isAssignableFrom(match)) {
return false;
}
}
return true;
}
};
}
/**
* Selects first only the classes that are assignable from the target, and then returns the most
* specific matching classes.
*
* @param target the Class to match
* @param availableClasses classes to search
* @return Set of only the most specific classes that match the target.
*/
static Set<Class<?>> findBestMatches(final Class<?> target,
final Set<Class<?>> availableClasses) {
final Set<Class<?>> matches = new HashSet<>();
if (availableClasses.contains(target)) {
return ImmutableSet.<Class<?>>of(target);
} else {
for (final Class<?> clazz : availableClasses) {
if (clazz.isAssignableFrom(target)) {
matches.add(clazz);
}
}
}
final Predicate<Class<?>> moreSpecificClassPredicate =
createMostSpecificMatchPredicate(matches, Functions.<Class<?>>identity());
return Sets.filter(matches, moreSpecificClassPredicate);
}
/**
* Returns a Immutable List sorted with the most specific associated class first. Each element is
* guaranteed to not be assignable to any element that appears before it in the list.
*/
@SuppressWarnings("checkstyle:rightCurly")
static <T> ImmutableList<T> sortMostSpecificFirst(final Iterable<T> classes,
final Function<T, Class<?>> toClass) {
final List<T> working = Lists.newArrayList();
// strip duplicates
for (final T t : classes) {
if (!working.contains(t)) {
working.add(t);
}
}
final List<T> sorted = Lists.newArrayList();
final Predicate<T> mostSpecific = createMostSpecificMatchPredicate(working, toClass);
boolean changed = false;
do {
changed = false;
for (final Iterator<T> iterator = working.iterator(); iterator.hasNext();) {
final T t = iterator.next();
if (mostSpecific.apply(t)) {
sorted.add(t);
iterator.remove();
changed = true;
}
}
} while (changed);
if (!working.isEmpty()) {
throw new IllegalStateException(
"Unable to find a element that does not have a more specific element in the set "
+ working);
}
return ImmutableList.copyOf(sorted);
}
}