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 org.eclipse.jdt.core.dom.ASTNode; 22 import org.eclipse.jdt.core.dom.AbstractTypeDeclaration; 23 import org.eclipse.jdt.core.dom.BodyDeclaration; 24 import org.eclipse.jdt.core.dom.EnumConstantDeclaration; 25 import org.eclipse.jdt.core.dom.FieldDeclaration; 26 import org.eclipse.jdt.core.dom.MethodDeclaration; 27 28 import java.util.ArrayList; 29 import java.util.Collections; 30 import java.util.List; 31 32 /** 33 * Utility methods associated with {@link BodyDeclarationLocator} and its standard implementations. 34 */ 35 public final class BodyDeclarationLocators { 36 BodyDeclarationLocators()37 private BodyDeclarationLocators() { 38 } 39 createLocatorsFromStrings(String[] locatorStrings)40 public static List<BodyDeclarationLocator> createLocatorsFromStrings(String[] locatorStrings) { 41 ImmutableList.Builder<BodyDeclarationLocator> locatorListBuilder = ImmutableList.builder(); 42 for (String locatorString : locatorStrings) { 43 BodyDeclarationLocator locator = BodyDeclarationLocators.fromStringForm(locatorString); 44 locatorListBuilder.add(locator); 45 } 46 return locatorListBuilder.build(); 47 } 48 49 /** 50 * Generates strings that can be used with {@link #fromStringForm(String)} to generate 51 * {@link BodyDeclarationLocator} instances capable of locating the supplied node. Usually returns 52 * a single element, except for fields declarations. 53 */ toLocatorStringForms(BodyDeclaration bodyDeclaration)54 public static List<String> toLocatorStringForms(BodyDeclaration bodyDeclaration) { 55 List<BodyDeclarationLocator> locators = createLocators(bodyDeclaration); 56 List<String> stringForms = new ArrayList<>(locators.size()); 57 for (BodyDeclarationLocator locator : locators) { 58 stringForms.add(locator.getStringFormType() + ":" + locator.getStringFormTarget()); 59 } 60 return stringForms; 61 } 62 63 /** 64 * Creates {@link BodyDeclarationLocator} objects that can find the supplied 65 * {@link BodyDeclaration}. Usually returns a single element, except for fields declarations. 66 */ createLocators(BodyDeclaration bodyDeclaration)67 public static List<BodyDeclarationLocator> createLocators(BodyDeclaration bodyDeclaration) { 68 AbstractTypeDeclaration typeDeclaration = TypeLocator.findTypeDeclarationNode(bodyDeclaration); 69 if (typeDeclaration == null) { 70 throw new AssertionError("Unable to find type declaration for " + typeDeclaration); 71 } 72 TypeLocator typeLocator = new TypeLocator(typeDeclaration); 73 74 int nodeType = bodyDeclaration.getNodeType(); 75 switch (nodeType) { 76 case ASTNode.FIELD_DECLARATION: 77 List<String> fieldNames = FieldLocator.getFieldNames((FieldDeclaration) bodyDeclaration); 78 List<BodyDeclarationLocator> fieldLocators = new ArrayList<>(fieldNames.size()); 79 for (String fieldName : fieldNames) { 80 fieldLocators.add(new FieldLocator(typeLocator, fieldName)); 81 } 82 return fieldLocators; 83 case ASTNode.METHOD_DECLARATION: 84 MethodDeclaration methodDeclaration = (MethodDeclaration) bodyDeclaration; 85 List<String> parameterTypeNames = ParameterMatcher.getParameterTypeNames(methodDeclaration); 86 return ImmutableList.<BodyDeclarationLocator>of( 87 new MethodLocator(typeLocator, methodDeclaration.getName().getIdentifier(), 88 parameterTypeNames)); 89 case ASTNode.TYPE_DECLARATION: 90 case ASTNode.ENUM_DECLARATION: 91 return ImmutableList.<BodyDeclarationLocator>of(typeLocator); 92 case ASTNode.ENUM_CONSTANT_DECLARATION: 93 EnumConstantDeclaration enumConstantDeclaration = (EnumConstantDeclaration) bodyDeclaration; 94 String constantName = enumConstantDeclaration.getName().getIdentifier(); 95 return ImmutableList.<BodyDeclarationLocator>of( 96 new EnumConstantLocator(typeLocator, constantName)); 97 default: 98 throw new IllegalArgumentException("Unsupported node type: " + nodeType); 99 } 100 } 101 102 /** 103 * Creates a {@link BodyDeclarationLocator} from a string form of a body declaration. 104 * See {@link #toLocatorStringForms(BodyDeclaration)}. 105 */ fromStringForm(String stringForm)106 public static BodyDeclarationLocator fromStringForm(String stringForm) { 107 List<String> components = splitInTwo(stringForm, ":"); 108 String locatorTypeName = components.get(0); 109 String locatorString = components.get(1); 110 switch (locatorTypeName) { 111 case FieldLocator.LOCATOR_TYPE_NAME: 112 List<String> typeAndField = splitInTwo(locatorString, "#"); 113 return new FieldLocator(new TypeLocator(typeAndField.get(0)), typeAndField.get(1)); 114 case MethodLocator.LOCATOR_TYPE_NAME: 115 List<String> typeAndMethod = splitInTwo(locatorString, "#"); 116 String methodNameAndParameters = typeAndMethod.get(1); 117 int parameterStartIndex = methodNameAndParameters.indexOf('('); 118 if (parameterStartIndex == -1) { 119 throw new IllegalArgumentException("No '(' found in " + methodNameAndParameters); 120 } 121 String methodName = methodNameAndParameters.substring(0, parameterStartIndex); 122 String parametersString = methodNameAndParameters.substring(parameterStartIndex); 123 List<String> parameterTypes = extractParameterTypes(parametersString); 124 return new MethodLocator(new TypeLocator(typeAndMethod.get(0)), methodName, parameterTypes); 125 case TypeLocator.LOCATOR_TYPE_NAME: 126 return new TypeLocator(locatorString); 127 case EnumConstantLocator.LOCATOR_TYPE_NAME: 128 List<String> typeAndConstant = splitInTwo(locatorString, "#"); 129 return new EnumConstantLocator( 130 new TypeLocator(typeAndConstant.get(0)), typeAndConstant.get(1)); 131 default: 132 throw new IllegalArgumentException("Unsupported locator type: " + locatorTypeName); 133 } 134 } 135 matchesAny(List<BodyDeclarationLocator> locators, BodyDeclaration node)136 public static boolean matchesAny(List<BodyDeclarationLocator> locators, BodyDeclaration node) { 137 for (BodyDeclarationLocator locator : locators) { 138 if (locator.matches(node)) { 139 return true; 140 } 141 } 142 return false; 143 } 144 145 /** 146 * Finds the declaration associated with a given node. If the node is not a child of a declaration 147 * {@code null} is returned. 148 */ findDeclarationNode(ASTNode node)149 public static BodyDeclaration findDeclarationNode(ASTNode node) { 150 ASTNode ancestor = node; 151 while (ancestor != null && !(ancestor instanceof BodyDeclaration)) { 152 ancestor = ancestor.getParent(); 153 } 154 155 return ancestor instanceof BodyDeclaration ? (BodyDeclaration) ancestor : null; 156 } 157 extractParameterTypes(String parametersString)158 private static List<String> extractParameterTypes(String parametersString) { 159 if (!(parametersString.startsWith("(") && parametersString.endsWith(")"))) { 160 throw new IllegalArgumentException("Expected \"(<types>)\" but was " + parametersString); 161 } 162 parametersString = parametersString.substring(1, parametersString.length() - 1); 163 if (parametersString.isEmpty()) { 164 return Collections.emptyList(); 165 } 166 return Splitter.on(',').splitToList(parametersString); 167 } 168 splitInTwo(String string, String separator)169 private static List<String> splitInTwo(String string, String separator) { 170 List<String> components = Splitter.on(separator).splitToList(string); 171 if (components.size() != 2) { 172 throw new IllegalArgumentException("Cannot split " + string + " on " + separator); 173 } 174 return components; 175 } 176 } 177