blob: db82c13bfcc90b8e7444f119bf3a578763170d1d [file] [log] [blame]
/*
* Copyright 2014-present Facebook, Inc.
*
* 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.facebook.buck.java;
import com.google.common.base.Joiner;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Lists;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.ASTParser;
import org.eclipse.jdt.core.dom.ASTVisitor;
import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
import org.eclipse.jdt.core.dom.AnnotationTypeDeclaration;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.EnumDeclaration;
import org.eclipse.jdt.core.dom.PackageDeclaration;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.jdt.core.dom.TypeDeclarationStatement;
import java.io.IOException;
import java.util.LinkedList;
import java.util.Map;
/**
* Extracts the set of exported symbols (class and enum names) from a Java code file, using the
* ASTParser from Eclipse.
*/
public class JavaFileParser {
private final int jlsLevel;
private final String javaVersion;
private static final ImmutableMap<String, String> javaVersionMap =
ImmutableMap.<String, String>builder()
.put("1", JavaCore.VERSION_1_1)
.put("2", JavaCore.VERSION_1_2)
.put("3", JavaCore.VERSION_1_3)
.put("4", JavaCore.VERSION_1_4)
.put("5", JavaCore.VERSION_1_5)
.put("6", JavaCore.VERSION_1_6)
.put("7", JavaCore.VERSION_1_7)
.build();
private JavaFileParser(int jlsLevel, String javaVersion) {
this.jlsLevel = jlsLevel;
this.javaVersion = javaVersion;
}
public static JavaFileParser createJavaFileParser(JavacOptions options) {
String javaVersion = Preconditions.checkNotNull(javaVersionMap.get(options.getSourceLevel()));
return new JavaFileParser(AST.JLS4, javaVersion);
}
public ImmutableSortedSet<String> getExportedSymbolsFromString(String code) throws IOException {
ASTParser parser = ASTParser.newParser(jlsLevel);
parser.setSource(code.toCharArray());
parser.setKind(ASTParser.K_COMPILATION_UNIT);
@SuppressWarnings("unchecked")
Map<String, String> options = JavaCore.getOptions();
JavaCore.setComplianceOptions(javaVersion, options);
parser.setCompilerOptions(options);
final CompilationUnit compilationUnit = (CompilationUnit) parser.createAST(/* monitor */ null);
final ImmutableSortedSet.Builder<String> symbolsBuilder = ImmutableSortedSet.naturalOrder();
compilationUnit.accept(new ASTVisitor() {
@Override
public boolean visit(TypeDeclaration node) {
// Local classes can be declared inside of methods. Skip over these.
if (node.getParent() instanceof TypeDeclarationStatement) {
return true;
}
symbolsBuilder.add(getFullyQualifiedTypeName(node));
return true;
}
@Override
public boolean visit(EnumDeclaration node) {
symbolsBuilder.add(getFullyQualifiedTypeName(node));
return true;
}
@Override
public boolean visit(AnnotationTypeDeclaration node) {
symbolsBuilder.add(getFullyQualifiedTypeName(node));
return true;
}
});
return symbolsBuilder.build();
}
private String getFullyQualifiedTypeName(AbstractTypeDeclaration node) {
LinkedList<String> nameParts = Lists.newLinkedList();
nameParts.add(node.getName().toString());
ASTNode parent = node.getParent();
while (!(parent instanceof CompilationUnit)) {
nameParts.addFirst(((AbstractTypeDeclaration) parent).getName().toString());
parent = parent.getParent();
}
// A Java file might not have a package. Hopefully all of ours do though...
PackageDeclaration packageDecl = ((CompilationUnit) parent).getPackage();
if (packageDecl != null) {
nameParts.addFirst(packageDecl.getName().toString());
}
return Joiner.on(".").join(nameParts);
}
}