| /* |
| * 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() { |
| } |
| } |