All Downloads are FREE. Search and download functionalities are using the official Maven repository.

tech.picnic.errorprone.utils.AnnotationAttributeMatcher Maven / Gradle / Ivy

package tech.picnic.errorprone.utils;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.SetMultimap;
import com.google.errorprone.util.ASTHelpers;
import com.sun.source.tree.AnnotationTree;
import com.sun.source.tree.AssignmentTree;
import com.sun.source.tree.ExpressionTree;
import com.sun.tools.javac.code.Type;
import java.io.Serializable;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Stream;

// XXX: Redefine using auto-value
// XXX: Document design decision that the project stays as close as possible to Error Prone.
//      ^ ... and *therefore* uses Google Auto Value rather than Immutables.org.
// XXX: Make this class implement the `MultiMatcher` interface.
/**
 * A matcher of (annotation, attribute) pairs.
 *
 * 

This class allows one to define a whitelist or blacklist of annotations or their attributes. * Annotations are identified by their fully qualified name. */ public final class AnnotationAttributeMatcher implements Serializable { private static final long serialVersionUID = 1L; private final boolean complement; private final ImmutableSet wholeTypes; private final ImmutableSetMultimap includedAttributes; private final ImmutableSetMultimap excludedAttributes; private AnnotationAttributeMatcher( boolean complement, ImmutableSet wholeTypes, ImmutableSetMultimap includedAttributes, ImmutableSetMultimap excludedAttributes) { this.complement = complement; this.wholeTypes = wholeTypes; this.includedAttributes = includedAttributes; this.excludedAttributes = excludedAttributes; } /** * Creates an {@link AnnotationAttributeMatcher}. * *

Each string provided to this method must be of the form {@code * "some.fully.qualified.AnnotationType"} or {@code * "some.fully.qualified.AnnotationType#attribute"}. * * @param inclusions If specified, only the listed annotations or annotation attributes are * matched. * @param exclusions The listed annotations or annotation attributes are not matched. * @return A non-{@code null} {@link AnnotationAttributeMatcher}. */ public static AnnotationAttributeMatcher create( Optional> inclusions, Iterable exclusions) { Set includedWholeTypes = new HashSet<>(); Set excludedWholeTypes = new HashSet<>(); SetMultimap includedAttributes = HashMultimap.create(); SetMultimap excludedAttributes = HashMultimap.create(); inclusions.ifPresent(incl -> update(incl, includedWholeTypes, includedAttributes)); update(exclusions, excludedWholeTypes, excludedAttributes); includedWholeTypes.removeAll(excludedWholeTypes); includedAttributes.keySet().removeAll(includedWholeTypes); includedAttributes.keySet().removeAll(excludedWholeTypes); excludedAttributes.forEach(includedAttributes::remove); excludedAttributes.keySet().removeAll(excludedWholeTypes); return new AnnotationAttributeMatcher( inclusions.isEmpty(), ImmutableSet.copyOf(inclusions.isPresent() ? includedWholeTypes : excludedWholeTypes), ImmutableSetMultimap.copyOf(includedAttributes), ImmutableSetMultimap.copyOf(excludedAttributes)); } private static void update( Iterable enumeration, Set wholeTypes, SetMultimap attributeRestrictions) { for (String entry : enumeration) { int hash = entry.indexOf('#'); if (hash < 0) { wholeTypes.add(entry); } else { String annotationType = entry.substring(0, hash); String attribute = entry.substring(hash + 1); attributeRestrictions.put(annotationType, attribute); } } } /** * Returns the subset of arguments of the given {@link AnnotationTree} matched by this instance. * * @param tree The annotation AST node to be inspected. * @return Any matching annotation arguments. */ public Stream extractMatchingArguments(AnnotationTree tree) { Type type = ASTHelpers.getType(tree.getAnnotationType()); if (type == null) { return Stream.empty(); } String annotationType = type.toString(); return tree.getArguments().stream() .map(ExpressionTree.class::cast) .filter(a -> matches(annotationType, extractAttributeName(a))); } private static String extractAttributeName(ExpressionTree expr) { return (expr instanceof AssignmentTree assignment) ? ASTHelpers.getSymbol(assignment.getVariable()).getSimpleName().toString() : "value"; } // XXX: The caller of this method can be implemented more efficiently in case of a "wholeTypes" // match. // XXX: Make this method private; re-implement the tests in terms of `#extractMatchingArguments`. @VisibleForTesting boolean matches(String annotationType, String attribute) { if (complement) { return !wholeTypes.contains(annotationType) && !excludedAttributes.containsEntry(annotationType, attribute); } return (wholeTypes.contains(annotationType) && !excludedAttributes.containsEntry(annotationType, attribute)) || includedAttributes.containsEntry(annotationType, attribute); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy