blob: f24b6531fc17405caacfbac960c34aedebed48cb [file] [log] [blame]
/*
* Copyright 2010 Google Inc.
*
* 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 com.google.gwt.validation.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 {
/**
* 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(T input) {
Class<?> inputClass = toClass.apply(input);
for (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(Class<?> target,
Set<Class<?>> availableClasses) {
Set<Class<?>> matches = new HashSet<Class<?>>();
if (availableClasses.contains(target)) {
return ImmutableSet.<Class<?>> of(target);
} else {
for (Class<?> clazz : availableClasses) {
if (clazz.isAssignableFrom(target)) {
matches.add(clazz);
}
}
}
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.
*/
static <T> ImmutableList<T> sortMostSpecificFirst(Iterable<T> classes,
Function<T, Class<?>> toClass) {
List<T> working = Lists.newArrayList();
// strip duplicates
for (T t : classes) {
if (!working.contains(t)) {
working.add(t);
}
}
List<T> sorted = Lists.newArrayList();
Predicate<T> mostSpecific = createMostSpecificMatchPredicate(working,
toClass);
boolean changed = false;
do {
changed = false;
for (Iterator<T> iterator = working.iterator(); iterator.hasNext();) {
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);
}
private Util() {
}
}