1 /* 2 * Copyright (C) 2015 The Android Open Source Project 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 package com.google.currysrc.api.process.ast; 17 18 import com.google.common.base.Splitter; 19 import com.google.common.collect.ImmutableList; 20 21 import java.io.IOException; 22 import java.nio.file.Files; 23 import java.nio.file.Path; 24 import org.eclipse.jdt.core.dom.ASTNode; 25 import org.eclipse.jdt.core.dom.AbstractTypeDeclaration; 26 import org.eclipse.jdt.core.dom.BodyDeclaration; 27 import org.eclipse.jdt.core.dom.EnumConstantDeclaration; 28 import org.eclipse.jdt.core.dom.FieldDeclaration; 29 import org.eclipse.jdt.core.dom.MethodDeclaration; 30 31 import java.util.ArrayList; 32 import java.util.Collections; 33 import java.util.List; 34 35 /** 36 * Utility methods associated with {@link BodyDeclarationLocator} and its standard implementations. 37 */ 38 public final class BodyDeclarationLocators { 39 BodyDeclarationLocators()40 private BodyDeclarationLocators() { 41 } 42 createLocatorsFromStrings(String[] locatorStrings)43 public static List<BodyDeclarationLocator> createLocatorsFromStrings(String[] locatorStrings) { 44 ImmutableList.Builder<BodyDeclarationLocator> locatorListBuilder = ImmutableList.builder(); 45 for (String locatorString : locatorStrings) { 46 BodyDeclarationLocator locator = BodyDeclarationLocators.fromStringForm(locatorString); 47 locatorListBuilder.add(locator); 48 } 49 return locatorListBuilder.build(); 50 } 51 52 /** 53 * Generates strings that can be used with {@link #fromStringForm(String)} to generate 54 * {@link BodyDeclarationLocator} instances capable of locating the supplied node. Usually returns 55 * a single element, except for fields declarations. 56 */ toLocatorStringForms(BodyDeclaration bodyDeclaration)57 public static List<String> toLocatorStringForms(BodyDeclaration bodyDeclaration) { 58 List<BodyDeclarationLocator> locators = createLocators(bodyDeclaration); 59 List<String> stringForms = new ArrayList<>(locators.size()); 60 for (BodyDeclarationLocator locator : locators) { 61 stringForms.add(locator.getStringFormType() + ":" + locator.getStringFormTarget()); 62 } 63 return stringForms; 64 } 65 66 /** 67 * Creates {@link BodyDeclarationLocator} objects that can find the supplied 68 * {@link BodyDeclaration}. Usually returns a single element, except for fields declarations. 69 */ createLocators(BodyDeclaration bodyDeclaration)70 public static List<BodyDeclarationLocator> createLocators(BodyDeclaration bodyDeclaration) { 71 AbstractTypeDeclaration typeDeclaration = TypeLocator.findTypeDeclarationNode(bodyDeclaration); 72 if (typeDeclaration == null) { 73 throw new AssertionError("Unable to find type declaration for " + typeDeclaration); 74 } 75 TypeLocator typeLocator = new TypeLocator(typeDeclaration); 76 77 int nodeType = bodyDeclaration.getNodeType(); 78 switch (nodeType) { 79 case ASTNode.FIELD_DECLARATION: 80 List<String> fieldNames = FieldLocator.getFieldNames((FieldDeclaration) bodyDeclaration); 81 List<BodyDeclarationLocator> fieldLocators = new ArrayList<>(fieldNames.size()); 82 for (String fieldName : fieldNames) { 83 fieldLocators.add(new FieldLocator(typeLocator, fieldName)); 84 } 85 return fieldLocators; 86 case ASTNode.METHOD_DECLARATION: 87 MethodDeclaration methodDeclaration = (MethodDeclaration) bodyDeclaration; 88 List<String> parameterTypeNames = ParameterMatcher.getParameterTypeNames(methodDeclaration); 89 return ImmutableList.<BodyDeclarationLocator>of( 90 new MethodLocator(typeLocator, methodDeclaration.getName().getIdentifier(), 91 parameterTypeNames)); 92 case ASTNode.TYPE_DECLARATION: 93 case ASTNode.ENUM_DECLARATION: 94 return ImmutableList.<BodyDeclarationLocator>of(typeLocator); 95 case ASTNode.ENUM_CONSTANT_DECLARATION: 96 EnumConstantDeclaration enumConstantDeclaration = (EnumConstantDeclaration) bodyDeclaration; 97 String constantName = enumConstantDeclaration.getName().getIdentifier(); 98 return ImmutableList.<BodyDeclarationLocator>of( 99 new EnumConstantLocator(typeLocator, constantName)); 100 default: 101 throw new IllegalArgumentException("Unsupported node type: " + nodeType); 102 } 103 } 104 105 /** 106 * Creates a {@link BodyDeclarationLocator} from a string form of a body declaration. 107 * See {@link #toLocatorStringForms(BodyDeclaration)}. 108 */ fromStringForm(String stringForm)109 public static BodyDeclarationLocator fromStringForm(String stringForm) { 110 List<String> components = splitInTwo(stringForm, ":"); 111 String locatorTypeName = components.get(0); 112 String locatorString = components.get(1); 113 switch (locatorTypeName) { 114 case FieldLocator.LOCATOR_TYPE_NAME: 115 List<String> typeAndField = splitInTwo(locatorString, "#"); 116 return new FieldLocator(new TypeLocator(typeAndField.get(0)), typeAndField.get(1)); 117 case MethodLocator.LOCATOR_TYPE_NAME: 118 List<String> typeAndMethod = splitInTwo(locatorString, "#"); 119 String methodNameAndParameters = typeAndMethod.get(1); 120 int parameterStartIndex = methodNameAndParameters.indexOf('('); 121 if (parameterStartIndex == -1) { 122 throw new IllegalArgumentException("No '(' found in " + methodNameAndParameters); 123 } 124 String methodName = methodNameAndParameters.substring(0, parameterStartIndex); 125 String parametersString = methodNameAndParameters.substring(parameterStartIndex); 126 List<String> parameterTypes = extractParameterTypes(parametersString); 127 return new MethodLocator(new TypeLocator(typeAndMethod.get(0)), methodName, parameterTypes); 128 case TypeLocator.LOCATOR_TYPE_NAME: 129 return new TypeLocator(locatorString); 130 case EnumConstantLocator.LOCATOR_TYPE_NAME: 131 List<String> typeAndConstant = splitInTwo(locatorString, "#"); 132 return new EnumConstantLocator( 133 new TypeLocator(typeAndConstant.get(0)), typeAndConstant.get(1)); 134 default: 135 throw new IllegalArgumentException("Unsupported locator type: " + locatorTypeName); 136 } 137 } 138 matchesAny(List<BodyDeclarationLocator> locators, BodyDeclaration node)139 public static boolean matchesAny(List<BodyDeclarationLocator> locators, BodyDeclaration node) { 140 for (BodyDeclarationLocator locator : locators) { 141 if (locator.matches(node)) { 142 return true; 143 } 144 } 145 return false; 146 } 147 148 /** 149 * Finds the declaration associated with a given node. If the node is not a child of a declaration 150 * {@code null} is returned. 151 */ findDeclarationNode(ASTNode node)152 public static BodyDeclaration findDeclarationNode(ASTNode node) { 153 ASTNode ancestor = node; 154 while (ancestor != null && !(ancestor instanceof BodyDeclaration)) { 155 ancestor = ancestor.getParent(); 156 } 157 158 return ancestor instanceof BodyDeclaration ? (BodyDeclaration) ancestor : null; 159 } 160 extractParameterTypes(String parametersString)161 private static List<String> extractParameterTypes(String parametersString) { 162 if (!(parametersString.startsWith("(") && parametersString.endsWith(")"))) { 163 throw new IllegalArgumentException("Expected \"(<types>)\" but was " + parametersString); 164 } 165 parametersString = parametersString.substring(1, parametersString.length() - 1); 166 if (parametersString.isEmpty()) { 167 return Collections.emptyList(); 168 } 169 return splitParameters(parametersString); 170 } 171 172 /** 173 * Split parameters by ','. Support simple generics syntax. 174 * TODO: Replace the implementation by JDT parser. 175 */ splitParameters(String parametersString)176 private static List<String> splitParameters(String parametersString) { 177 List<String> result = new ArrayList<>(); 178 int genericsLevel = 0; 179 int start = 0; 180 for (int p = 0; p < parametersString.length(); p++) { 181 char c = parametersString.charAt(p); 182 if (genericsLevel == 0 && c == ',') { 183 result.add(parametersString.substring(start, p)); 184 start = p + 1; 185 } else if (c == '<') { 186 genericsLevel++; 187 } else if (c == '>') { 188 genericsLevel--; 189 } 190 } 191 if (start < parametersString.length()) { 192 result.add(parametersString.substring(start, parametersString.length())); 193 } 194 return result; 195 } 196 197 splitInTwo(String string, String separator)198 private static List<String> splitInTwo(String string, String separator) { 199 List<String> components = Splitter.on(separator).splitToList(string); 200 if (components.size() != 2) { 201 throw new IllegalArgumentException("Cannot split " + string + " on " + separator); 202 } 203 return components; 204 } 205 206 /** 207 * Read body declarations from a file. 208 * 209 * <p>Blank lines and lines starting with a {@code #} are ignored. 210 * 211 * @param path the path to the file. 212 * @return The list of {@link BodyDeclarationLocator} instances. 213 */ readBodyDeclarationLocators(Path path)214 public static List<BodyDeclarationLocator> readBodyDeclarationLocators(Path path) { 215 return createLocatorsFromStrings(TypeLocator.readLines(path)); 216 } 217 } 218