From bd52114431cbf2870054332ee9f171863b57f582 Mon Sep 17 00:00:00 2001 From: bsorrentino Date: Wed, 7 Feb 2018 14:12:41 -0600 Subject: [PATCH] add better support for annotation processing in return type --- .../org/bsc/processor/TypescriptHelper.java | 192 ++++++++++++++++-- .../bsc/processor/TypescriptProcessor.java | 19 +- .../org/bsc/processor/RefrectionTest.java | 109 ++++++++++ .../test/java/org/bsc/processor/Sample1.java | 23 +++ .../test/java/org/bsc/processor/Sample2.java | 6 + 5 files changed, 324 insertions(+), 25 deletions(-) create mode 100644 processor/src/test/java/org/bsc/processor/RefrectionTest.java create mode 100644 processor/src/test/java/org/bsc/processor/Sample1.java create mode 100644 processor/src/test/java/org/bsc/processor/Sample2.java diff --git a/processor/src/main/java/org/bsc/processor/TypescriptHelper.java b/processor/src/main/java/org/bsc/processor/TypescriptHelper.java index bff1417..867bbe7 100644 --- a/processor/src/main/java/org/bsc/processor/TypescriptHelper.java +++ b/processor/src/main/java/org/bsc/processor/TypescriptHelper.java @@ -5,13 +5,61 @@ import java.beans.BeanInfo; import java.beans.IntrospectionException; import java.beans.PropertyDescriptor; +import java.lang.reflect.GenericArrayType; import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.lang.reflect.Parameter; +import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.lang.reflect.WildcardType; import java.util.Arrays; +import java.util.function.BiFunction; +import java.util.function.BiPredicate; +import java.util.function.Consumer; +import java.util.function.Predicate; import java.util.stream.Collectors; -public interface TypescriptHelper { +public class TypescriptHelper { + + /** + * + */ + public static BiPredicate, Class> isPackageMatch = (a, b ) -> + a.getPackage().equals(b.getPackage()) ; + + /** + * + */ + public static Predicate> isFunctionalInterface = type -> + type.isInterface() && type.isAnnotationPresent(FunctionalInterface.class); + + /** + * + */ + public static BiFunction,TypeVariable, Boolean> typeParameterMatch = (declaringClass, typeParameter) -> + Arrays.stream(declaringClass.getTypeParameters()) + .map( (tp) -> tp.getName()) + .anyMatch( name -> name.equals(typeParameter.getName())) + ; + + private final static Consumer log = msg -> System.out.println(msg); + + /** + * + * @param p + * @return + */ + public static final String getParameterName( Parameter p ) { + final String name = p.getName(); + + switch( name ) { + case "function": + return "func"; + default: + return name; + } + } /** @@ -87,7 +135,7 @@ static String getClassDecl( Class type, if( superclass!=null ) { inherited .append( " extends ") - .append( getName(superclass, type, true) ) + .append( getTypeName(superclass, type, true) ) ; } } @@ -97,7 +145,7 @@ static String getClassDecl( Class type, if(interfaces.length > 0 ) { final String ifc = Arrays.stream(interfaces) - .map( (c) -> getName(c,type, true) ) + .map( (c) -> getTypeName(c,type, true) ) .collect( Collectors.joining(", ")) ; inherited @@ -165,7 +213,7 @@ static String getSimpleName( Class type ) { * @param type * @return */ - static String getName( Class type ) { + static String getTypeName( Class type ) { return type.getName().concat(getClassParametersDecl(type)); } @@ -176,11 +224,11 @@ static String getName( Class type ) { * @return * @throws ClassNotFoundException */ - static String getName( Type type, Class declaringClass, boolean packageResolution ) throws ClassNotFoundException { + static String getTypeName( Type type, Class declaringClass, boolean packageResolution ) throws ClassNotFoundException { final Class clazz = Class.forName(type.getTypeName()); - return getName( clazz, declaringClass, packageResolution ); + return getTypeName( clazz, declaringClass, packageResolution ); } @@ -190,7 +238,7 @@ static String getName( Type type, Class declaringClass, boolean packageResolu * @param declaringClass * @return */ - static String getName( Class type, Class declaringClass, boolean packageResolution ) { + static String getTypeName( Class type, Class declaringClass, boolean packageResolution ) { final java.util.List dc_parameters_list = Arrays.stream(declaringClass.getTypeParameters()) @@ -200,19 +248,18 @@ static String getName( Class type, Class declaringClass, boolean packageRe final java.util.List type_parameters_list = Arrays.stream(type.getTypeParameters()) .map( tp -> (dc_parameters_list.contains(tp.getName()) ) ? tp.getName() : "any" ) - //.map( tp -> tp.getName() ) .collect(Collectors.toList()); final java.util.List parameters = dc_parameters_list.size() == type_parameters_list.size() ? dc_parameters_list : type_parameters_list ; - boolean isFunctionaInterface = ( type.isInterface() && type.isAnnotationPresent(FunctionalInterface.class)); + //boolean isFunctionalInterface = ( type.isInterface() && type.isAnnotationPresent(FunctionalInterface.class)); final Package currentNS = (packageResolution) ? declaringClass.getPackage() : null; return new StringBuilder() .append( - type.getPackage().equals(currentNS) || isFunctionaInterface ? + type.getPackage().equals(currentNS) || isFunctionalInterface.test(type) ? type.getSimpleName() : type.getName() ) @@ -220,6 +267,119 @@ static String getName( Class type, Class declaringClass, boolean packageRe .toString(); } + + /** + * + * @param type + * @param declaringClass + * @param declaredClassMap + * @param packageResolution + * @return + * @throws Exception + */ + public static String convertJavaToTS( Type type, + Class declaringClass, + java.util.Map> declaredClassMap, + boolean packageResolution) + { + + + + if( type instanceof ParameterizedType ) { + + final ParameterizedType pType = (ParameterizedType) type; + + final Class rawType = (Class)pType.getRawType(); + + if( !declaredClassMap.containsKey(rawType.getName()) ) { + return format("any /*%s*/",rawType.getName()); + } + + String result = pType.getTypeName(); + + if( isFunctionalInterface.test(rawType) || (packageResolution && isPackageMatch.test(rawType, declaringClass)) ) { + result = result.replace( rawType.getName(), rawType.getSimpleName()); + } + + final Type[] typeArgs = pType.getActualTypeArguments(); + + for( Type t : typeArgs ) { + if( t instanceof ParameterizedType ) { + + final String typeName = convertJavaToTS( t, declaringClass, declaredClassMap, packageResolution); + log.accept(format( "Parameterized Type %s - %s", t, typeName )); + result = result.replace( t.getTypeName(), typeName); + + } + else if( t instanceof TypeVariable ) { + + log.accept(format( "type variable: %s", t )); + + final TypeVariable tv = (TypeVariable)t; + + if( !typeParameterMatch.apply(declaringClass, tv )) { + final String name = tv.getName(); + result = result.replaceAll( "<"+name, "", "any>") + ; + + } + continue; + } + else if( t instanceof Class ) { + log.accept(format( "class: %s", t.getTypeName() )); + + final String name = convertJavaToTS( (Class)t, declaringClass, declaredClassMap, packageResolution); + + result = result.replace(t.getTypeName(), name); + } + else if( t instanceof WildcardType ) { + //throw new IllegalArgumentException( format("type argument <%s> 'WildcardType' is a not supported yet!", t)); + result = result.replace(t.getTypeName(), format( "any/*%s*/", t)); + } + else if( t instanceof GenericArrayType ) { + throw new IllegalArgumentException( format("type argument <%s> 'GenericArrayType' is a not supported yet!", t)); + } + + } + + return result; + } + else if( type instanceof TypeVariable ) { + log.accept(format( "class: %s", type.getTypeName() )); + + final TypeVariable tv = (TypeVariable)type; + + if( !typeParameterMatch.apply(declaringClass, tv )) { + final String name = tv.getName(); + return format("any/*%s*/", name); + } + + return type.getTypeName(); + } + else if( type instanceof Class ) { + log.accept(format( "class: %s", type.getTypeName() )); + + final String name = convertJavaToTS( (Class)type, declaringClass, declaredClassMap, packageResolution); + return name; + } + else if( type instanceof WildcardType ) { + throw new IllegalArgumentException( "type 'WildcardType' is a not supported yet!"); + } + else if( type instanceof GenericArrayType ) { + final GenericArrayType t = (GenericArrayType)type; + log.accept(format( "generic array type: %s", t.getGenericComponentType().getTypeName() )); + //throw new IllegalArgumentException( format("type <%s> 'GenericArrayType' is a not supported yet!", type)); + + return ( typeParameterMatch.apply(declaringClass, (TypeVariable) t.getGenericComponentType() )) ? + format("[%s]", t.getGenericComponentType() ) : + format("[any/*%s*/]", t.getGenericComponentType() ); + } + + throw new IllegalArgumentException( "type is a not recognised type!"); + } + /** * * @param type @@ -228,10 +388,10 @@ static String getName( Class type, Class declaringClass, boolean packageRe * @param packageResolution * @return */ - static String convertJavaToTS( Class type, - Class declaringClass, - java.util.Map> declaredClassMap, - boolean packageResolution ) + private static String convertJavaToTS( Class type, + Class declaringClass, + java.util.Map> declaredClassMap, + boolean packageResolution ) { if( type == null ) return "any"; @@ -251,10 +411,10 @@ static String convertJavaToTS( Class type, if( type.isArray()) return format("[any] /* %s */",type.getName()); if( declaredClassMap.containsKey(type.getName()) ) { - return getName(type, declaringClass, packageResolution); + return getTypeName(type, declaringClass, packageResolution); } - return format("any /* %s */",type.getName()); + return format("any /*%s*/",type.getName()); } diff --git a/processor/src/main/java/org/bsc/processor/TypescriptProcessor.java b/processor/src/main/java/org/bsc/processor/TypescriptProcessor.java index 1379d32..4800ed0 100644 --- a/processor/src/main/java/org/bsc/processor/TypescriptProcessor.java +++ b/processor/src/main/java/org/bsc/processor/TypescriptProcessor.java @@ -2,9 +2,10 @@ import static org.bsc.processor.TypescriptHelper.convertJavaToTS; import static org.bsc.processor.TypescriptHelper.getClassDecl; -import static org.bsc.processor.TypescriptHelper.getName; +import static org.bsc.processor.TypescriptHelper.getTypeName; import static org.bsc.processor.TypescriptHelper.getSimpleName; import static org.bsc.processor.TypescriptHelper.isStaticMethod; +import static org.bsc.processor.TypescriptHelper.getParameterName; import java.beans.PropertyDescriptor; import java.io.Closeable; @@ -193,7 +194,7 @@ private String getPropertyDecl( Class declaringClass, final String typeName = pClass.getTypeName(); try { - final String name = getName( pClass, pd.getPropertyType(), true); + final String name = getTypeName( pClass, pd.getPropertyType(), true); final String r = rType.getTypeName() .replaceAll(typeName, name) @@ -230,24 +231,24 @@ private String getPropertyDecl( Class declaringClass, * @param declaredClassMap * @return */ - private String getMethodParametersDecl( Method m, - Class declaringClass, - java.util.Map> declaredClassMap, - boolean packageResolution ) + protected String getMethodParametersDecl( Method m, + Class declaringClass, + java.util.Map> declaredClassMap, + boolean packageResolution ) { - final Class returnType = m.getReturnType(); - final Parameter[] params = m.getParameters(); final String params_string = Arrays.stream(params) .map( (tp) -> String.format( "%s:%s", - tp.getName(), + getParameterName(tp), convertJavaToTS(tp.getType(),declaringClass,declaredClassMap, packageResolution) ) ) .collect(Collectors.joining(", ")) ; + final Type returnType = m.getGenericReturnType(); + final String tsType = convertJavaToTS( returnType, declaringClass, diff --git a/processor/src/test/java/org/bsc/processor/RefrectionTest.java b/processor/src/test/java/org/bsc/processor/RefrectionTest.java new file mode 100644 index 0000000..1408ab9 --- /dev/null +++ b/processor/src/test/java/org/bsc/processor/RefrectionTest.java @@ -0,0 +1,109 @@ +package org.bsc.processor; + +import java.lang.reflect.Method; +import java.lang.reflect.Type; +import java.util.Collections; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.hamcrest.core.IsEqual; +import org.hamcrest.core.IsNull; +import org.junit.Assert; +import org.junit.Test; + +public class RefrectionTest { + + final TypescriptProcessor processor = new TypescriptProcessor(); + + private java.util.Map> declaredClassMap( Class ... classes) { + return Stream.of( classes ) + .collect( Collectors.toMap( c -> c.getName(), c -> c) ) + ; + } + + @Test + public void testSample1() throws Exception { + + final Class type = Sample1.class; + + Stream.of(type.getTypeParameters()).forEach( v -> System.out.println(v.getName())) ; + + + { + final Method m = type.getMethod("method4"); + final Type rType = m.getGenericReturnType(); + final String result = TypescriptHelper.convertJavaToTS(rType, type, Collections.emptyMap(), true); + Assert.assertThat( result, IsNull.notNullValue()); + Assert.assertThat( result, IsEqual.equalTo("any/*C*/")); + } + { + final Method m = type.getMethod("method5"); + final Type rType = m.getGenericReturnType(); + final String result = TypescriptHelper.convertJavaToTS(rType, type, Collections.emptyMap(), true); + Assert.assertThat( result, IsNull.notNullValue()); + Assert.assertThat( result, IsEqual.equalTo("any/*C*/")); + } + { + final Method m = type.getMethod("method1_1"); + final Type rType = m.getGenericReturnType(); + final String result = TypescriptHelper.convertJavaToTS(rType, type, + declaredClassMap(Sample2.class), + true); + Assert.assertThat( result, IsNull.notNullValue()); + Assert.assertThat( result, IsEqual.equalTo("Sample2")); + } + { + final Method m = type.getMethod("method1_2"); + final Type rType = m.getGenericReturnType(); + final String result = TypescriptHelper.convertJavaToTS(rType, type, + declaredClassMap(Sample2.class,java.lang.Comparable.class), + true); + Assert.assertThat( result, IsNull.notNullValue()); + Assert.assertThat( result, IsEqual.equalTo("java.lang.Comparable>")); + } + { + final Method m = type.getMethod("method1_3"); + final Type rType = m.getGenericReturnType(); + final String result = TypescriptHelper.convertJavaToTS(rType, type, + declaredClassMap(java.util.function.BiPredicate.class), + true); + Assert.assertThat( result, IsNull.notNullValue()); + Assert.assertThat( result, IsEqual.equalTo("BiPredicate")); + } + { + final Method m = type.getMethod("method1_3"); + final Type rType = m.getGenericReturnType(); + final String result = TypescriptHelper.convertJavaToTS(rType, type, + Collections.emptyMap(), + true); + Assert.assertThat( result, IsNull.notNullValue()); + Assert.assertThat( result, IsEqual.equalTo("any /*java.util.function.BiPredicate*/")); + } + { + final Method m = type.getMethod("method1"); + final Type rType = m.getGenericReturnType(); + final String result = TypescriptHelper.convertJavaToTS(rType, type, + declaredClassMap(java.util.Map.class), + true); + Assert.assertThat( result, IsNull.notNullValue()); + Assert.assertThat( result, IsEqual.equalTo("java.util.Map")); + } + + { + final Method m = type.getMethod("method2"); + final Type rType = m.getGenericReturnType(); + final String result = TypescriptHelper.convertJavaToTS(rType, type, Collections.emptyMap(), true); + Assert.assertThat( result, IsNull.notNullValue()); + Assert.assertThat( result, IsEqual.equalTo("string")); + } + + { + final Method m = type.getMethod("method3"); + final Type rType = m.getGenericReturnType(); + final String result = TypescriptHelper.convertJavaToTS(rType, type, Collections.emptyMap(), true); + Assert.assertThat( result, IsNull.notNullValue()); + Assert.assertThat( result, IsEqual.equalTo("E")); + } + + } +} diff --git a/processor/src/test/java/org/bsc/processor/Sample1.java b/processor/src/test/java/org/bsc/processor/Sample1.java new file mode 100644 index 0000000..9cc265e --- /dev/null +++ b/processor/src/test/java/org/bsc/processor/Sample1.java @@ -0,0 +1,23 @@ +package org.bsc.processor; + +import java.util.concurrent.Future; +import java.util.function.BiPredicate; + +public interface Sample1 { + + java.util.Map method1(); + + Sample2 method1_1(); + + Comparable> method1_2(); + + BiPredicate method1_3(); + + String method2(); + + E method3(); + + C method4(); + + > C method5(); +} diff --git a/processor/src/test/java/org/bsc/processor/Sample2.java b/processor/src/test/java/org/bsc/processor/Sample2.java new file mode 100644 index 0000000..bd38dc4 --- /dev/null +++ b/processor/src/test/java/org/bsc/processor/Sample2.java @@ -0,0 +1,6 @@ +package org.bsc.processor; + +public interface Sample2 { + + T method1(); +}