1 /* 2 * Copyright 2016 Federico Tomassetti 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.github.javaparser.symbolsolver.javaparser; 18 19 import com.github.javaparser.ast.CompilationUnit; 20 import com.github.javaparser.ast.Node; 21 import com.github.javaparser.ast.body.*; 22 import com.github.javaparser.ast.expr.MethodCallExpr; 23 import com.github.javaparser.ast.expr.NameExpr; 24 import com.github.javaparser.ast.expr.SimpleName; 25 import com.github.javaparser.ast.stmt.ReturnStmt; 26 import com.github.javaparser.ast.stmt.SwitchStmt; 27 28 import java.util.List; 29 import java.util.Optional; 30 31 /** 32 * This class can be used to easily retrieve nodes from a JavaParser AST. 33 * 34 * @author Federico Tomassetti 35 */ 36 public final class Navigator { 37 38 private Navigator() { 39 // prevent instantiation 40 } 41 42 /** 43 * @deprecated use Node.getParentNode 44 */ 45 @Deprecated 46 public static Node getParentNode(Node node) { 47 return node.getParentNode().orElse(null); 48 } 49 50 public static Node requireParentNode(Node node) { 51 return node.getParentNode().orElseThrow(() -> new IllegalStateException("Parent not found, the node does not appear to be inserted in a correct AST")); 52 } 53 54 public static Optional<TypeDeclaration<?>> findType(CompilationUnit cu, String qualifiedName) { 55 if (cu.getTypes().isEmpty()) { 56 return Optional.empty(); 57 } 58 59 final String typeName = getOuterTypeName(qualifiedName); 60 Optional<TypeDeclaration<?>> type = cu.getTypes().stream().filter((t) -> t.getName().getId().equals(typeName)).findFirst(); 61 62 final String innerTypeName = getInnerTypeName(qualifiedName); 63 if (type.isPresent() && !innerTypeName.isEmpty()) { 64 return findType(type.get(), innerTypeName); 65 } 66 return type; 67 } 68 69 public static Optional<TypeDeclaration<?>> findType(TypeDeclaration<?> td, String qualifiedName) { 70 final String typeName = getOuterTypeName(qualifiedName); 71 72 Optional<TypeDeclaration<?>> type = Optional.empty(); 73 for (Node n : td.getMembers()) { 74 if (n instanceof TypeDeclaration && ((TypeDeclaration<?>) n).getName().getId().equals(typeName)) { 75 type = Optional.of((TypeDeclaration<?>) n); 76 break; 77 } 78 } 79 final String innerTypeName = getInnerTypeName(qualifiedName); 80 if (type.isPresent() && !innerTypeName.isEmpty()) { 81 return findType(type.get(), innerTypeName); 82 } 83 return type; 84 } 85 86 public static ClassOrInterfaceDeclaration demandClass(CompilationUnit cu, String qualifiedName) { 87 ClassOrInterfaceDeclaration cd = demandClassOrInterface(cu, qualifiedName); 88 if (cd.isInterface()) { 89 throw new IllegalStateException("Type is not a class"); 90 } 91 return cd; 92 } 93 94 public static EnumDeclaration demandEnum(CompilationUnit cu, String qualifiedName) { 95 Optional<TypeDeclaration<?>> res = findType(cu, qualifiedName); 96 if (!res.isPresent()) { 97 throw new IllegalStateException("No type found"); 98 } 99 if (!(res.get() instanceof EnumDeclaration)) { 100 throw new IllegalStateException("Type is not an enum"); 101 } 102 return (EnumDeclaration) res.get(); 103 } 104 105 public static MethodDeclaration demandMethod(TypeDeclaration<?> cd, String name) { 106 MethodDeclaration found = null; 107 for (BodyDeclaration<?> bd : cd.getMembers()) { 108 if (bd instanceof MethodDeclaration) { 109 MethodDeclaration md = (MethodDeclaration) bd; 110 if (md.getNameAsString().equals(name)) { 111 if (found != null) { 112 throw new IllegalStateException("Ambiguous getName"); 113 } 114 found = md; 115 } 116 } 117 } 118 if (found == null) { 119 throw new IllegalStateException("No method called " + name); 120 } 121 return found; 122 } 123 124 public static VariableDeclarator demandField(ClassOrInterfaceDeclaration cd, String name) { 125 for (BodyDeclaration<?> bd : cd.getMembers()) { 126 if (bd instanceof FieldDeclaration) { 127 FieldDeclaration fd = (FieldDeclaration) bd; 128 for (VariableDeclarator vd : fd.getVariables()) { 129 if (vd.getName().getId().equals(name)) { 130 return vd; 131 } 132 } 133 } 134 } 135 throw new IllegalStateException("No field with given name"); 136 } 137 138 public static Optional<NameExpr> findNameExpression(Node node, String name) { 139 return node.findFirst(NameExpr.class, n -> n.getNameAsString().equals(name)); 140 } 141 142 public static Optional<SimpleName> findSimpleName(Node node, String name) { 143 return node.findFirst(SimpleName.class, n -> n.asString().equals(name)); 144 } 145 146 147 public static Optional<MethodCallExpr> findMethodCall(Node node, String methodName) { 148 return node.findFirst(MethodCallExpr.class, n -> n.getNameAsString().equals(methodName)); 149 } 150 151 public static Optional<VariableDeclarator> demandVariableDeclaration(Node node, String name) { 152 return node.findFirst(VariableDeclarator.class, n -> n.getNameAsString().equals(name)); 153 } 154 155 public static ClassOrInterfaceDeclaration demandClassOrInterface(CompilationUnit compilationUnit, String qualifiedName) { 156 return findType(compilationUnit, qualifiedName) 157 .map(res -> res.toClassOrInterfaceDeclaration().orElseThrow(() -> new IllegalStateException("Type is not a class or an interface, it is " + res.getClass().getCanonicalName()))) 158 .orElseThrow(() -> new IllegalStateException("No type named '" + qualifiedName + "'found")); 159 } 160 161 // TODO should be demand or requireSwitch 162 public static SwitchStmt findSwitch(Node node) { 163 return findSwitchHelper(node).orElseThrow(IllegalArgumentException::new); 164 } 165 166 public static <N extends Node> N findNodeOfGivenClass(Node node, Class<N> clazz) { 167 return node.findFirst(clazz).orElseThrow(IllegalArgumentException::new); 168 } 169 170 /** 171 * @deprecated use Node.findAll instead 172 */ 173 @Deprecated 174 public static <N extends Node> List<N> findAllNodesOfGivenClass(Node node, Class<N> clazz) { 175 return node.findAll(clazz); 176 } 177 178 // TODO should be demand or require... 179 public static ReturnStmt findReturnStmt(MethodDeclaration method) { 180 return findNodeOfGivenClass(method, ReturnStmt.class); 181 } 182 183 /** 184 * @deprecated use Node.findParent instead 185 */ 186 @Deprecated 187 public static <N extends Node> Optional<N> findAncestor(Node node, Class<N> clazz) { 188 return node.findParent(clazz); 189 } 190 191 /// 192 /// Private methods 193 /// 194 195 private static String getOuterTypeName(String qualifiedName) { 196 return qualifiedName.split("\\.", 2)[0]; 197 } 198 199 private static String getInnerTypeName(String qualifiedName) { 200 if (qualifiedName.contains(".")) { 201 return qualifiedName.split("\\.", 2)[1]; 202 } 203 return ""; 204 } 205 206 private static Optional<SwitchStmt> findSwitchHelper(Node node) { 207 // TODO can be replaced by findFirst with the correct algorithm. 208 if (node instanceof SwitchStmt) { 209 return Optional.of((SwitchStmt) node); 210 } 211 for (Node child : node.getChildNodes()) { 212 Optional<SwitchStmt> resChild = findSwitchHelper(child); 213 if (resChild.isPresent()) { 214 return resChild; 215 } 216 } 217 return Optional.empty(); 218 } 219 } 220