/* * Copyright 2014 Google Inc. All rights reserved. * * 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.errorprone.refaster; import com.google.auto.value.AutoValue; import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.common.collect.ClassToInstanceMap; import com.google.common.collect.ImmutableClassToInstanceMap; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Maps; import com.google.errorprone.VisitorState; import com.google.errorprone.matchers.Matcher; import com.google.errorprone.refaster.UPlaceholderExpression.PlaceholderParamIdent; import com.google.errorprone.refaster.annotation.Matches; import com.google.errorprone.refaster.annotation.MayOptionallyUse; import com.google.errorprone.refaster.annotation.NotMatches; import com.google.errorprone.refaster.annotation.OfKind; import com.google.errorprone.refaster.annotation.Placeholder; import com.sun.source.tree.ExpressionTree; import com.sun.source.tree.Tree; import com.sun.tools.javac.tree.JCTree.JCExpression; import com.sun.tools.javac.tree.JCTree.JCStatement; import com.sun.tools.javac.util.List; import java.io.Serializable; import java.lang.annotation.Annotation; import java.util.Arrays; import java.util.Set; /** * Representation of a {@code Refaster} placeholder method, which can represent an arbitrary * operation on a specific set of expressions. * * @author lowasser@google.com (Louis Wasserman) */ @AutoValue abstract class PlaceholderMethod implements Serializable { static PlaceholderMethod create(CharSequence name, UType returnType, ImmutableMap<UVariableDecl, ImmutableClassToInstanceMap<Annotation>> parameters, ClassToInstanceMap<Annotation> annotations) { final boolean allowsIdentity = annotations.getInstance(Placeholder.class).allowsIdentity(); final Class<? extends Matcher<? super ExpressionTree>> matchesClass = annotations.containsKey(Matches.class) ? UTemplater.getValue(annotations.getInstance(Matches.class)) : null; final Class<? extends Matcher<? super ExpressionTree>> notMatchesClass = annotations.containsKey(NotMatches.class) ? UTemplater.getValue(annotations.getInstance(NotMatches.class)) : null; final Predicate<Tree.Kind> allowedKinds = annotations.containsKey(OfKind.class) ? Predicates.<Tree.Kind>in(Arrays.asList(annotations.getInstance(OfKind.class).value())) : Predicates.<Tree.Kind>alwaysTrue(); class PlaceholderMatcher implements Serializable, Matcher<ExpressionTree> { @Override public boolean matches(ExpressionTree t, VisitorState state) { try { return (allowsIdentity || !(t instanceof PlaceholderParamIdent)) && (matchesClass == null || matchesClass.newInstance().matches(t, state)) && (notMatchesClass == null || !notMatchesClass.newInstance().matches(t, state)) && allowedKinds.apply(t.getKind()); } catch (InstantiationException | IllegalAccessException e) { throw new RuntimeException(e); } } } return new AutoValue_PlaceholderMethod( StringName.of(name), returnType, parameters, new PlaceholderMatcher(), ImmutableClassToInstanceMap.<Annotation, Annotation>copyOf(annotations)); } abstract StringName name(); abstract UType returnType(); abstract ImmutableMap<UVariableDecl, ImmutableClassToInstanceMap<Annotation>> annotatedParameters(); abstract Matcher<ExpressionTree> matcher(); abstract ImmutableClassToInstanceMap<Annotation> annotations(); ImmutableSet<UVariableDecl> parameters() { return annotatedParameters().keySet(); } /** * Parameters which must be referenced in any tree matched to this placeholder. */ Set<UVariableDecl> requiredParameters() { return Maps.filterValues(annotatedParameters(), new Predicate<ImmutableClassToInstanceMap<Annotation>>() { @Override public boolean apply(ImmutableClassToInstanceMap<Annotation> annotations) { return !annotations.containsKey(MayOptionallyUse.class); } }).keySet(); } PlaceholderExpressionKey exprKey() { return new PlaceholderExpressionKey(name().contents(), this); } PlaceholderBlockKey blockKey() { return new PlaceholderBlockKey(name().contents(), this); } static final class PlaceholderExpressionKey extends Bindings.Key<JCExpression> implements Comparable<PlaceholderExpressionKey> { final PlaceholderMethod method; private PlaceholderExpressionKey(String str, PlaceholderMethod method) { super(str); this.method = method; } @Override public int compareTo(PlaceholderExpressionKey o) { return getIdentifier().compareTo(o.getIdentifier()); } } static final class PlaceholderBlockKey extends Bindings.Key<List<JCStatement>> { final PlaceholderMethod method; private PlaceholderBlockKey(String str, PlaceholderMethod method) { super(str); this.method = method; } } }