blob: b20f325f1986b14508962d08c3738838cd36f2b6 [file] [log] [blame]
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.currysrc.api.transform.ast;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.google.common.collect.BiMap;
import com.google.common.collect.ImmutableBiMap;
import com.google.common.collect.Lists;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
import org.eclipse.jdt.core.dom.BodyDeclaration;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.EnumConstantDeclaration;
import org.eclipse.jdt.core.dom.EnumDeclaration;
import org.eclipse.jdt.core.dom.FieldDeclaration;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
import org.eclipse.jdt.core.dom.Type;
import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
import java.util.Collections;
import java.util.List;
/**
* Utility methods associated with {@link BodyDeclarationLocater} and its standard implementations.
*/
public final class BodyDeclarationLocaters {
private final static BiMap<Integer, String> NODE_TYPE_TO_STRING_NAME =
ImmutableBiMap.<Integer, String>builder()
.put(ASTNode.FIELD_DECLARATION, "field")
.put(ASTNode.METHOD_DECLARATION, "method")
.put(ASTNode.TYPE_DECLARATION, "class")
.put(ASTNode.ENUM_DECLARATION, "enum")
.put(ASTNode.ENUM_CONSTANT_DECLARATION, "enumConstant")
.build();
private BodyDeclarationLocaters() {
}
/**
* Generates a string that can be used with {@link #fromStringForm(String)} to generate a
* {@link BodyDeclarationLocater} capable of locating the supplied node.
*/
public static String toLocaterStringForm(BodyDeclaration bodyDeclaration) {
return getDeclarationTypeString(bodyDeclaration) + ":" + createName(bodyDeclaration);
}
/**
* Creates a {@link BodyDeclarationLocater} from a string form of a body declaration.
* See {@link #toLocaterStringForm(BodyDeclaration)}.
*/
public static BodyDeclarationLocater fromStringForm(String stringForm) {
List<String> components = splitInTwo(stringForm, ":");
int nodeType = getDeclarationNodeType(components.get(0));
String locaterString = components.get(1);
switch (nodeType) {
case ASTNode.FIELD_DECLARATION:
List<String> typeAndField = splitInTwo(locaterString, "#");
return new FieldLocater(new TypeLocater(typeAndField.get(0)), typeAndField.get(1));
case ASTNode.METHOD_DECLARATION:
List<String> typeAndMethod = splitInTwo(locaterString, "#");
String methodNameAndParameters = typeAndMethod.get(1);
int parameterStartIndex = methodNameAndParameters.indexOf('(');
if (parameterStartIndex == -1) {
throw new IllegalArgumentException("No '(' found in " + methodNameAndParameters);
}
String methodName = methodNameAndParameters.substring(0, parameterStartIndex);
String parametersString = methodNameAndParameters.substring(parameterStartIndex);
List<String> parameterTypes = extractParameterTypes(parametersString);
return new MethodLocater(new TypeLocater(typeAndMethod.get(0)), methodName, parameterTypes);
case ASTNode.TYPE_DECLARATION:
case ASTNode.ENUM_DECLARATION:
return new TypeLocater(locaterString);
case ASTNode.ENUM_CONSTANT_DECLARATION:
List<String> typeAndConstant = splitInTwo(locaterString, "#");
return new EnumConstantLocater(
new TypeLocater(typeAndConstant.get(0)), typeAndConstant.get(1));
default:
throw new IllegalArgumentException("Unsupported node type: " + nodeType);
}
}
private static String getDeclarationTypeString(BodyDeclaration bodyDeclaration) {
String typeString = NODE_TYPE_TO_STRING_NAME.get(bodyDeclaration.getNodeType());
if (typeString == null) {
throw new IllegalArgumentException("Unsupported node type: " + bodyDeclaration);
}
return typeString;
}
private static int getDeclarationNodeType(String bodyDeclarationTypeString) {
Integer nodeType = NODE_TYPE_TO_STRING_NAME.inverse().get(bodyDeclarationTypeString);
if (nodeType == null) {
throw new IllegalArgumentException(
"Unsupported node type string: " + bodyDeclarationTypeString);
}
return nodeType;
}
private static String createName(BodyDeclaration bodyDeclaration) {
if (bodyDeclaration instanceof AbstractTypeDeclaration) {
AbstractTypeDeclaration abstractTypeDeclaration = (AbstractTypeDeclaration) bodyDeclaration;
if (abstractTypeDeclaration.isPackageMemberTypeDeclaration()) {
// Top-level class
CompilationUnit cu = (CompilationUnit) abstractTypeDeclaration.getParent();
return cu.getPackage().getName().getFullyQualifiedName() + "." +
abstractTypeDeclaration.getName().getFullyQualifiedName();
}
}
switch (bodyDeclaration.getNodeType()) {
case ASTNode.FIELD_DECLARATION: {
// Ignores the possibility of multiple fields covered by a single javadoc.
FieldDeclaration fieldDeclaration = (FieldDeclaration) bodyDeclaration;
List<VariableDeclarationFragment> fieldDeclarations =
(List<VariableDeclarationFragment>) fieldDeclaration.fragments();
if (fieldDeclarations.size() > 1) {
throw new AssertionError("No field name found: multiple found");
}
AbstractTypeDeclaration parentType =
(AbstractTypeDeclaration) fieldDeclaration.getParent();
return createName(parentType) + "#" + fieldDeclarations.get(0).getName();
}
case ASTNode.METHOD_DECLARATION: {
MethodDeclaration methodDeclaration = (MethodDeclaration) bodyDeclaration;
AbstractTypeDeclaration parentType =
(AbstractTypeDeclaration) methodDeclaration.getParent();
return createName(parentType) + "#" + methodDeclaration.getName()
+ createParameterTypeListString(methodDeclaration.parameters());
}
case ASTNode.TYPE_DECLARATION:
case ASTNode.ENUM_DECLARATION: {
AbstractTypeDeclaration nestedTypeDeclaration = (AbstractTypeDeclaration) bodyDeclaration;
AbstractTypeDeclaration parentType =
(AbstractTypeDeclaration) nestedTypeDeclaration.getParent();
return createName(parentType) + "$" + nestedTypeDeclaration.getName();
}
case ASTNode.ENUM_CONSTANT_DECLARATION: {
EnumConstantDeclaration enumConstantDeclaration =
(EnumConstantDeclaration) bodyDeclaration;
EnumDeclaration parentType = (EnumDeclaration) enumConstantDeclaration.getParent();
return createName(parentType) + "#" + enumConstantDeclaration.getName();
}
default:
throw new AssertionError("Unsupported node type: " + bodyDeclaration);
}
}
private static String createParameterTypeListString(List<SingleVariableDeclaration> parameters) {
return "(" + Joiner.on(",").join(createParameterTypeList(parameters)) + ")";
}
private static List<String> createParameterTypeList(List<SingleVariableDeclaration> parameters) {
List<String> types = Lists.newArrayList();
for (SingleVariableDeclaration singleVariableDeclaration : parameters) {
Type type = singleVariableDeclaration.getType();
// toString() does the right thing in all cases.
types.add(type.toString());
}
return types;
}
private static List<String> extractParameterTypes(String parametersString) {
if (!(parametersString.startsWith("(") && parametersString.endsWith(")"))) {
throw new IllegalArgumentException("Expected \"(<types>)\" but was " + parametersString);
}
parametersString = parametersString.substring(1, parametersString.length() - 1);
if (parametersString.isEmpty()) {
return Collections.emptyList();
}
return Splitter.on(',').splitToList(parametersString);
}
private static List<String> splitInTwo(String string, String separator) {
List<String> components = Splitter.on(separator).splitToList(string);
if (components.size() != 2) {
throw new IllegalArgumentException("Cannot split " + string + " on " + separator);
}
return components;
}
}