• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2016 Google LLC
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  */
16 package com.google.auto.common;
17 
18 import static com.google.common.truth.Truth.assertThat;
19 import static java.nio.charset.StandardCharsets.UTF_8;
20 import static java.util.Objects.requireNonNull;
21 import static javax.lang.model.util.ElementFilter.methodsIn;
22 
23 import com.google.common.base.Converter;
24 import com.google.common.base.Optional;
25 import com.google.common.base.StandardSystemProperty;
26 import com.google.common.collect.ImmutableList;
27 import com.google.common.collect.ImmutableSet;
28 import com.google.common.collect.Range;
29 import com.google.common.io.Files;
30 import com.google.common.truth.Correspondence;
31 import com.google.common.truth.Expect;
32 import com.google.testing.compile.CompilationRule;
33 import java.io.File;
34 import java.util.AbstractCollection;
35 import java.util.AbstractList;
36 import java.util.ArrayList;
37 import java.util.Collection;
38 import java.util.List;
39 import java.util.Set;
40 import javax.annotation.processing.AbstractProcessor;
41 import javax.annotation.processing.RoundEnvironment;
42 import javax.annotation.processing.SupportedAnnotationTypes;
43 import javax.lang.model.SourceVersion;
44 import javax.lang.model.element.Element;
45 import javax.lang.model.element.ExecutableElement;
46 import javax.lang.model.element.TypeElement;
47 import javax.lang.model.element.VariableElement;
48 import javax.lang.model.type.ArrayType;
49 import javax.lang.model.type.DeclaredType;
50 import javax.lang.model.type.TypeKind;
51 import javax.lang.model.type.TypeMirror;
52 import javax.lang.model.type.TypeVariable;
53 import javax.lang.model.type.TypeVisitor;
54 import javax.lang.model.util.Elements;
55 import javax.lang.model.util.SimpleTypeVisitor6;
56 import javax.lang.model.util.Types;
57 import javax.tools.JavaCompiler;
58 import javax.tools.JavaFileObject;
59 import javax.tools.StandardJavaFileManager;
60 import javax.tools.StandardLocation;
61 import org.eclipse.jdt.internal.compiler.tool.EclipseCompiler;
62 import org.checkerframework.checker.nullness.qual.Nullable;
63 import org.junit.Before;
64 import org.junit.Rule;
65 import org.junit.Test;
66 import org.junit.rules.TestRule;
67 import org.junit.runner.Description;
68 import org.junit.runner.RunWith;
69 import org.junit.runners.Parameterized;
70 import org.junit.runners.model.Statement;
71 
72 /**
73  * Tests that the {@link Overrides} class has behaviour consistent with javac. We test this in
74  * two ways: once with {@link Overrides.ExplicitOverrides} using javac's own {@link Elements} and
75  * {@link Types}, and once with it using the version of those objects from the Eclipse compiler
76  * (ecj).
77  *
78  * @author emcmanus@google.com (Éamonn McManus)
79  */
80 @RunWith(Parameterized.class)
81 public class OverridesTest {
82   @Parameterized.Parameters(name = "{0}")
data()83   public static CompilerType[] data() {
84     return CompilerType.values();
85   }
86 
87   @Rule public CompilationRule compilation = new CompilationRule();
88   @Rule public EcjCompilationRule ecjCompilation = new EcjCompilationRule();
89   @Rule public Expect expect = Expect.create();
90 
91   public enum CompilerType {
92     JAVAC {
93       @Override
initUtils(OverridesTest test)94       void initUtils(OverridesTest test) {
95         test.typeUtils = test.compilation.getTypes();
96         test.elementUtils = test.compilation.getElements();
97       }
98     },
99     ECJ {
100       @Override
initUtils(OverridesTest test)101       void initUtils(OverridesTest test) {
102         test.typeUtils = test.ecjCompilation.types;
103         test.elementUtils = test.ecjCompilation.elements;
104       }
105     };
106 
initUtils(OverridesTest test)107     abstract void initUtils(OverridesTest test);
108   }
109 
110   private final CompilerType compilerType;
111 
112   private Types typeUtils;
113   private Elements elementUtils;
114   private Elements javacElementUtils;
115   private Overrides javacOverrides;
116   private Overrides.ExplicitOverrides explicitOverrides;
117 
OverridesTest(CompilerType compilerType)118   public OverridesTest(CompilerType compilerType) {
119     this.compilerType = compilerType;
120   }
121 
122   @Before
initializeTestElements()123   public void initializeTestElements() {
124     javacElementUtils = compilation.getElements();
125     javacOverrides = new Overrides.NativeOverrides(javacElementUtils);
126     compilerType.initUtils(this);
127     explicitOverrides = new Overrides.ExplicitOverrides(typeUtils);
128   }
129 
130   static class TypesForInheritance {
131     interface One {
m()132       void m();
133 
m(String x)134       void m(String x);
135 
n()136       void n();
137 
number()138       Number number();
139     }
140 
141     interface Two {
m()142       void m();
143 
m(int x)144       void m(int x);
145 
number()146       Integer number();
147     }
148 
149     static class Parent {
m()150       public void m() {}
151     }
152 
153     static class ChildOfParent extends Parent {}
154 
155     static class ChildOfOne implements One {
156       @Override
m()157       public void m() {}
158 
159       @Override
m(String x)160       public void m(String x) {}
161 
162       @Override
n()163       public void n() {}
164 
165       @Override
number()166       public Number number() {
167         return 0;
168       }
169     }
170 
171     static class ChildOfOneAndTwo implements One, Two {
172       @Override
m()173       public void m() {}
174 
175       @Override
m(String x)176       public void m(String x) {}
177 
178       @Override
m(int x)179       public void m(int x) {}
180 
181       @Override
n()182       public void n() {}
183 
184       @Override
number()185       public Integer number() {
186         return 0;
187       }
188     }
189 
190     static class ChildOfParentAndOne extends Parent implements One {
191       @Override
m()192       public void m() {}
193 
194       @Override
m(String x)195       public void m(String x) {}
196 
197       @Override
n()198       public void n() {}
199 
200       @Override
number()201       public Number number() {
202         return 0;
203       }
204     }
205 
206     static class ChildOfParentAndOneAndTwo extends Parent implements One, Two {
207       @Override
m(String x)208       public void m(String x) {}
209 
210       @Override
m(int x)211       public void m(int x) {}
212 
213       @Override
n()214       public void n() {}
215 
216       @Override
number()217       public Integer number() {
218         return 0;
219       }
220     }
221 
222     abstract static class AbstractChildOfOne implements One {}
223 
224     abstract static class AbstractChildOfOneAndTwo implements One, Two {}
225 
226     abstract static class AbstractChildOfParentAndOneAndTwo extends Parent implements One, Two {}
227 
228     interface ExtendingOneAndTwo extends One, Two {}
229   }
230 
231   static class MoreTypesForInheritance {
232     interface Key {}
233 
234     interface BindingType {}
235 
236     interface ContributionType {}
237 
238     interface HasKey {
key()239       Key key();
240     }
241 
242     interface HasBindingType {
bindingType()243       BindingType bindingType();
244     }
245 
246     interface HasContributionType {
contributionType()247       ContributionType contributionType();
248     }
249 
250     abstract static class BindingDeclaration implements HasKey {
bindingElement()251       abstract Optional<Element> bindingElement();
252 
contributingModule()253       abstract Optional<TypeElement> contributingModule();
254     }
255 
256     abstract static class MultibindingDeclaration extends BindingDeclaration
257         implements HasBindingType, HasContributionType {
258       @Override
key()259       public abstract Key key();
260 
261       @Override
contributionType()262       public abstract ContributionType contributionType();
263 
264       @Override
bindingType()265       public abstract BindingType bindingType();
266     }
267   }
268 
269   static class TypesForVisibility {
270     public abstract static class PublicGrandparent {
foo()271       public abstract String foo();
272     }
273 
274     private static class PrivateParent extends PublicGrandparent {
275       @Override
foo()276       public String foo() {
277         return "foo";
278       }
279     }
280 
281     static class Child extends PrivateParent {}
282   }
283 
284   static class TypesForGenerics {
285     interface GCollection<E> {
add(E x)286       boolean add(E x);
287     }
288 
289     interface GList<E> extends GCollection<E> {
290       @Override
add(E x)291       boolean add(E x);
292     }
293 
294     static class StringList implements GList<String> {
295       @Override
add(String x)296       public boolean add(String x) {
297         return false;
298       }
299     }
300 
301     @SuppressWarnings("rawtypes")
302     static class RawList implements GList {
303       @Override
add(Object x)304       public boolean add(Object x) {
305         return false;
306       }
307     }
308   }
309 
310   @SuppressWarnings("rawtypes")
311   static class TypesForRaw {
312     static class RawParent {
frob(List x)313       void frob(List x) {}
314     }
315 
316     static class RawChildOfRaw extends RawParent {
317       @Override
frob(List x)318       void frob(List x) {}
319     }
320 
321     static class NonRawParent {
frob(List<String> x)322       void frob(List<String> x) {}
323     }
324 
325     static class RawChildOfNonRaw extends NonRawParent {
326       @Override
frob(List x)327       void frob(List x) {}
328     }
329   }
330 
331   @Test
overridesInheritance()332   public void overridesInheritance() {
333     checkOverridesInContainedClasses(TypesForInheritance.class);
334   }
335 
336   @Test
overridesMoreInheritance()337   public void overridesMoreInheritance() {
338     checkOverridesInContainedClasses(MoreTypesForInheritance.class);
339   }
340 
341   @Test
overridesVisibility()342   public void overridesVisibility() {
343     checkOverridesInContainedClasses(TypesForVisibility.class);
344   }
345 
346   @Test
overridesGenerics()347   public void overridesGenerics() {
348     checkOverridesInContainedClasses(TypesForGenerics.class);
349   }
350 
351   @Test
overridesRaw()352   public void overridesRaw() {
353     checkOverridesInContainedClasses(TypesForRaw.class);
354   }
355 
356   // Test a tricky diamond inheritance hierarchy:
357   //               Collection
358   //              /          \
359   // AbstractCollection     List
360   //              \          /
361   //              AbstractList
362   // This also tests that we do the right thing with generics, since naively the TypeMirror
363   // that you get for List<E> will not appear to be a subtype of the one you get for Collection<E>
364   // since the two Es are not the same.
365   @Test
overridesDiamond()366   public void overridesDiamond() {
367     checkOverridesInSet(
368         ImmutableSet.<Class<?>>of(
369             Collection.class, List.class, AbstractCollection.class, AbstractList.class));
370   }
371 
checkOverridesInContainedClasses(Class<?> container)372   private void checkOverridesInContainedClasses(Class<?> container) {
373     checkOverridesInSet(ImmutableSet.copyOf(container.getDeclaredClasses()));
374   }
375 
checkOverridesInSet(ImmutableSet<Class<?>> testClasses)376   private void checkOverridesInSet(ImmutableSet<Class<?>> testClasses) {
377     assertThat(testClasses).isNotEmpty();
378     ImmutableSet.Builder<TypeElement> testTypesBuilder = ImmutableSet.builder();
379     for (Class<?> testClass : testClasses) {
380       testTypesBuilder.add(getTypeElement(testClass));
381     }
382     ImmutableSet<TypeElement> testTypes = testTypesBuilder.build();
383     ImmutableSet.Builder<ExecutableElement> testMethodsBuilder = ImmutableSet.builder();
384     for (TypeElement testType : testTypes) {
385       testMethodsBuilder.addAll(methodsIn(testType.getEnclosedElements()));
386     }
387     ImmutableSet<ExecutableElement> testMethods = testMethodsBuilder.build();
388     for (TypeElement in : testTypes) {
389       TypeElement javacIn = javacType(in);
390       List<ExecutableElement> inMethods = methodsIn(elementUtils.getAllMembers(in));
391       for (ExecutableElement overrider : inMethods) {
392         ExecutableElement javacOverrider = javacMethod(overrider);
393         for (ExecutableElement overridden : testMethods) {
394           ExecutableElement javacOverridden = javacMethod(overridden);
395           boolean javacSays = javacOverrides.overrides(javacOverrider, javacOverridden, javacIn);
396           boolean weSay = explicitOverrides.overrides(overrider, overridden, in);
397           if (javacSays != weSay) {
398             expect
399                 .withMessage(
400                     "%s.%s overrides %s.%s in %s: javac says %s, we say %s",
401                     overrider.getEnclosingElement(),
402                     overrider,
403                     overridden.getEnclosingElement(),
404                     overridden,
405                     in,
406                     javacSays,
407                     weSay)
408                 .fail();
409           }
410         }
411       }
412     }
413   }
414 
getTypeElement(Class<?> c)415   private TypeElement getTypeElement(Class<?> c) {
416     return elementUtils.getTypeElement(c.getCanonicalName());
417   }
418 
getMethod(TypeElement in, String name, TypeKind... parameterTypeKinds)419   private ExecutableElement getMethod(TypeElement in, String name, TypeKind... parameterTypeKinds) {
420     ExecutableElement found = null;
421     methods:
422     for (ExecutableElement method : methodsIn(in.getEnclosedElements())) {
423       if (method.getSimpleName().contentEquals(name)
424           && method.getParameters().size() == parameterTypeKinds.length) {
425         for (int i = 0; i < parameterTypeKinds.length; i++) {
426           if (method.getParameters().get(i).asType().getKind() != parameterTypeKinds[i]) {
427             continue methods;
428           }
429         }
430         assertThat(found).isNull();
431         found = method;
432       }
433     }
434     assertThat(found).isNotNull();
435     return requireNonNull(found);
436   }
437 
438   // These skeletal parallels to the real collection classes ensure that the test is independent
439   // of the details of those classes, for example whether List<E> redeclares add(E) even though
440   // it also inherits it from Collection<E>.
441 
442   private interface XCollection<E> {
add(E e)443     boolean add(E e);
444   }
445 
446   private interface XList<E> extends XCollection<E> {}
447 
448   private abstract static class XAbstractCollection<E> implements XCollection<E> {
449     @Override
add(E e)450     public boolean add(E e) {
451       return false;
452     }
453   }
454 
455   private abstract static class XAbstractList<E> extends XAbstractCollection<E>
456       implements XList<E> {
457     @Override
add(E e)458     public boolean add(E e) {
459       return true;
460     }
461   }
462 
463   private abstract static class XStringList extends XAbstractList<String> {}
464 
465   private abstract static class XAbstractStringList implements XList<String> {}
466 
467   private abstract static class XNumberList<E extends Number> extends XAbstractList<E> {}
468 
469   // Parameter of add(E) in StringList is String.
470   // That means that we successfully recorded E[AbstractList] = String and E[List] = E[AbstractList]
471   // and String made it all the way through.
472   @Test
methodParameters_StringList()473   public void methodParameters_StringList() {
474     TypeElement xAbstractList = getTypeElement(XAbstractList.class);
475     TypeElement xStringList = getTypeElement(XStringList.class);
476     TypeElement string = getTypeElement(String.class);
477 
478     ExecutableElement add = getMethod(xAbstractList, "add", TypeKind.TYPEVAR);
479     List<TypeMirror> params = explicitOverrides.erasedParameterTypes(add, xStringList);
480     List<TypeMirror> expectedParams = ImmutableList.of(string.asType());
481     assertTypeListsEqual(params, expectedParams);
482   }
483 
484   // Parameter of add(E) in AbstractStringList is String.
485   // That means that we successfully recorded E[List] = String and E[Collection] = E[List].
486   @Test
methodParameters_AbstractStringList()487   public void methodParameters_AbstractStringList() {
488     TypeElement xCollection = getTypeElement(XCollection.class);
489     TypeElement xAbstractStringList = getTypeElement(XAbstractStringList.class);
490     TypeElement string = getTypeElement(String.class);
491 
492     ExecutableElement add = getMethod(xCollection, "add", TypeKind.TYPEVAR);
493 
494     List<TypeMirror> params = explicitOverrides.erasedParameterTypes(add, xAbstractStringList);
495     List<TypeMirror> expectedParams = ImmutableList.of(string.asType());
496     assertTypeListsEqual(params, expectedParams);
497   }
498 
499   // Parameter of add(E) in NumberList is Number.
500   // That means that we successfully recorded E[AbstractList] = Number and on from
501   // there, with Number being used because it is the erasure of <E extends Number>.
502   @Test
methodParams_NumberList()503   public void methodParams_NumberList() {
504     TypeElement xCollection = getTypeElement(XCollection.class);
505     TypeElement xNumberList = getTypeElement(XNumberList.class);
506     TypeElement number = getTypeElement(Number.class);
507 
508     ExecutableElement add = getMethod(xCollection, "add", TypeKind.TYPEVAR);
509 
510     List<TypeMirror> params = explicitOverrides.erasedParameterTypes(add, xNumberList);
511     List<TypeMirror> expectedParams = ImmutableList.of(number.asType());
512     assertTypeListsEqual(params, expectedParams);
513   }
514 
515   // This is derived from a class that provoked a StackOverflowError in an earlier version.
516   private abstract static class StringToRangeConverter<T extends Comparable<T>>
517       extends Converter<String, Range<T>> {
518     @Override
doBackward(Range<T> b)519     protected String doBackward(Range<T> b) {
520       return "";
521     }
522   }
523 
524   @Test
methodParams_RecursiveBound()525   public void methodParams_RecursiveBound() {
526     TypeElement stringToRangeConverter = getTypeElement(StringToRangeConverter.class);
527     TypeElement range = getTypeElement(Range.class);
528     ExecutableElement valueConverter =
529         getMethod(stringToRangeConverter, "doBackward", TypeKind.DECLARED);
530     List<TypeMirror> params =
531         explicitOverrides.erasedParameterTypes(valueConverter, stringToRangeConverter);
532     List<TypeMirror> expectedParams =
533         ImmutableList.<TypeMirror>of(typeUtils.erasure(range.asType()));
534     assertTypeListsEqual(params, expectedParams);
535   }
536 
537   @Test
methodFromSuperclasses()538   public void methodFromSuperclasses() {
539     TypeElement xAbstractCollection = getTypeElement(XAbstractCollection.class);
540     TypeElement xAbstractList = getTypeElement(XAbstractList.class);
541     TypeElement xAbstractStringList = getTypeElement(XAbstractStringList.class);
542     TypeElement xStringList = getTypeElement(XStringList.class);
543 
544     ExecutableElement add = getMethod(xAbstractCollection, "add", TypeKind.TYPEVAR);
545 
546     ExecutableElement addInAbstractStringList =
547         explicitOverrides.methodFromSuperclasses(xAbstractStringList, add);
548     assertThat(addInAbstractStringList).isNull();
549 
550     ExecutableElement addInStringList = explicitOverrides.methodFromSuperclasses(xStringList, add);
551     assertThat(requireNonNull(addInStringList).getEnclosingElement()).isEqualTo(xAbstractList);
552   }
553 
554   @Test
methodFromSuperinterfaces()555   public void methodFromSuperinterfaces() {
556     TypeElement xCollection = getTypeElement(XCollection.class);
557     TypeElement xAbstractList = getTypeElement(XAbstractList.class);
558     TypeElement xAbstractStringList = getTypeElement(XAbstractStringList.class);
559     TypeElement xNumberList = getTypeElement(XNumberList.class);
560     TypeElement xList = getTypeElement(XList.class);
561 
562     ExecutableElement add = getMethod(xCollection, "add", TypeKind.TYPEVAR);
563 
564     ExecutableElement addInAbstractStringList =
565         explicitOverrides.methodFromSuperinterfaces(xAbstractStringList, add);
566     assertThat(requireNonNull(addInAbstractStringList).getEnclosingElement())
567         .isEqualTo(xCollection);
568 
569     ExecutableElement addInNumberList =
570         explicitOverrides.methodFromSuperinterfaces(xNumberList, add);
571     assertThat(requireNonNull(addInNumberList).getEnclosingElement()).isEqualTo(xAbstractList);
572 
573     ExecutableElement addInList = explicitOverrides.methodFromSuperinterfaces(xList, add);
574     assertThat(requireNonNull(addInList).getEnclosingElement()).isEqualTo(xCollection);
575   }
576 
assertTypeListsEqual(@ullable List<TypeMirror> actual, List<TypeMirror> expected)577   private void assertTypeListsEqual(@Nullable List<TypeMirror> actual, List<TypeMirror> expected) {
578     assertThat(actual)
579       .comparingElementsUsing(Correspondence.from(typeUtils::isSameType, "is same type as"))
580       .containsExactlyElementsIn(expected)
581       .inOrder();
582   }
583 
584   // TODO(emcmanus): replace this with something from compile-testing when that's available.
585   /**
586    * An equivalent to {@link CompilationRule} that uses ecj (the Eclipse compiler) instead of javac.
587    * If the parameterized test is not selecting ecj then this rule has no effect.
588    */
589   public class EcjCompilationRule implements TestRule {
590     Elements elements;
591     Types types;
592 
593     @Override
apply(Statement base, Description description)594     public Statement apply(Statement base, Description description) {
595       if (!compilerType.equals(CompilerType.ECJ)) {
596         return base;
597       }
598       return new EcjCompilationStatement(base);
599     }
600   }
601 
602   private class EcjCompilationStatement extends Statement {
603     private final Statement statement;
604 
EcjCompilationStatement(Statement base)605     EcjCompilationStatement(Statement base) {
606       this.statement = base;
607     }
608 
609     @Override
evaluate()610     public void evaluate() throws Throwable {
611       File tmpDir = File.createTempFile("OverridesTest", "dir");
612       tmpDir.delete();
613       tmpDir.mkdir();
614       File dummySourceFile = new File(tmpDir, "Dummy.java");
615       try {
616         Files.asCharSink(dummySourceFile, UTF_8).write("class Dummy {}");
617         evaluate(dummySourceFile);
618       } finally {
619         dummySourceFile.delete();
620         tmpDir.delete();
621       }
622     }
623 
evaluate(File dummySourceFile)624     private void evaluate(File dummySourceFile) throws Throwable {
625       JavaCompiler compiler = new EclipseCompiler();
626       StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, UTF_8);
627       // This hack is only needed in a Google-internal Java 8 environment where symbolic links make
628       // it hard for ecj to find the boot class path. Elsewhere it is unnecessary but harmless.
629       File rtJar = new File(StandardSystemProperty.JAVA_HOME.value() + "/lib/rt.jar");
630       if (rtJar.exists()) {
631         List<File> bootClassPath =
632             ImmutableList.<File>builder()
633                 .add(rtJar)
634                 .addAll(fileManager.getLocation(StandardLocation.PLATFORM_CLASS_PATH))
635                 .build();
636         fileManager.setLocation(StandardLocation.PLATFORM_CLASS_PATH, bootClassPath);
637       }
638       Iterable<? extends JavaFileObject> sources = fileManager.getJavaFileObjects(dummySourceFile);
639       JavaCompiler.CompilationTask task =
640           compiler.getTask(null, fileManager, null, null, null, sources);
641       EcjTestProcessor processor = new EcjTestProcessor(statement);
642       task.setProcessors(ImmutableList.of(processor));
643       assertThat(task.call()).isTrue();
644       processor.maybeThrow();
645     }
646   }
647 
648   @SupportedAnnotationTypes("*")
649   private class EcjTestProcessor extends AbstractProcessor {
650     private final Statement statement;
651     private Throwable thrown;
652 
EcjTestProcessor(Statement statement)653     EcjTestProcessor(Statement statement) {
654       this.statement = statement;
655     }
656 
657     @Override
getSupportedSourceVersion()658     public SourceVersion getSupportedSourceVersion() {
659       return SourceVersion.latest();
660     }
661 
662     @Override
process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv)663     public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
664       if (roundEnv.processingOver()) {
665         ecjCompilation.elements = processingEnv.getElementUtils();
666         ecjCompilation.types = processingEnv.getTypeUtils();
667         try {
668           statement.evaluate();
669         } catch (Throwable t) {
670           thrown = t;
671         }
672       }
673       return false;
674     }
675 
maybeThrow()676     void maybeThrow() throws Throwable {
677       if (thrown != null) {
678         throw thrown;
679       }
680     }
681   }
682 
javacType(TypeElement type)683   private TypeElement javacType(TypeElement type) {
684     return javacElementUtils.getTypeElement(type.getQualifiedName().toString());
685   }
686 
javacMethod(ExecutableElement method)687   private ExecutableElement javacMethod(ExecutableElement method) {
688     if (elementUtils == javacElementUtils) {
689       return method;
690     }
691     TypeElement containingType = MoreElements.asType(method.getEnclosingElement());
692     TypeElement javacContainingType = javacType(containingType);
693     List<ExecutableElement> candidates = new ArrayList<ExecutableElement>();
694     methods:
695     for (ExecutableElement javacMethod : methodsIn(javacContainingType.getEnclosedElements())) {
696       if (javacMethod.getSimpleName().contentEquals(method.getSimpleName())
697           && javacMethod.getParameters().size() == method.getParameters().size()) {
698         for (int i = 0; i < method.getParameters().size(); i++) {
699           VariableElement parameter = method.getParameters().get(i);
700           VariableElement javacParameter = javacMethod.getParameters().get(i);
701           if (!erasedToString(parameter.asType()).equals(erasedToString(javacParameter.asType()))) {
702             continue methods;
703           }
704         }
705         candidates.add(javacMethod);
706       }
707     }
708     if (candidates.size() == 1) {
709       return candidates.get(0);
710     } else {
711       throw new IllegalStateException(
712           "Expected one javac method matching " + method + " but found " + candidates);
713     }
714   }
715 
erasedToString(TypeMirror type)716   private static String erasedToString(TypeMirror type) {
717     return ERASED_STRING_TYPE_VISITOR.visit(type);
718   }
719 
720   private static final TypeVisitor<String, Void> ERASED_STRING_TYPE_VISITOR =
721       new SimpleTypeVisitor6<String, Void>() {
722         @Override
723         protected String defaultAction(TypeMirror e, Void p) {
724           return e.toString();
725         }
726 
727         @Override
728         public String visitArray(ArrayType t, Void p) {
729           return visit(t.getComponentType()) + "[]";
730         }
731 
732         @Override
733         public String visitDeclared(DeclaredType t, Void p) {
734           return MoreElements.asType(t.asElement()).getQualifiedName().toString();
735         }
736 
737         @Override
738         public String visitTypeVariable(TypeVariable t, Void p) {
739           return visit(t.getUpperBound());
740         }
741       };
742 }
743