Home | History | Annotate | Download | only in ast
      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 
     37   private BodyDeclarationLocators() {
     38   }
     39 
     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    */
     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    */
     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    */
    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 
    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    */
    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 
    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 
    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