From 439d0c413d83dd99c4c6b7bbc1e3ee9b8285f8cb Mon Sep 17 00:00:00 2001 From: Thomas Durieux Date: Sat, 9 Jun 2018 09:53:04 +0200 Subject: [PATCH] fix: support creation of partial shadow classes (#2040) --- pom.xml | 10 + .../java/JavaReflectionVisitorImpl.java | 261 ++++++++++++------ .../java/JavaReflectionTreeBuilderTest.java | 66 +++-- 3 files changed, 227 insertions(+), 110 deletions(-) diff --git a/pom.xml b/pom.xml index 969f94b91fd..ce01f8f1401 100644 --- a/pom.xml +++ b/pom.xml @@ -254,6 +254,16 @@ querydsl-core 3.6.9 test + + + com.google.code.findbugs + jsr305 + + + com.mysema.commons + mysema-commons-lang + + diff --git a/src/main/java/spoon/support/visitor/java/JavaReflectionVisitorImpl.java b/src/main/java/spoon/support/visitor/java/JavaReflectionVisitorImpl.java index bf4802030a9..3edff854bdf 100644 --- a/src/main/java/spoon/support/visitor/java/JavaReflectionVisitorImpl.java +++ b/src/main/java/spoon/support/visitor/java/JavaReflectionVisitorImpl.java @@ -18,7 +18,6 @@ import spoon.SpoonException; import spoon.reflect.path.CtRole; -import spoon.reflect.reference.CtTypeReference; import spoon.support.visitor.java.reflect.RtMethod; import spoon.support.visitor.java.reflect.RtParameter; @@ -46,38 +45,70 @@ public void visitClass(Class clazz) { if (clazz.getPackage() != null) { clazz.getPackage(); } - for (TypeVariable> generic : clazz.getTypeParameters()) { - visitTypeParameter(generic); + try { + for (TypeVariable> generic : clazz.getTypeParameters()) { + visitTypeParameter(generic); + } + } catch (NoClassDefFoundError ignore) { + // partial classpath } - if (clazz.getGenericSuperclass() != null && clazz.getGenericSuperclass() != Object.class) { - visitTypeReference(CtRole.SUPER_TYPE, clazz.getGenericSuperclass()); + try { + if (clazz.getGenericSuperclass() != null && clazz.getGenericSuperclass() != Object.class) { + visitTypeReference(CtRole.SUPER_TYPE, clazz.getGenericSuperclass()); + } + } catch (NoClassDefFoundError ignore) { + // partial classpath } - for (Type anInterface : clazz.getGenericInterfaces()) { - visitTypeReference(CtRole.INTERFACE, anInterface); + try { + for (Type anInterface : clazz.getGenericInterfaces()) { + visitTypeReference(CtRole.INTERFACE, anInterface); + } + } catch (NoClassDefFoundError ignore) { + // partial classpath } - for (Annotation annotation : clazz.getDeclaredAnnotations()) { - visitAnnotation(annotation); + try { + for (Annotation annotation : clazz.getDeclaredAnnotations()) { + visitAnnotation(annotation); + } + } catch (NoClassDefFoundError ignore) { + // partial classpath } - for (Constructor constructor : clazz.getDeclaredConstructors()) { - if (constructor.isSynthetic()) { - continue; + try { + for (Constructor constructor : clazz.getDeclaredConstructors()) { + if (constructor.isSynthetic()) { + continue; + } + visitConstructor(constructor); } - visitConstructor(constructor); + } catch (NoClassDefFoundError ignore) { + // partial classpath } - for (RtMethod method : getDeclaredMethods(clazz)) { - if (method.getMethod().isSynthetic()) { - continue; + try { + for (RtMethod method : getDeclaredMethods(clazz)) { + if (method.getMethod().isSynthetic()) { + continue; + } + visitMethod(method); } - visitMethod(method); + } catch (NoClassDefFoundError ignore) { + // partial classpath } - for (Field field : clazz.getDeclaredFields()) { - if (field.isSynthetic()) { - continue; + try { + for (Field field : clazz.getDeclaredFields()) { + if (field.isSynthetic()) { + continue; + } + visitField(field); } - visitField(field); + } catch (NoClassDefFoundError ignore) { + // partial classpath } - for (Class aClass : clazz.getDeclaredClasses()) { - visitType(aClass); + try { + for (Class aClass : clazz.getDeclaredClasses()) { + visitType(aClass); + } + } catch (NoClassDefFoundError ignore) { + // partial classpath } } @@ -99,29 +130,54 @@ public void visitInterface(Class clazz) { if (clazz.getPackage() != null) { clazz.getPackage(); } - for (Type anInterface : clazz.getGenericInterfaces()) { - visitTypeReference(CtRole.INTERFACE, anInterface); + try { + for (Type anInterface : clazz.getGenericInterfaces()) { + visitTypeReference(CtRole.INTERFACE, anInterface); + } + } catch (NoClassDefFoundError ignore) { + // partial classpath } - for (Annotation annotation : clazz.getDeclaredAnnotations()) { - visitAnnotation(annotation); + + try { + for (Annotation annotation : clazz.getDeclaredAnnotations()) { + visitAnnotation(annotation); + } + } catch (NoClassDefFoundError ignore) { + // partial classpath } - for (RtMethod method : getDeclaredMethods(clazz)) { - if (method.getMethod().isSynthetic()) { - continue; + try { + for (RtMethod method : getDeclaredMethods(clazz)) { + if (method.getMethod().isSynthetic()) { + continue; + } + visitMethod(method); } - visitMethod(method); + } catch (NoClassDefFoundError ignore) { + // partial classpath } - for (Field field : clazz.getDeclaredFields()) { - if (field.isSynthetic()) { - continue; + try { + for (Field field : clazz.getDeclaredFields()) { + if (field.isSynthetic()) { + continue; + } + visitField(field); } - visitField(field); + } catch (NoClassDefFoundError ignore) { + // partial classpath } - for (Class aClass : clazz.getDeclaredClasses()) { - visitType(aClass); + try { + for (Class aClass : clazz.getDeclaredClasses()) { + visitType(aClass); + } + } catch (NoClassDefFoundError ignore) { + // partial classpath } - for (TypeVariable> generic : clazz.getTypeParameters()) { - visitTypeParameter(generic); + try { + for (TypeVariable> generic : clazz.getTypeParameters()) { + visitTypeParameter(generic); + } + } catch (NoClassDefFoundError ignore) { + // partial classpath } } @@ -131,46 +187,70 @@ public void visitEnum(Class clazz) { if (clazz.getPackage() != null) { clazz.getPackage(); } - for (Type anInterface : clazz.getGenericInterfaces()) { - visitTypeReference(CtRole.INTERFACE, anInterface); - } - for (Annotation annotation : clazz.getDeclaredAnnotations()) { - visitAnnotation(annotation); + try { + for (Type anInterface : clazz.getGenericInterfaces()) { + visitTypeReference(CtRole.INTERFACE, anInterface); + } + } catch (NoClassDefFoundError ignore) { + // partial classpath } - for (Constructor constructor : clazz.getDeclaredConstructors()) { - if (Modifier.isPrivate(constructor.getModifiers())) { - Class[] paramTypes = constructor.getParameterTypes(); - if (paramTypes.length == 2 && paramTypes[0] == String.class && paramTypes[1] == int.class) { - //ignore implicit enum constructor + try { + for (Annotation annotation : clazz.getDeclaredAnnotations()) { + visitAnnotation(annotation); + } + } catch (NoClassDefFoundError ignore) { + // partial classpath + } + try { + for (Constructor constructor : clazz.getDeclaredConstructors()) { + if (Modifier.isPrivate(constructor.getModifiers())) { + Class[] paramTypes = constructor.getParameterTypes(); + if (paramTypes.length == 2 && paramTypes[0] == String.class && paramTypes[1] == int.class) { + //ignore implicit enum constructor + continue; + } + } + if (constructor.isSynthetic()) { continue; } + visitConstructor(constructor); } - if (constructor.isSynthetic()) { - continue; - } - visitConstructor(constructor); + } catch (NoClassDefFoundError ignore) { + // partial classpath } - for (RtMethod method : getDeclaredMethods(clazz)) { - if (("valueOf".equals(method.getName()) && method.getParameterTypes().length == 1 && String.class.equals(method.getParameterTypes()[0])) || "values".equals(method.getName())) { - continue; - } - if (method.getMethod().isSynthetic()) { - continue; + try { + for (RtMethod method : getDeclaredMethods(clazz)) { + if (("valueOf".equals(method.getName()) && method.getParameterTypes().length == 1 && String.class.equals(method.getParameterTypes()[0])) || "values".equals(method.getName())) { + continue; + } + if (method.getMethod().isSynthetic()) { + continue; + } + visitMethod(method); } - visitMethod(method); + } catch (NoClassDefFoundError ignore) { + // partial classpath } - for (Field field : clazz.getDeclaredFields()) { - if (field.isSynthetic()) { - continue; - } - if (field.isEnumConstant()) { - visitEnumValue(field); - } else { - visitField(field); + try { + for (Field field : clazz.getDeclaredFields()) { + if (field.isSynthetic()) { + continue; + } + if (field.isEnumConstant()) { + visitEnumValue(field); + } else { + visitField(field); + } } + } catch (NoClassDefFoundError ignore) { + // partial classpath } - for (Class aClass : clazz.getDeclaredClasses()) { - visitType(aClass); + try { + for (Class aClass : clazz.getDeclaredClasses()) { + visitType(aClass); + } + } catch (NoClassDefFoundError ignore) { + // partial classpath } } @@ -180,23 +260,39 @@ public void visitAnnotationClass(Class clazz) { if (clazz.getPackage() != null) { clazz.getPackage(); } - for (Annotation annotation : clazz.getDeclaredAnnotations()) { - visitAnnotation(annotation); + try { + for (Annotation annotation : clazz.getDeclaredAnnotations()) { + visitAnnotation(annotation); + } + } catch (NoClassDefFoundError ignore) { + // partial classpath } - for (RtMethod method : getDeclaredMethods(clazz)) { - if (method.getMethod().isSynthetic()) { - continue; + try { + for (RtMethod method : getDeclaredMethods(clazz)) { + if (method.getMethod().isSynthetic()) { + continue; + } + visitMethod(method); } - visitMethod(method); + } catch (NoClassDefFoundError ignore) { + // partial classpath } - for (Field field : clazz.getDeclaredFields()) { - if (field.isSynthetic()) { - continue; + try { + for (Field field : clazz.getDeclaredFields()) { + if (field.isSynthetic()) { + continue; + } + visitField(field); } - visitField(field); + } catch (NoClassDefFoundError ignore) { + // partial classpath } - for (Class aClass : clazz.getDeclaredClasses()) { - visitType(aClass); + try { + for (Class aClass : clazz.getDeclaredClasses()) { + visitType(aClass); + } + } catch (NoClassDefFoundError ignore) { + // partial classpath } } @@ -322,7 +418,6 @@ public void visitTypeParameterReference(CtRole ro @Override public final void visitTypeReference(CtRole role, Type type) { - CtTypeReference ctTypeReference; if (type instanceof TypeVariable) { this.visitTypeParameterReference(role, (TypeVariable) type); return; diff --git a/src/test/java/spoon/support/visitor/java/JavaReflectionTreeBuilderTest.java b/src/test/java/spoon/support/visitor/java/JavaReflectionTreeBuilderTest.java index c5249717353..ef48922a4df 100644 --- a/src/test/java/spoon/support/visitor/java/JavaReflectionTreeBuilderTest.java +++ b/src/test/java/spoon/support/visitor/java/JavaReflectionTreeBuilderTest.java @@ -1,33 +1,7 @@ package spoon.support.visitor.java; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; -import static spoon.testing.utils.ModelUtils.createFactory; - -import java.io.File; -import java.io.ObjectInputStream; -import java.lang.annotation.Retention; -import java.net.CookieManager; -import java.net.URLClassLoader; -import java.time.format.TextStyle; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.function.Consumer; -import java.util.stream.Collectors; - +import com.mysema.query.support.ProjectableQuery; import org.junit.Test; - import spoon.Launcher; import spoon.SpoonException; import spoon.metamodel.MetamodelConcept; @@ -71,6 +45,32 @@ import spoon.support.visitor.equals.EqualsVisitor; import spoon.test.generics.ComparableComparatorBug; +import java.io.File; +import java.io.ObjectInputStream; +import java.lang.annotation.Retention; +import java.net.CookieManager; +import java.net.URLClassLoader; +import java.time.format.TextStyle; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Consumer; +import java.util.stream.Collectors; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static spoon.testing.utils.ModelUtils.createFactory; + public class JavaReflectionTreeBuilderTest { @Test public void testScannerClass() throws Exception { @@ -568,4 +568,16 @@ public void testTypeParameterCtConditionnal() { assertEquals("T", typeParameter.getSimpleName()); assertTrue(typeParameter.getSuperclass() == null); } + + + + @Test + public void testPartialShadow() { + // contract: the shadow class can be partially created + Factory factory = createFactory(); + CtType type = factory.Type().get(ProjectableQuery.class); + assertEquals("ProjectableQuery", type.getSimpleName()); + // because one of the parameter is not in the classpath therefor the reflection did not succeed to list the methods + assertEquals(0, type.getMethods().size()); + } }