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 */
16package com.google.currysrc.api.process.ast;
17
18import com.google.common.base.Splitter;
19import com.google.common.collect.ImmutableList;
20
21import org.eclipse.jdt.core.dom.ASTNode;
22import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
23import org.eclipse.jdt.core.dom.BodyDeclaration;
24import org.eclipse.jdt.core.dom.EnumConstantDeclaration;
25import org.eclipse.jdt.core.dom.FieldDeclaration;
26import org.eclipse.jdt.core.dom.MethodDeclaration;
27
28import java.util.ArrayList;
29import java.util.Collections;
30import java.util.List;
31
32/**
33 * Utility methods associated with {@link BodyDeclarationLocator} and its standard implementations.
34 */
35public 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