/*
* 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.jvm.java;
import static com.facebook.buck.jvm.java.JavaCompilationConstants.DEFAULT_JAVAC_OPTIONS;
import static org.junit.Assert.assertEquals;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableSortedSet;
import java.io.IOException;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
public class JavaFileParserTest {
@Rule public ExpectedException thrown = ExpectedException.none();
private static final String JAVA_CODE_WITH_MANY_CLASSES =
Joiner.on('\n')
.join(
"package com.example;",
"",
"public class Example {",
" public static int variablesNotCaptured, maybeLater;",
"",
" private Example() {}",
"",
" public static void functionsNotCapturedEither() {",
" }",
"",
" public enum InnerEnum {",
" foo;",
"",
" public class InnerClass {",
" }",
" }",
"",
" interface InnerInterface {",
" }",
"}",
"",
"class AnotherOuterClass {",
"}");
@Test
public void testJavaFileParsing() throws IOException {
JavaFileParser parser = JavaFileParser.createJavaFileParser(DEFAULT_JAVAC_OPTIONS);
ImmutableSortedSet<String> symbols =
parser.getExportedSymbolsFromString(JAVA_CODE_WITH_MANY_CLASSES);
assertEquals(
"getExportedSymbolsFromString should extract both top-level and inner symbols as provided",
ImmutableSortedSet.of(
"com.example.AnotherOuterClass",
"com.example.Example",
"com.example.Example.InnerEnum",
"com.example.Example.InnerEnum.InnerClass",
"com.example.Example.InnerInterface"),
symbols);
}
private static final String JAVA_CODE_WITH_LOCAL_CLASS_IN_ANONYMOUS_CLASS =
Joiner.on('\n')
.join(
"package com.example;",
"public class NonlocalClass {",
" @Override",
" Iterator<Entry<K, V>> entryIterator() {",
" return new Itr<Entry<K, V>>() {",
" @Override",
" Entry<K, V> output(BiEntry<K, V> entry) {",
" return new MapEntry(entry);",
" }",
"",
" class MapEntry {}",
" };",
" }",
"}");
@Test
public void testJavaFileParsingWithLocalClassInAnonymousClass() throws IOException {
JavaFileParser parser = JavaFileParser.createJavaFileParser(DEFAULT_JAVAC_OPTIONS);
ImmutableSortedSet<String> symbols =
parser.getExportedSymbolsFromString(JAVA_CODE_WITH_LOCAL_CLASS_IN_ANONYMOUS_CLASS);
assertEquals(
"getExportedSymbolsFromString should not consider non-local classes to be provided",
ImmutableSortedSet.of("com.example.NonlocalClass"),
symbols);
}
private static final String JAVA_CODE_WITH_LOCAL_CLASS =
Joiner.on('\n')
.join(
"package com.example;",
"public class NonlocalClass {",
" public static void exampleMethod() {",
" class LocalClass {",
" }",
" }",
"}");
@Test
public void testJavaFileParsingWithLocalClass() throws IOException {
JavaFileParser parser = JavaFileParser.createJavaFileParser(DEFAULT_JAVAC_OPTIONS);
ImmutableSortedSet<String> symbols =
parser.getExportedSymbolsFromString(JAVA_CODE_WITH_LOCAL_CLASS);
assertEquals(
"getExportedSymbolsFromString should not consider non-local classes to be provided",
ImmutableSortedSet.of("com.example.NonlocalClass"),
symbols);
}
private static final String JAVA_CODE_WITH_NO_PACKAGE = "public class NoPackageExample { }";
@Test
public void testJavaFileParsingWithNoPackage() throws IOException {
JavaFileParser parser = JavaFileParser.createJavaFileParser(DEFAULT_JAVAC_OPTIONS);
ImmutableSortedSet<String> symbols =
parser.getExportedSymbolsFromString(JAVA_CODE_WITH_NO_PACKAGE);
assertEquals(
"getExportedSymbolsFromString should be able to extract package-less classes as provided",
ImmutableSortedSet.of("NoPackageExample"),
symbols);
}
private static final String JAVA_CODE_WITH_ANNOTATION_TYPE =
"public @interface ExampleAnnotationType { }";
@Test
public void testJavaFileParsingWithAnnotationType() throws IOException {
JavaFileParser parser = JavaFileParser.createJavaFileParser(DEFAULT_JAVAC_OPTIONS);
ImmutableSortedSet<String> symbols =
parser.getExportedSymbolsFromString(JAVA_CODE_WITH_ANNOTATION_TYPE);
assertEquals(
"getExportedSymbolsFromString should be able to extract symbols with annotations",
ImmutableSortedSet.of("ExampleAnnotationType"),
symbols);
}
private static final String JAVA_CODE_WITH_IMPORTS =
Joiner.on('\n')
.join(
"package com.example;",
"",
"import java.util.Map;",
"",
"public class AnExample {",
"",
" public void doStuff(Map map) {",
"",
" }",
"}");
@Test
public void testExtractingRequiredSymbols() throws IOException {
JavaFileParser parser = JavaFileParser.createJavaFileParser(DEFAULT_JAVAC_OPTIONS);
JavaFileParser.JavaFileFeatures features =
parser.extractFeaturesFromJavaCode(JAVA_CODE_WITH_IMPORTS);
assertEquals(
"extractFeaturesFromJavaCode should be able to find a top-level class",
ImmutableSortedSet.of("com.example.AnExample"),
features.providedSymbols);
assertEquals(ImmutableSortedSet.of(), features.requiredSymbols);
assertEquals(
"extractFeaturesFromJavaCode should be able to find an ordinary import",
ImmutableSortedSet.of("java.util.Map"),
features.exportedSymbols);
}
private static final String JAVA_CODE_WITH_IMPORTS_THAT_DO_NOT_FOLLOW_THE_NAMING_CONVENTIONS =
Joiner.on('\n')
.join(
"package com.example;",
"",
"import com.facebook.buck.nsOuter.nsInner;",
"",
"import org.mozilla.intl.chardet.nsDetector;",
"import org.mozilla.intl.chardet.nsICharsetDetectionObserver;",
"import org.mozilla.intl.chardet.nsPSMDetector;",
"",
"public class AnExample {}");
@Test
public void testExtractingRequiredSymbolsWithImportsThatDoNotFollowTheNamingConventions() {
JavaFileParser parser = JavaFileParser.createJavaFileParser(DEFAULT_JAVAC_OPTIONS);
JavaFileParser.JavaFileFeatures features =
parser.extractFeaturesFromJavaCode(
JAVA_CODE_WITH_IMPORTS_THAT_DO_NOT_FOLLOW_THE_NAMING_CONVENTIONS);
assertEquals(
ImmutableSortedSet.of(
"com.facebook.buck.nsOuter",
"org.mozilla.intl.chardet.nsDetector",
"org.mozilla.intl.chardet.nsICharsetDetectionObserver",
"org.mozilla.intl.chardet.nsPSMDetector"),
features.requiredSymbols);
assertEquals(ImmutableSortedSet.of(), features.exportedSymbols);
}
private static final String JAVA_CODE_WITH_IMPORTS_THAT_HAVE_NO_CAPITAL_LETTERS =
Joiner.on('\n')
.join(
"package com.example;",
"",
"import com.facebook.buck.badactor;",
"",
"public class AnExample {}");
/**
* Verifies that an import that completely violates the expectations around naming conventions
* will still be added. Although we can police this in our own code, we have no control over what
* third-party libraries do.
*/
@Test
public void testExtractingRequiredSymbolsWithImportsThatHaveNoCapitalLetters() {
JavaFileParser parser = JavaFileParser.createJavaFileParser(DEFAULT_JAVAC_OPTIONS);
JavaFileParser.JavaFileFeatures features =
parser.extractFeaturesFromJavaCode(JAVA_CODE_WITH_IMPORTS_THAT_HAVE_NO_CAPITAL_LETTERS);
assertEquals(ImmutableSortedSet.of("com.facebook.buck.badactor"), features.requiredSymbols);
assertEquals(ImmutableSortedSet.of("com.example.AnExample"), features.providedSymbols);
}
private static final String JAVA_CODE_WITH_FULLY_QUALIFIED_REFERENCES =
Joiner.on('\n')
.join(
"package com.example;",
"",
"import java.util.Map;",
"",
"public class AnExample {",
"",
" public int getMeaningOfLife() {",
" String meaningOfLife = ClassInThisPackage.MEANING_OF_LIFE;",
" return java.lang.Integer.valueOf(meaningOfLife, 10);",
" }",
"}");
@Test
public void testExtractingRequiredSymbolsWithFullyQualifiedReference() throws IOException {
JavaFileParser parser = JavaFileParser.createJavaFileParser(DEFAULT_JAVAC_OPTIONS);
JavaFileParser.JavaFileFeatures features =
parser.extractFeaturesFromJavaCode(JAVA_CODE_WITH_FULLY_QUALIFIED_REFERENCES);
assertEquals(
"extractFeaturesFromJavaCode should be able to find a top-level class",
ImmutableSortedSet.of("com.example.AnExample"),
features.providedSymbols);
assertEquals(
"extractFeaturesFromJavaCode should be able to make appropriate inferences about: "
+ "(1) unqualified references to types in the same package "
+ "(2) fully-qualified references to types",
ImmutableSortedSet.of(
"java.util.Map", "com.example.ClassInThisPackage", "java.lang.Integer"),
features.requiredSymbols);
assertEquals(ImmutableSortedSet.of(), features.exportedSymbols);
}
private static final String JAVA_CODE_WITH_STATIC_IMPORT =
Joiner.on('\n')
.join(
"package com.example;",
"",
"import static com.google.common.util.concurrent.MoreExecutors.listeningDecorator;",
"",
"public class AnExample {",
" public void executor() { listeningDecorator(null); }",
"}");
@Test
public void testExtractingRequiredSymbolsWithStaticImport() throws IOException {
JavaFileParser parser = JavaFileParser.createJavaFileParser(DEFAULT_JAVAC_OPTIONS);
JavaFileParser.JavaFileFeatures features =
parser.extractFeaturesFromJavaCode(JAVA_CODE_WITH_STATIC_IMPORT);
assertEquals(
ImmutableSortedSet.of("com.google.common.util.concurrent.MoreExecutors"),
features.requiredSymbols);
assertEquals(ImmutableSortedSet.of(), features.exportedSymbols);
}
/**
* Note that using a wildcard import is frequently a "poison pill" when it comes to extracting
* required symbols via {@link JavaFileParser}; however, there is some hardcoded support for
* common wildcards, such as {@code import java.util.*}.
*/
private static final String JAVA_CODE_WITH_SUPPORTED_WILDCARD_IMPORT =
Joiner.on('\n')
.join(
"package com.example;",
"",
"import java.util.*;",
"",
"public class AnExample {",
" public Map newInstance() { return new HashMap(); }",
"}");
@Test
public void testExtractingRequiredSymbolsWithSupportedWildcardImport() throws IOException {
JavaFileParser parser = JavaFileParser.createJavaFileParser(DEFAULT_JAVAC_OPTIONS);
JavaFileParser.JavaFileFeatures features =
parser.extractFeaturesFromJavaCode(JAVA_CODE_WITH_SUPPORTED_WILDCARD_IMPORT);
assertEquals(ImmutableSortedSet.of("com.example.AnExample"), features.providedSymbols);
assertEquals(ImmutableSortedSet.of("java.util.HashMap"), features.requiredSymbols);
assertEquals(ImmutableSortedSet.of("java.util.Map"), features.exportedSymbols);
}
private static final String JAVA_CODE_WITH_UNSUPPORTED_WILDCARD_IMPORT =
Joiner.on('\n')
.join(
"package com.example;",
"",
"import com.example.things.Thinger;",
"import com.mystery.*;",
"",
"import java.util.*;",
"",
"public class AnExample extends FooBar {",
" public Map newInstance() { return new HashMap(); }",
" public SomeExample newThinger() { return Thinger.createSomeExample(); }",
"}");
@Test
public void testExtractingRequiredSymbolsWithUnsupportedWildcardImport() throws IOException {
JavaFileParser parser = JavaFileParser.createJavaFileParser(DEFAULT_JAVAC_OPTIONS);
JavaFileParser.JavaFileFeatures features =
parser.extractFeaturesFromJavaCode(JAVA_CODE_WITH_UNSUPPORTED_WILDCARD_IMPORT);
assertEquals(ImmutableSortedSet.of("com.example.AnExample"), features.providedSymbols);
assertEquals(
"Should be restricted to explicit imports because the wildcard import makes it "
+ "impossible for JavaFileParser to know things such as whether it is "
+ "com.example.FooBar or com.mystery.FooBar "
+ "(this is also true for Map and HashMap, as things are implemented today).",
ImmutableSortedSet.of("com.example.things.Thinger"),
features.requiredSymbols);
assertEquals(
"Currently contains some items, but ideally would be empty.",
ImmutableSortedSet.of("java.util.Map", "com.example.FooBar", "com.example.SomeExample"),
features.exportedSymbols);
}
private static final String JAVA_CODE_THROWS_FULLY_QUALIFIED_EXCEPTION =
Joiner.on('\n')
.join(
"package com.example;",
"",
"public class AnExample {",
" public void read() throws java.io.IOException {}",
"}");
@Test
public void testExtractingRequiredSymbolsWithFullyQualifiedThrows() throws IOException {
JavaFileParser parser = JavaFileParser.createJavaFileParser(DEFAULT_JAVAC_OPTIONS);
JavaFileParser.JavaFileFeatures features =
parser.extractFeaturesFromJavaCode(JAVA_CODE_THROWS_FULLY_QUALIFIED_EXCEPTION);
assertEquals(
"extractFeaturesFromJavaCode should be able to find a top-level class",
ImmutableSortedSet.of("com.example.AnExample"),
features.providedSymbols);
assertEquals(ImmutableSortedSet.of(), features.requiredSymbols);
assertEquals(ImmutableSortedSet.of("java.io.IOException"), features.exportedSymbols);
}
private static final String JAVA_CODE_INSTANTIATES_CLASS_IN_PACKAGE =
Joiner.on('\n')
.join(
"package com.example;",
"",
"public class AnExample {",
" public Widget create() { return new Widget(); }",
"}");
@Test
public void testExtractingRequiredSymbolsWithNewTypeInPackage() throws IOException {
JavaFileParser parser = JavaFileParser.createJavaFileParser(DEFAULT_JAVAC_OPTIONS);
JavaFileParser.JavaFileFeatures features =
parser.extractFeaturesFromJavaCode(JAVA_CODE_INSTANTIATES_CLASS_IN_PACKAGE);
assertEquals(
"extractFeaturesFromJavaCode should be able to find a top-level class",
ImmutableSortedSet.of("com.example.AnExample"),
features.providedSymbols);
assertEquals(ImmutableSortedSet.of(), features.requiredSymbols);
assertEquals(ImmutableSortedSet.of("com.example.Widget"), features.exportedSymbols);
}
private static final String JAVA_CODE_CREATES_IN_PACKAGE_TYPE_WITHIN_PACKAGE_TYPE =
Joiner.on('\n')
.join(
"package com.example;",
"",
"public class AnExample {",
" public Widget create() { return new Widget(new Woojet()); }",
"}");
@Test
public void testExtractingRequiredSymbolsRecursesIntoNewCall() throws IOException {
JavaFileParser parser = JavaFileParser.createJavaFileParser(DEFAULT_JAVAC_OPTIONS);
JavaFileParser.JavaFileFeatures features =
parser.extractFeaturesFromJavaCode(JAVA_CODE_CREATES_IN_PACKAGE_TYPE_WITHIN_PACKAGE_TYPE);
assertEquals(ImmutableSortedSet.of("com.example.Woojet"), features.requiredSymbols);
assertEquals(ImmutableSortedSet.of("com.example.Widget"), features.exportedSymbols);
}
private static final String JAVA_CODE_DOES_INSTANCEOF_CHECK_FOR_TYPE_WITHIN_PACKAGE =
Joiner.on('\n')
.join(
"package com.example;",
"",
"public class AnExample {",
" public boolean isWoojet(Object widget) { return widget instanceof Woojet; }",
"}");
@Test
public void testExtractingRequiredSymbolsWithInstanceofCheckInPackage() throws IOException {
JavaFileParser parser = JavaFileParser.createJavaFileParser(DEFAULT_JAVAC_OPTIONS);
JavaFileParser.JavaFileFeatures features =
parser.extractFeaturesFromJavaCode(JAVA_CODE_DOES_INSTANCEOF_CHECK_FOR_TYPE_WITHIN_PACKAGE);
assertEquals(ImmutableSortedSet.of("com.example.Woojet"), features.requiredSymbols);
assertEquals(ImmutableSortedSet.of(), features.exportedSymbols);
}
private static final String JAVA_CODE_DOES_CAST_FOR_TYPE_WITHIN_PACKAGE =
Joiner.on('\n')
.join(
"package com.example;",
"",
"public class AnExample {",
" public void castToWidget(Object widget) { Object obj = (Widget) widget; }",
"}");
@Test
public void testExtractingRequiredSymbolsWithCastToTypeInPackage() throws IOException {
JavaFileParser parser = JavaFileParser.createJavaFileParser(DEFAULT_JAVAC_OPTIONS);
JavaFileParser.JavaFileFeatures features =
parser.extractFeaturesFromJavaCode(JAVA_CODE_DOES_CAST_FOR_TYPE_WITHIN_PACKAGE);
assertEquals(ImmutableSortedSet.of("com.example.Widget"), features.requiredSymbols);
assertEquals(ImmutableSortedSet.of(), features.exportedSymbols);
}
private static final String JAVA_CODE_DOES_CAST_FOR_TYPE_WITHIN_PACKAGE_IN_METHOD_INVOCATION =
Joiner.on('\n')
.join(
"package com.example;",
"",
"public class AnExample {",
" public Object createWidget() { return create((Widget) null); }",
"}");
@Test
public void testExtractingRequiredSymbolsWithCastToTypeInPackageWithinMethodInvocation()
throws IOException {
JavaFileParser parser = JavaFileParser.createJavaFileParser(DEFAULT_JAVAC_OPTIONS);
JavaFileParser.JavaFileFeatures features =
parser.extractFeaturesFromJavaCode(
JAVA_CODE_DOES_CAST_FOR_TYPE_WITHIN_PACKAGE_IN_METHOD_INVOCATION);
assertEquals(ImmutableSortedSet.of("com.example.Widget"), features.requiredSymbols);
assertEquals(ImmutableSortedSet.of(), features.exportedSymbols);
}
private static final String JAVA_CODE_SPECIFIES_PARAM_FOR_TYPE_WITHIN_PACKAGE =
Joiner.on('\n')
.join(
"package com.example;",
"",
"public class AnExample {",
" public void printWidget(Widget widget) { System.out.println(widget); }",
"}");
@Test
public void testExtractingRequiredSymbolsWithParamInPackage() throws IOException {
JavaFileParser parser = JavaFileParser.createJavaFileParser(DEFAULT_JAVAC_OPTIONS);
JavaFileParser.JavaFileFeatures features =
parser.extractFeaturesFromJavaCode(JAVA_CODE_SPECIFIES_PARAM_FOR_TYPE_WITHIN_PACKAGE);
assertEquals(ImmutableSortedSet.of(), features.requiredSymbols);
assertEquals(ImmutableSortedSet.of("com.example.Widget"), features.exportedSymbols);
}
private static final String JAVA_CODE_SPECIFIES_STATIC_METHOD_IN_PACKAGE =
Joiner.on('\n')
.join(
"package com.example;",
"",
"public class AnExample {",
" public Object createWidget() { return Widget.newInstance(); }",
"}");
@Test
public void testExtractingRequiredSymbolsWithStaticMethodAccess() throws IOException {
JavaFileParser parser = JavaFileParser.createJavaFileParser(DEFAULT_JAVAC_OPTIONS);
JavaFileParser.JavaFileFeatures features =
parser.extractFeaturesFromJavaCode(JAVA_CODE_SPECIFIES_STATIC_METHOD_IN_PACKAGE);
assertEquals(ImmutableSortedSet.of("com.example.Widget"), features.requiredSymbols);
assertEquals(ImmutableSortedSet.of(), features.exportedSymbols);
}
private static final String JAVA_CODE_SPECIFIES_TYPE_IN_PACKAGE =
Joiner.on('\n')
.join(
"package com.example;",
"",
"public class AnExample {",
" public void createWidget() {",
" Widget widget = WidgetFactory.newInstance();",
" }",
"}");
@Test
public void testExtractingRequiredSymbolsWithTypeOnlyReferencedAsLocalVariable()
throws IOException {
JavaFileParser parser = JavaFileParser.createJavaFileParser(DEFAULT_JAVAC_OPTIONS);
JavaFileParser.JavaFileFeatures features =
parser.extractFeaturesFromJavaCode(JAVA_CODE_SPECIFIES_TYPE_IN_PACKAGE);
assertEquals(
ImmutableSortedSet.of("com.example.Widget", "com.example.WidgetFactory"),
features.requiredSymbols);
assertEquals(ImmutableSortedSet.of(), features.exportedSymbols);
}
private static final String PROPERTY_LOOKUP_EXPRESSION =
Joiner.on('\n')
.join(
"package com.example;",
"",
"import java.util.*;",
"",
"public class AnExample {",
" public static void main(String[] args) {",
" int numArgs = args.length;",
" List<String> asArray = new ArrayList(numArgs);",
" }",
"}");
@Test
public void testExtractingRequiredSymbolsWithPropertyLookupExpression() throws IOException {
JavaFileParser parser = JavaFileParser.createJavaFileParser(DEFAULT_JAVAC_OPTIONS);
JavaFileParser.JavaFileFeatures features =
parser.extractFeaturesFromJavaCode(PROPERTY_LOOKUP_EXPRESSION);
assertEquals(
"args.length should not appear in features.requiredSymbols.",
ImmutableSortedSet.of("java.util.ArrayList", "java.util.List"),
features.requiredSymbols);
assertEquals(ImmutableSortedSet.of(), features.exportedSymbols);
}
private static final String JAVA_FULL_FEATURED_EXAMPLE =
Joiner.on('\n')
.join(
"package com.example;",
"",
"public class AnExample extends AnotherExample implements IExample {",
" private MyField myField;",
"",
" @AnnotationWithArgs(str = MoarConstants.COMPILE_TIME_CONSTANT)",
" @ChecksMeaning(42)",
" @Edible",
" public static MyExample createFrom(Foo foo, Bar bar) throws MyException {",
" FooAndBar foobar = new FooAndBarSubclass(foo, bar);",
" foobar.init(Thinger.class, com.otherexample.Thinger.class);",
" int meaningOfLife = foobar.someMethod().otherMethod().okLastMethod();",
" if (meaningOfLife == Constants.MEANING_OF_LIFE) {",
" return MyExampleFactory.newInstance(meaningOfLife);",
" } else {",
" Object[] unused = new MyArray[42];",
" Object[] unused2 = new MyArray2[] { null };",
" try {",
" unused2.toString();",
" } catch (BizarreCheckedException e) {}",
" return null;",
" }",
" }",
"}");
@Test
public void testExtractingRequiredSymbolsWithNonTrivialJavaLogic() throws IOException {
JavaFileParser parser = JavaFileParser.createJavaFileParser(DEFAULT_JAVAC_OPTIONS);
JavaFileParser.JavaFileFeatures features =
parser.extractFeaturesFromJavaCode(JAVA_FULL_FEATURED_EXAMPLE);
assertEquals(
ImmutableSortedSet.of(
"com.example.BizarreCheckedException",
"com.example.Constants",
"com.example.FooAndBar",
"com.example.FooAndBarSubclass",
"com.example.MoarConstants",
"com.example.MyArray",
"com.example.MyArray2",
"com.example.MyExampleFactory",
"com.example.MyField",
"com.example.Thinger",
"com.otherexample.Thinger"),
features.requiredSymbols);
assertEquals(
ImmutableSortedSet.of(
"com.example.AnnotationWithArgs",
"com.example.AnotherExample",
"com.example.Bar",
"com.example.ChecksMeaning",
"com.example.Edible",
"com.example.Foo",
"com.example.IExample",
"com.example.MyExample",
"com.example.MyException"),
features.exportedSymbols);
}
private static final String EXPORTED_TYPES_EXAMPLE =
Joiner.on('\n')
.join(
"package com.example;",
"",
"public class AnExample extends SuperExample implements IExample1, IExample2 {",
" public PublicField publicField;",
" protected ProtectedField protectedField;",
" PackagePrivateField packagePrivateField;",
" private PrivateField privateField;",
"",
" public static PublicReturnType createFrom(PublicParam p) throws PublicException {",
" return null;",
" }",
"",
" protected static ProtectedReturnType createFrom(ProtectedParam p) ",
" throws ProtectedException {",
" return null;",
" }",
"",
" static PackagePrivateReturnType createFrom(PackagePrivateParam p) ",
" throws PackagePrivateException {",
" return null;",
" }",
"",
" private static PrivateReturnType createFrom(PrivateParam p) throws PrivateException {",
" return null;",
" }",
"}");
@Test
public void testExtractingExportedTypes() throws IOException {
JavaFileParser parser = JavaFileParser.createJavaFileParser(DEFAULT_JAVAC_OPTIONS);
JavaFileParser.JavaFileFeatures features =
parser.extractFeaturesFromJavaCode(EXPORTED_TYPES_EXAMPLE);
assertEquals(
ImmutableSortedSet.of(
"com.example.IExample1",
"com.example.IExample2",
"com.example.PublicException",
"com.example.PublicField",
"com.example.PublicParam",
"com.example.PublicReturnType",
"com.example.ProtectedException",
"com.example.ProtectedField",
"com.example.ProtectedParam",
"com.example.ProtectedReturnType",
"com.example.PackagePrivateException",
"com.example.PackagePrivateField",
"com.example.PackagePrivateParam",
"com.example.PackagePrivateReturnType",
"com.example.SuperExample"),
features.exportedSymbols);
}
private static final String EXPORTED_TYPES_INTERFACE_EXAMPLE =
Joiner.on('\n')
.join(
"package com.example;",
"",
"import com.example.interfaces.IExample;",
"",
"public interface Example extends IExample {",
"}");
@Test
public void testExtractingExportedTypesFromInterfaceThatExtendsInterfaceFromAnotherPackage()
throws IOException {
JavaFileParser parser = JavaFileParser.createJavaFileParser(DEFAULT_JAVAC_OPTIONS);
JavaFileParser.JavaFileFeatures features =
parser.extractFeaturesFromJavaCode(EXPORTED_TYPES_INTERFACE_EXAMPLE);
assertEquals(ImmutableSortedSet.of(), features.requiredSymbols);
assertEquals(ImmutableSortedSet.of("com.example.Example"), features.providedSymbols);
assertEquals(
ImmutableSortedSet.of("com.example.interfaces.IExample"), features.exportedSymbols);
}
private static final String EXPORTED_TYPES_SUPERCLASS_WITH_GENERIC =
Joiner.on('\n')
.join(
"package com.example;",
"",
"import com.example.interfaces.IExample;",
"",
"public class Example<T> extends IExample<T> {",
"}");
@Test
public void testExtractingExportedTypesFromSuperclassWithAGeneric() throws IOException {
JavaFileParser parser = JavaFileParser.createJavaFileParser(DEFAULT_JAVAC_OPTIONS);
JavaFileParser.JavaFileFeatures features =
parser.extractFeaturesFromJavaCode(EXPORTED_TYPES_SUPERCLASS_WITH_GENERIC);
assertEquals(ImmutableSortedSet.of(), features.requiredSymbols);
assertEquals(ImmutableSortedSet.of("com.example.Example"), features.providedSymbols);
assertEquals(
ImmutableSortedSet.of("com.example.interfaces.IExample"), features.exportedSymbols);
}
private static final String EXPORTED_TYPES_WITH_CLASS_THAT_LOOKS_LIKE_A_GENERIC =
Joiner.on('\n')
.join(
"package com.example;",
"",
"import com.google.common.base.Function;",
"import org.stringtemplate.v4.ST;",
"",
"public class Example {",
" public StringTemplateStep(Function<ST, ST> configure) {}",
"}");
/** This is a case that we ran into in Buck's own source code. */
@Test
public void testExtractingExportedTypesWithClassThatLooksLikeAGeneric() throws IOException {
JavaFileParser parser = JavaFileParser.createJavaFileParser(DEFAULT_JAVAC_OPTIONS);
JavaFileParser.JavaFileFeatures features =
parser.extractFeaturesFromJavaCode(EXPORTED_TYPES_WITH_CLASS_THAT_LOOKS_LIKE_A_GENERIC);
assertEquals(ImmutableSortedSet.of(), features.requiredSymbols);
assertEquals(ImmutableSortedSet.of("com.example.Example"), features.providedSymbols);
assertEquals(
ImmutableSortedSet.of("com.google.common.base.Function", "org.stringtemplate.v4.ST"),
features.exportedSymbols);
}
}