com.google.testing.compile.MoreTrees Maven / Gradle / Ivy
/*
* Copyright (C) 2014 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.testing.compile;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.testing.compile.Parser.ParseResult;
import com.sun.source.tree.BreakTree;
import com.sun.source.tree.ClassTree;
import com.sun.source.tree.CompilationUnitTree;
import com.sun.source.tree.ContinueTree;
import com.sun.source.tree.IdentifierTree;
import com.sun.source.tree.LabeledStatementTree;
import com.sun.source.tree.LiteralTree;
import com.sun.source.tree.MemberSelectTree;
import com.sun.source.tree.MethodTree;
import com.sun.source.tree.Tree;
import com.sun.source.tree.TypeParameterTree;
import com.sun.source.tree.VariableTree;
import com.sun.source.util.TreePath;
import com.sun.source.util.TreePathScanner;
import java.util.Arrays;
import javax.annotation.Nullable;
/**
* A class containing methods which are useful for gaining access to {@code Tree} instances from
* within unit tests.
*/
@SuppressWarnings("restriction") // Sun APIs usage intended
final class MoreTrees {
/** Parses the source given into a {@link CompilationUnitTree}. */
static CompilationUnitTree parseLinesToTree(String... source) {
return parseLinesToTree(Arrays.asList(source));
}
/** Parses the source given into a {@link CompilationUnitTree}. */
static CompilationUnitTree parseLinesToTree(Iterable source) {
Iterable parseResults =
Parser.parse(ImmutableList.of(JavaFileObjects.forSourceLines("", source)))
.compilationUnits();
return Iterables.getOnlyElement(parseResults);
}
/** Parses the source given and produces a {@link ParseResult}. */
static ParseResult parseLines(String... source) {
return parseLines(Arrays.asList(source));
}
/** Parses the source given and produces a {@link ParseResult}. */
static ParseResult parseLines(Iterable source) {
return Parser.parse(ImmutableList.of(JavaFileObjects.forSourceLines("", source)));
}
/**
* Finds the first instance of the given {@link Tree.Kind} that is a subtree of the root provided.
*
* @throw IllegalArgumentException if no such subtree exists.
*/
static Tree findSubtree(CompilationUnitTree root, Tree.Kind treeKind) {
return findSubtree(root, treeKind, null);
}
/**
* Finds the first instance of the given {@link Tree.Kind} that is a subtree of the root provided
* and which matches identifier string.
*
* See the doc on {@link #findSubtreePath} for details on the identifier param.
*
* @throw IllegalArgumentException if no such subtree exists.
*/
static Tree findSubtree(CompilationUnitTree root, Tree.Kind treeKind,
@Nullable String identifier) {
return findSubtreePath(root, treeKind, identifier).getLeaf();
}
/**
* Finds a path to the first instance of the given {@link Tree.Kind} that is a subtree of the root
* provided.
*
* @throw IllegalArgumentException if no such subtree exists.
*/
static TreePath findSubtreePath(CompilationUnitTree root, Tree.Kind treeKind) {
return findSubtreePath(root, treeKind, null);
}
/**
* Finds a TreePath terminating at the first instance of the given {@link Tree.Kind} that is a
* subtree of the root provided and which matches the optional identifier string.
*
*
Identifier strings are only valid for some {@link Tree} and may take different meanings. The
* following list provides a quick summary of the matching behavior:
*
* - {@link Tree}s with kind {@code BREAK}, {@code CONTINUE}, and {@code LABELED_STATEMENT}
* match on their {@code getLabel()} methods.
*
- {@link Tree}s with kind {@code ANNOTATION_TYPE}, {@code CLASS}, {@code ENUM},
* and {@code INTERFACE} match on their {@code getSimpleName()} method.
*
- {@link Tree}s with kind {@code *_LITERAL} match on their {@code getValue()} method.
*
- {@link Tree}s with kind {@code IDENTIFIER}, {@code METHOD}, and {@code TYPE_PARAMETER}
* match on their {@code getName()} method.
*
- {@link Tree}s with kind {@code MEMBER_SELECT} matches on their {@code getIdentifier()}
* method.
*
* @throws IllegalArgumentException if no such subtree exists or if an identifier-based match
* is requested for any type but one of the following:
*/
static TreePath findSubtreePath(CompilationUnitTree root, Tree.Kind treeKind,
@Nullable String identifier) {
SearchScanner subtreeFinder = new SearchScanner(treeKind,
(identifier == null) ? Optional.
absent() : Optional.of(identifier));
Optional res = subtreeFinder.scan(root, null);
Preconditions.checkArgument(res.isPresent(), "Couldn't find any subtree matching the given "
+ "criteria. Root: %s, Class: %s, Identifier: %s", root, treeKind, identifier);
return res.get();
}
/**
* A {@link TreePathScanner} to power the subtree searches in this class
*/
static final class SearchScanner extends TreePathScanner, Void> {
private final Optional identifier;
private final Tree.Kind kindSought;
public SearchScanner(Tree.Kind kindSought, Optional identifier) {
this.kindSought = kindSought;
this.identifier = identifier;
}
/**
* Returns {@code true} iff the node and corresponding id value provided match the identifier
* and kind sought.
*/
private boolean isMatch(Tree node, Optional