104b228542fa91e91551c6ea4e6a2bac1d55ca1afNeil Fuller/*
204b228542fa91e91551c6ea4e6a2bac1d55ca1afNeil Fuller * Copyright (C) 2015 The Android Open Source Project
304b228542fa91e91551c6ea4e6a2bac1d55ca1afNeil Fuller *
404b228542fa91e91551c6ea4e6a2bac1d55ca1afNeil Fuller * Licensed under the Apache License, Version 2.0 (the "License");
504b228542fa91e91551c6ea4e6a2bac1d55ca1afNeil Fuller * you may not use this file except in compliance with the License.
604b228542fa91e91551c6ea4e6a2bac1d55ca1afNeil Fuller * You may obtain a copy of the License at
704b228542fa91e91551c6ea4e6a2bac1d55ca1afNeil Fuller *
804b228542fa91e91551c6ea4e6a2bac1d55ca1afNeil Fuller *      http://www.apache.org/licenses/LICENSE-2.0
904b228542fa91e91551c6ea4e6a2bac1d55ca1afNeil Fuller *
1004b228542fa91e91551c6ea4e6a2bac1d55ca1afNeil Fuller * Unless required by applicable law or agreed to in writing, software
1104b228542fa91e91551c6ea4e6a2bac1d55ca1afNeil Fuller * distributed under the License is distributed on an "AS IS" BASIS,
1204b228542fa91e91551c6ea4e6a2bac1d55ca1afNeil Fuller * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1304b228542fa91e91551c6ea4e6a2bac1d55ca1afNeil Fuller * See the License for the specific language governing permissions and
1404b228542fa91e91551c6ea4e6a2bac1d55ca1afNeil Fuller * limitations under the License.
1504b228542fa91e91551c6ea4e6a2bac1d55ca1afNeil Fuller */
16ac1c676b1256aaa85c5cc22494ea56bd2d276b9fNeil Fullerpackage com.google.currysrc.api.process.ast;
1704b228542fa91e91551c6ea4e6a2bac1d55ca1afNeil Fuller
1804b228542fa91e91551c6ea4e6a2bac1d55ca1afNeil Fullerimport com.google.common.base.Splitter;
19cc49f813b0d7bf6664102b30b5513fd21c362e0dNeil Fullerimport com.google.common.collect.ImmutableList;
2004b228542fa91e91551c6ea4e6a2bac1d55ca1afNeil Fuller
2104b228542fa91e91551c6ea4e6a2bac1d55ca1afNeil Fullerimport org.eclipse.jdt.core.dom.ASTNode;
2204b228542fa91e91551c6ea4e6a2bac1d55ca1afNeil Fullerimport org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
2304b228542fa91e91551c6ea4e6a2bac1d55ca1afNeil Fullerimport org.eclipse.jdt.core.dom.BodyDeclaration;
2404b228542fa91e91551c6ea4e6a2bac1d55ca1afNeil Fullerimport org.eclipse.jdt.core.dom.EnumConstantDeclaration;
2504b228542fa91e91551c6ea4e6a2bac1d55ca1afNeil Fullerimport org.eclipse.jdt.core.dom.FieldDeclaration;
2604b228542fa91e91551c6ea4e6a2bac1d55ca1afNeil Fullerimport org.eclipse.jdt.core.dom.MethodDeclaration;
2704b228542fa91e91551c6ea4e6a2bac1d55ca1afNeil Fuller
2893cf604e9dd0525f15bc0a7450b2a35f3884c298Neil Fullerimport java.util.ArrayList;
2904b228542fa91e91551c6ea4e6a2bac1d55ca1afNeil Fullerimport java.util.Collections;
3004b228542fa91e91551c6ea4e6a2bac1d55ca1afNeil Fullerimport java.util.List;
3104b228542fa91e91551c6ea4e6a2bac1d55ca1afNeil Fuller
3204b228542fa91e91551c6ea4e6a2bac1d55ca1afNeil Fuller/**
33ac1c676b1256aaa85c5cc22494ea56bd2d276b9fNeil Fuller * Utility methods associated with {@link BodyDeclarationLocator} and its standard implementations.
3404b228542fa91e91551c6ea4e6a2bac1d55ca1afNeil Fuller */
35ac1c676b1256aaa85c5cc22494ea56bd2d276b9fNeil Fullerpublic final class BodyDeclarationLocators {
3604b228542fa91e91551c6ea4e6a2bac1d55ca1afNeil Fuller
37ac1c676b1256aaa85c5cc22494ea56bd2d276b9fNeil Fuller  private BodyDeclarationLocators() {
3804b228542fa91e91551c6ea4e6a2bac1d55ca1afNeil Fuller  }
3904b228542fa91e91551c6ea4e6a2bac1d55ca1afNeil Fuller
40ac1c676b1256aaa85c5cc22494ea56bd2d276b9fNeil Fuller  public static List<BodyDeclarationLocator> createLocatorsFromStrings(String[] locatorStrings) {
41ac1c676b1256aaa85c5cc22494ea56bd2d276b9fNeil Fuller    ImmutableList.Builder<BodyDeclarationLocator> locatorListBuilder = ImmutableList.builder();
42ac1c676b1256aaa85c5cc22494ea56bd2d276b9fNeil Fuller    for (String locatorString : locatorStrings) {
43ac1c676b1256aaa85c5cc22494ea56bd2d276b9fNeil Fuller      BodyDeclarationLocator locator = BodyDeclarationLocators.fromStringForm(locatorString);
44ac1c676b1256aaa85c5cc22494ea56bd2d276b9fNeil Fuller      locatorListBuilder.add(locator);
45cc49f813b0d7bf6664102b30b5513fd21c362e0dNeil Fuller    }
46ac1c676b1256aaa85c5cc22494ea56bd2d276b9fNeil Fuller    return locatorListBuilder.build();
47cc49f813b0d7bf6664102b30b5513fd21c362e0dNeil Fuller  }
48cc49f813b0d7bf6664102b30b5513fd21c362e0dNeil Fuller
4904b228542fa91e91551c6ea4e6a2bac1d55ca1afNeil Fuller  /**
5093cf604e9dd0525f15bc0a7450b2a35f3884c298Neil Fuller   * Generates strings that can be used with {@link #fromStringForm(String)} to generate
51ac1c676b1256aaa85c5cc22494ea56bd2d276b9fNeil Fuller   * {@link BodyDeclarationLocator} instances capable of locating the supplied node. Usually returns
5293cf604e9dd0525f15bc0a7450b2a35f3884c298Neil Fuller   * a single element, except for fields declarations.
5304b228542fa91e91551c6ea4e6a2bac1d55ca1afNeil Fuller   */
54ac1c676b1256aaa85c5cc22494ea56bd2d276b9fNeil Fuller  public static List<String> toLocatorStringForms(BodyDeclaration bodyDeclaration) {
55ac1c676b1256aaa85c5cc22494ea56bd2d276b9fNeil Fuller    List<BodyDeclarationLocator> locators = createLocators(bodyDeclaration);
56ac1c676b1256aaa85c5cc22494ea56bd2d276b9fNeil Fuller    List<String> stringForms = new ArrayList<>(locators.size());
57ac1c676b1256aaa85c5cc22494ea56bd2d276b9fNeil Fuller    for (BodyDeclarationLocator locator : locators) {
58ac1c676b1256aaa85c5cc22494ea56bd2d276b9fNeil Fuller      stringForms.add(locator.getStringFormType() + ":" + locator.getStringFormTarget());
5993cf604e9dd0525f15bc0a7450b2a35f3884c298Neil Fuller    }
6093cf604e9dd0525f15bc0a7450b2a35f3884c298Neil Fuller    return stringForms;
6193cf604e9dd0525f15bc0a7450b2a35f3884c298Neil Fuller  }
6293cf604e9dd0525f15bc0a7450b2a35f3884c298Neil Fuller
6393cf604e9dd0525f15bc0a7450b2a35f3884c298Neil Fuller  /**
64ac1c676b1256aaa85c5cc22494ea56bd2d276b9fNeil Fuller   * Creates {@link BodyDeclarationLocator} objects that can find the supplied
6593cf604e9dd0525f15bc0a7450b2a35f3884c298Neil Fuller   * {@link BodyDeclaration}. Usually returns a single element, except for fields declarations.
6693cf604e9dd0525f15bc0a7450b2a35f3884c298Neil Fuller   */
67ac1c676b1256aaa85c5cc22494ea56bd2d276b9fNeil Fuller  public static List<BodyDeclarationLocator> createLocators(BodyDeclaration bodyDeclaration) {
68ac1c676b1256aaa85c5cc22494ea56bd2d276b9fNeil Fuller    AbstractTypeDeclaration typeDeclaration = TypeLocator.findTypeDeclarationNode(bodyDeclaration);
6993cf604e9dd0525f15bc0a7450b2a35f3884c298Neil Fuller    if (typeDeclaration == null) {
7093cf604e9dd0525f15bc0a7450b2a35f3884c298Neil Fuller      throw new AssertionError("Unable to find type declaration for " + typeDeclaration);
7193cf604e9dd0525f15bc0a7450b2a35f3884c298Neil Fuller    }
72ac1c676b1256aaa85c5cc22494ea56bd2d276b9fNeil Fuller    TypeLocator typeLocator = new TypeLocator(typeDeclaration);
7393cf604e9dd0525f15bc0a7450b2a35f3884c298Neil Fuller
7493cf604e9dd0525f15bc0a7450b2a35f3884c298Neil Fuller    int nodeType = bodyDeclaration.getNodeType();
7593cf604e9dd0525f15bc0a7450b2a35f3884c298Neil Fuller    switch (nodeType) {
7693cf604e9dd0525f15bc0a7450b2a35f3884c298Neil Fuller      case ASTNode.FIELD_DECLARATION:
77ac1c676b1256aaa85c5cc22494ea56bd2d276b9fNeil Fuller        List<String> fieldNames = FieldLocator.getFieldNames((FieldDeclaration) bodyDeclaration);
78ac1c676b1256aaa85c5cc22494ea56bd2d276b9fNeil Fuller        List<BodyDeclarationLocator> fieldLocators = new ArrayList<>(fieldNames.size());
7993cf604e9dd0525f15bc0a7450b2a35f3884c298Neil Fuller        for (String fieldName : fieldNames) {
80ac1c676b1256aaa85c5cc22494ea56bd2d276b9fNeil Fuller          fieldLocators.add(new FieldLocator(typeLocator, fieldName));
8193cf604e9dd0525f15bc0a7450b2a35f3884c298Neil Fuller        }
82ac1c676b1256aaa85c5cc22494ea56bd2d276b9fNeil Fuller        return fieldLocators;
8393cf604e9dd0525f15bc0a7450b2a35f3884c298Neil Fuller      case ASTNode.METHOD_DECLARATION:
8493cf604e9dd0525f15bc0a7450b2a35f3884c298Neil Fuller        MethodDeclaration methodDeclaration = (MethodDeclaration) bodyDeclaration;
8593cf604e9dd0525f15bc0a7450b2a35f3884c298Neil Fuller        List<String> parameterTypeNames = ParameterMatcher.getParameterTypeNames(methodDeclaration);
86ac1c676b1256aaa85c5cc22494ea56bd2d276b9fNeil Fuller        return ImmutableList.<BodyDeclarationLocator>of(
87ac1c676b1256aaa85c5cc22494ea56bd2d276b9fNeil Fuller            new MethodLocator(typeLocator, methodDeclaration.getName().getIdentifier(),
8893cf604e9dd0525f15bc0a7450b2a35f3884c298Neil Fuller            parameterTypeNames));
8993cf604e9dd0525f15bc0a7450b2a35f3884c298Neil Fuller      case ASTNode.TYPE_DECLARATION:
9093cf604e9dd0525f15bc0a7450b2a35f3884c298Neil Fuller      case ASTNode.ENUM_DECLARATION:
91ac1c676b1256aaa85c5cc22494ea56bd2d276b9fNeil Fuller        return ImmutableList.<BodyDeclarationLocator>of(typeLocator);
9293cf604e9dd0525f15bc0a7450b2a35f3884c298Neil Fuller      case ASTNode.ENUM_CONSTANT_DECLARATION:
9393cf604e9dd0525f15bc0a7450b2a35f3884c298Neil Fuller        EnumConstantDeclaration enumConstantDeclaration = (EnumConstantDeclaration) bodyDeclaration;
9493cf604e9dd0525f15bc0a7450b2a35f3884c298Neil Fuller        String constantName = enumConstantDeclaration.getName().getIdentifier();
95ac1c676b1256aaa85c5cc22494ea56bd2d276b9fNeil Fuller        return ImmutableList.<BodyDeclarationLocator>of(
96ac1c676b1256aaa85c5cc22494ea56bd2d276b9fNeil Fuller            new EnumConstantLocator(typeLocator, constantName));
9793cf604e9dd0525f15bc0a7450b2a35f3884c298Neil Fuller      default:
9893cf604e9dd0525f15bc0a7450b2a35f3884c298Neil Fuller        throw new IllegalArgumentException("Unsupported node type: " + nodeType);
9993cf604e9dd0525f15bc0a7450b2a35f3884c298Neil Fuller    }
10004b228542fa91e91551c6ea4e6a2bac1d55ca1afNeil Fuller  }
10104b228542fa91e91551c6ea4e6a2bac1d55ca1afNeil Fuller
10204b228542fa91e91551c6ea4e6a2bac1d55ca1afNeil Fuller  /**
103ac1c676b1256aaa85c5cc22494ea56bd2d276b9fNeil Fuller   * Creates a {@link BodyDeclarationLocator} from a string form of a body declaration.
104ac1c676b1256aaa85c5cc22494ea56bd2d276b9fNeil Fuller   * See {@link #toLocatorStringForms(BodyDeclaration)}.
10504b228542fa91e91551c6ea4e6a2bac1d55ca1afNeil Fuller   */
106ac1c676b1256aaa85c5cc22494ea56bd2d276b9fNeil Fuller  public static BodyDeclarationLocator fromStringForm(String stringForm) {
10704b228542fa91e91551c6ea4e6a2bac1d55ca1afNeil Fuller    List<String> components = splitInTwo(stringForm, ":");
108ac1c676b1256aaa85c5cc22494ea56bd2d276b9fNeil Fuller    String locatorTypeName = components.get(0);
109ac1c676b1256aaa85c5cc22494ea56bd2d276b9fNeil Fuller    String locatorString = components.get(1);
110ac1c676b1256aaa85c5cc22494ea56bd2d276b9fNeil Fuller    switch (locatorTypeName) {
111ac1c676b1256aaa85c5cc22494ea56bd2d276b9fNeil Fuller      case FieldLocator.LOCATOR_TYPE_NAME:
112ac1c676b1256aaa85c5cc22494ea56bd2d276b9fNeil Fuller        List<String> typeAndField = splitInTwo(locatorString, "#");
113ac1c676b1256aaa85c5cc22494ea56bd2d276b9fNeil Fuller        return new FieldLocator(new TypeLocator(typeAndField.get(0)), typeAndField.get(1));
114ac1c676b1256aaa85c5cc22494ea56bd2d276b9fNeil Fuller      case MethodLocator.LOCATOR_TYPE_NAME:
115ac1c676b1256aaa85c5cc22494ea56bd2d276b9fNeil Fuller        List<String> typeAndMethod = splitInTwo(locatorString, "#");
11604b228542fa91e91551c6ea4e6a2bac1d55ca1afNeil Fuller        String methodNameAndParameters = typeAndMethod.get(1);
11704b228542fa91e91551c6ea4e6a2bac1d55ca1afNeil Fuller        int parameterStartIndex = methodNameAndParameters.indexOf('(');
11804b228542fa91e91551c6ea4e6a2bac1d55ca1afNeil Fuller        if (parameterStartIndex == -1) {
11904b228542fa91e91551c6ea4e6a2bac1d55ca1afNeil Fuller          throw new IllegalArgumentException("No '(' found in " + methodNameAndParameters);
12004b228542fa91e91551c6ea4e6a2bac1d55ca1afNeil Fuller        }
12104b228542fa91e91551c6ea4e6a2bac1d55ca1afNeil Fuller        String methodName = methodNameAndParameters.substring(0, parameterStartIndex);
12204b228542fa91e91551c6ea4e6a2bac1d55ca1afNeil Fuller        String parametersString = methodNameAndParameters.substring(parameterStartIndex);
12304b228542fa91e91551c6ea4e6a2bac1d55ca1afNeil Fuller        List<String> parameterTypes = extractParameterTypes(parametersString);
124ac1c676b1256aaa85c5cc22494ea56bd2d276b9fNeil Fuller        return new MethodLocator(new TypeLocator(typeAndMethod.get(0)), methodName, parameterTypes);
125ac1c676b1256aaa85c5cc22494ea56bd2d276b9fNeil Fuller      case TypeLocator.LOCATOR_TYPE_NAME:
126ac1c676b1256aaa85c5cc22494ea56bd2d276b9fNeil Fuller        return new TypeLocator(locatorString);
127ac1c676b1256aaa85c5cc22494ea56bd2d276b9fNeil Fuller      case EnumConstantLocator.LOCATOR_TYPE_NAME:
128ac1c676b1256aaa85c5cc22494ea56bd2d276b9fNeil Fuller        List<String> typeAndConstant = splitInTwo(locatorString, "#");
129ac1c676b1256aaa85c5cc22494ea56bd2d276b9fNeil Fuller        return new EnumConstantLocator(
130ac1c676b1256aaa85c5cc22494ea56bd2d276b9fNeil Fuller            new TypeLocator(typeAndConstant.get(0)), typeAndConstant.get(1));
13104b228542fa91e91551c6ea4e6a2bac1d55ca1afNeil Fuller      default:
132ac1c676b1256aaa85c5cc22494ea56bd2d276b9fNeil Fuller        throw new IllegalArgumentException("Unsupported locator type: " + locatorTypeName);
13304b228542fa91e91551c6ea4e6a2bac1d55ca1afNeil Fuller    }
13404b228542fa91e91551c6ea4e6a2bac1d55ca1afNeil Fuller  }
13504b228542fa91e91551c6ea4e6a2bac1d55ca1afNeil Fuller
136ac1c676b1256aaa85c5cc22494ea56bd2d276b9fNeil Fuller  public static boolean matchesAny(List<BodyDeclarationLocator> locators, BodyDeclaration node) {
137ac1c676b1256aaa85c5cc22494ea56bd2d276b9fNeil Fuller    for (BodyDeclarationLocator locator : locators) {
138ac1c676b1256aaa85c5cc22494ea56bd2d276b9fNeil Fuller      if (locator.matches(node)) {
139cc49f813b0d7bf6664102b30b5513fd21c362e0dNeil Fuller        return true;
140cc49f813b0d7bf6664102b30b5513fd21c362e0dNeil Fuller      }
141cc49f813b0d7bf6664102b30b5513fd21c362e0dNeil Fuller    }
142cc49f813b0d7bf6664102b30b5513fd21c362e0dNeil Fuller    return false;
143cc49f813b0d7bf6664102b30b5513fd21c362e0dNeil Fuller  }
144cc49f813b0d7bf6664102b30b5513fd21c362e0dNeil Fuller
145cc49f813b0d7bf6664102b30b5513fd21c362e0dNeil Fuller  /**
146cc49f813b0d7bf6664102b30b5513fd21c362e0dNeil Fuller   * Finds the declaration associated with a given node. If the node is not a child of a declaration
147cc49f813b0d7bf6664102b30b5513fd21c362e0dNeil Fuller   * {@code null} is returned.
148cc49f813b0d7bf6664102b30b5513fd21c362e0dNeil Fuller   */
149cc49f813b0d7bf6664102b30b5513fd21c362e0dNeil Fuller  public static BodyDeclaration findDeclarationNode(ASTNode node) {
150cc49f813b0d7bf6664102b30b5513fd21c362e0dNeil Fuller    ASTNode ancestor = node;
151cc49f813b0d7bf6664102b30b5513fd21c362e0dNeil Fuller    while (ancestor != null && !(ancestor instanceof BodyDeclaration)) {
152cc49f813b0d7bf6664102b30b5513fd21c362e0dNeil Fuller      ancestor = ancestor.getParent();
153cc49f813b0d7bf6664102b30b5513fd21c362e0dNeil Fuller    }
154cc49f813b0d7bf6664102b30b5513fd21c362e0dNeil Fuller
155cc49f813b0d7bf6664102b30b5513fd21c362e0dNeil Fuller    return ancestor instanceof BodyDeclaration ? (BodyDeclaration) ancestor : null;
156cc49f813b0d7bf6664102b30b5513fd21c362e0dNeil Fuller  }
157cc49f813b0d7bf6664102b30b5513fd21c362e0dNeil Fuller
15804b228542fa91e91551c6ea4e6a2bac1d55ca1afNeil Fuller  private static List<String> extractParameterTypes(String parametersString) {
15904b228542fa91e91551c6ea4e6a2bac1d55ca1afNeil Fuller    if (!(parametersString.startsWith("(") && parametersString.endsWith(")"))) {
16004b228542fa91e91551c6ea4e6a2bac1d55ca1afNeil Fuller      throw new IllegalArgumentException("Expected \"(<types>)\" but was " + parametersString);
16104b228542fa91e91551c6ea4e6a2bac1d55ca1afNeil Fuller    }
16204b228542fa91e91551c6ea4e6a2bac1d55ca1afNeil Fuller    parametersString = parametersString.substring(1, parametersString.length() - 1);
16304b228542fa91e91551c6ea4e6a2bac1d55ca1afNeil Fuller    if (parametersString.isEmpty()) {
16404b228542fa91e91551c6ea4e6a2bac1d55ca1afNeil Fuller      return Collections.emptyList();
16504b228542fa91e91551c6ea4e6a2bac1d55ca1afNeil Fuller    }
16604b228542fa91e91551c6ea4e6a2bac1d55ca1afNeil Fuller    return Splitter.on(',').splitToList(parametersString);
16704b228542fa91e91551c6ea4e6a2bac1d55ca1afNeil Fuller  }
16804b228542fa91e91551c6ea4e6a2bac1d55ca1afNeil Fuller
16904b228542fa91e91551c6ea4e6a2bac1d55ca1afNeil Fuller  private static List<String> splitInTwo(String string, String separator) {
17004b228542fa91e91551c6ea4e6a2bac1d55ca1afNeil Fuller    List<String> components = Splitter.on(separator).splitToList(string);
17104b228542fa91e91551c6ea4e6a2bac1d55ca1afNeil Fuller    if (components.size() != 2) {
17204b228542fa91e91551c6ea4e6a2bac1d55ca1afNeil Fuller      throw new IllegalArgumentException("Cannot split " + string + " on " + separator);
17304b228542fa91e91551c6ea4e6a2bac1d55ca1afNeil Fuller    }
17404b228542fa91e91551c6ea4e6a2bac1d55ca1afNeil Fuller    return components;
17504b228542fa91e91551c6ea4e6a2bac1d55ca1afNeil Fuller  }
17604b228542fa91e91551c6ea4e6a2bac1d55ca1afNeil Fuller}
177