|
Java example source code file (FeatureUtil.java)
This example Java source code file (FeatureUtil.java) is included in the alvinalexander.com
"Java Source Code
Warehouse" project. The intent of this project is to help you "Learn
Java by Example" TM.
Learn more about this Java project at its project page.
The FeatureUtil.java Java example source code
/*
* Copyright (C) 2008 The Guava Authors
*
* 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.common.collect.testing.features;
import com.google.common.annotations.GwtIncompatible;
import com.google.common.collect.testing.Helpers;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
/**
* Utilities for collecting and validating tester requirements from annotations.
*
* @author George van den Driessche
*/
@GwtIncompatible
public class FeatureUtil {
/**
* A cache of annotated objects (typically a Class or Method) to its
* set of annotations.
*/
private static Map<AnnotatedElement, List annotationCache =
new HashMap<AnnotatedElement, List();
private static final Map<Class>, TesterRequirements> classTesterRequirementsCache =
new HashMap<Class>, TesterRequirements>();
private static final Map<Method, TesterRequirements> methodTesterRequirementsCache =
new HashMap<Method, TesterRequirements>();
/**
* Given a set of features, add to it all the features directly or indirectly
* implied by any of them, and return it.
* @param features the set of features to expand
* @return the same set of features, expanded with all implied features
*/
public static Set<Feature>> addImpliedFeatures(Set> features) {
Queue<Feature>> queue = new ArrayDeque>(features);
while (!queue.isEmpty()) {
Feature<?> feature = queue.remove();
for (Feature<?> implied : feature.getImpliedFeatures()) {
if (features.add(implied)) {
queue.add(implied);
}
}
}
return features;
}
/**
* Given a set of features, return a new set of all features directly or
* indirectly implied by any of them.
* @param features the set of features whose implications to find
* @return the implied set of features
*/
public static Set<Feature>> impliedFeatures(Set> features) {
Set<Feature>> impliedSet = new LinkedHashSet>();
Queue<Feature>> queue = new ArrayDeque>(features);
while (!queue.isEmpty()) {
Feature<?> feature = queue.remove();
for (Feature<?> implied : feature.getImpliedFeatures()) {
if (!features.contains(implied) && impliedSet.add(implied)) {
queue.add(implied);
}
}
}
return impliedSet;
}
/**
* Get the full set of requirements for a tester class.
* @param testerClass a tester class
* @return all the constraints implicitly or explicitly required by the class
* or any of its superclasses.
* @throws ConflictingRequirementsException if the requirements are mutually
* inconsistent.
*/
public static TesterRequirements getTesterRequirements(Class<?> testerClass)
throws ConflictingRequirementsException {
synchronized (classTesterRequirementsCache) {
TesterRequirements requirements = classTesterRequirementsCache.get(testerClass);
if (requirements == null) {
requirements = buildTesterRequirements(testerClass);
classTesterRequirementsCache.put(testerClass, requirements);
}
return requirements;
}
}
/**
* Get the full set of requirements for a tester class.
* @param testerMethod a test method of a tester class
* @return all the constraints implicitly or explicitly required by the
* method, its declaring class, or any of its superclasses.
* @throws ConflictingRequirementsException if the requirements are
* mutually inconsistent.
*/
public static TesterRequirements getTesterRequirements(Method testerMethod)
throws ConflictingRequirementsException {
synchronized (methodTesterRequirementsCache) {
TesterRequirements requirements = methodTesterRequirementsCache.get(testerMethod);
if (requirements == null) {
requirements = buildTesterRequirements(testerMethod);
methodTesterRequirementsCache.put(testerMethod, requirements);
}
return requirements;
}
}
/**
* Construct the full set of requirements for a tester class.
* @param testerClass a tester class
* @return all the constraints implicitly or explicitly required by the class
* or any of its superclasses.
* @throws ConflictingRequirementsException if the requirements are mutually
* inconsistent.
*/
static TesterRequirements buildTesterRequirements(Class<?> testerClass)
throws ConflictingRequirementsException {
final TesterRequirements declaredRequirements = buildDeclaredTesterRequirements(testerClass);
Class<?> baseClass = testerClass.getSuperclass();
if (baseClass == null) {
return declaredRequirements;
} else {
final TesterRequirements clonedBaseRequirements =
new TesterRequirements(getTesterRequirements(baseClass));
return incorporateRequirements(clonedBaseRequirements, declaredRequirements, testerClass);
}
}
/**
* Construct the full set of requirements for a tester method.
* @param testerMethod a test method of a tester class
* @return all the constraints implicitly or explicitly required by the
* method, its declaring class, or any of its superclasses.
* @throws ConflictingRequirementsException if the requirements are mutually
* inconsistent.
*/
static TesterRequirements buildTesterRequirements(Method testerMethod)
throws ConflictingRequirementsException {
TesterRequirements clonedClassRequirements =
new TesterRequirements(getTesterRequirements(testerMethod.getDeclaringClass()));
TesterRequirements declaredRequirements = buildDeclaredTesterRequirements(testerMethod);
return incorporateRequirements(clonedClassRequirements, declaredRequirements, testerMethod);
}
/**
* Construct the set of requirements specified by annotations
* directly on a tester class or method.
* @param classOrMethod a tester class or a test method thereof
* @return all the constraints implicitly or explicitly required by
* annotations on the class or method.
* @throws ConflictingRequirementsException if the requirements are mutually
* inconsistent.
*/
public static TesterRequirements buildDeclaredTesterRequirements(AnnotatedElement classOrMethod)
throws ConflictingRequirementsException {
TesterRequirements requirements = new TesterRequirements();
Iterable<Annotation> testerAnnotations = getTesterAnnotations(classOrMethod);
for (Annotation testerAnnotation : testerAnnotations) {
TesterRequirements moreRequirements = buildTesterRequirements(testerAnnotation);
incorporateRequirements(requirements, moreRequirements, testerAnnotation);
}
return requirements;
}
/**
* Find all the tester annotations declared on a tester class or method.
* @param classOrMethod a class or method whose tester annotations to find
* @return an iterable sequence of tester annotations on the class
*/
public static Iterable<Annotation> getTesterAnnotations(AnnotatedElement classOrMethod) {
synchronized (annotationCache) {
List<Annotation> annotations = annotationCache.get(classOrMethod);
if (annotations == null) {
annotations = new ArrayList<Annotation>();
for (Annotation a : classOrMethod.getDeclaredAnnotations()) {
if (a.annotationType().isAnnotationPresent(TesterAnnotation.class)) {
annotations.add(a);
}
}
annotations = Collections.unmodifiableList(annotations);
annotationCache.put(classOrMethod, annotations);
}
return annotations;
}
}
/**
* Find all the constraints explicitly or implicitly specified by a single
* tester annotation.
* @param testerAnnotation a tester annotation
* @return the requirements specified by the annotation
* @throws ConflictingRequirementsException if the requirements are mutually
* inconsistent.
*/
private static TesterRequirements buildTesterRequirements(Annotation testerAnnotation)
throws ConflictingRequirementsException {
Class<? extends Annotation> annotationClass = testerAnnotation.annotationType();
final Feature<?>[] presentFeatures;
final Feature<?>[] absentFeatures;
try {
presentFeatures = (Feature[]) annotationClass.getMethod("value").invoke(testerAnnotation);
absentFeatures = (Feature[]) annotationClass.getMethod("absent").invoke(testerAnnotation);
} catch (Exception e) {
throw new IllegalArgumentException("Error extracting features from tester annotation.", e);
}
Set<Feature>> allPresentFeatures =
addImpliedFeatures(Helpers.<Feature>>copyToSet(presentFeatures));
Set<Feature>> allAbsentFeatures =
addImpliedFeatures(Helpers.<Feature>>copyToSet(absentFeatures));
if (!Collections.disjoint(allPresentFeatures, allAbsentFeatures)) {
throw new ConflictingRequirementsException(
"Annotation explicitly or "
+ "implicitly requires one or more features to be both present "
+ "and absent.",
intersection(allPresentFeatures, allAbsentFeatures),
testerAnnotation);
}
return new TesterRequirements(allPresentFeatures, allAbsentFeatures);
}
/**
* Incorporate additional requirements into an existing requirements object.
* @param requirements the existing requirements object
* @param moreRequirements more requirements to incorporate
* @param source the source of the additional requirements
* (used only for error reporting)
* @return the existing requirements object, modified to include the
* additional requirements
* @throws ConflictingRequirementsException if the additional requirements
* are inconsistent with the existing requirements
*/
private static TesterRequirements incorporateRequirements(
TesterRequirements requirements, TesterRequirements moreRequirements, Object source)
throws ConflictingRequirementsException {
Set<Feature>> presentFeatures = requirements.getPresentFeatures();
Set<Feature>> absentFeatures = requirements.getAbsentFeatures();
Set<Feature>> morePresentFeatures = moreRequirements.getPresentFeatures();
Set<Feature>> moreAbsentFeatures = moreRequirements.getAbsentFeatures();
checkConflict("absent", absentFeatures, "present", morePresentFeatures, source);
checkConflict("present", presentFeatures, "absent", moreAbsentFeatures, source);
presentFeatures.addAll(morePresentFeatures);
absentFeatures.addAll(moreAbsentFeatures);
return requirements;
}
// Used by incorporateRequirements() only
private static void checkConflict(
String earlierRequirement,
Set<Feature>> earlierFeatures,
String newRequirement,
Set<Feature>> newFeatures,
Object source)
throws ConflictingRequirementsException {
if (!Collections.disjoint(newFeatures, earlierFeatures)) {
throw new ConflictingRequirementsException(
String.format(
Locale.ROOT,
"Annotation requires to be %s features that earlier "
+ "annotations required to be %s.",
newRequirement,
earlierRequirement),
intersection(newFeatures, earlierFeatures),
source);
}
}
/**
* Construct a new {@link java.util.Set} that is the intersection
* of the given sets.
*/
public static <T> Set intersection(Set extends T> set1, Set extends T> set2) {
Set<T> result = Helpers.copyToSet(set1);
result.retainAll(set2);
return result;
}
}
Other Java examples (source code examples)
Here is a short list of links related to this Java FeatureUtil.java source code file:
|