diff --git a/core/pom.xml b/core/pom.xml
new file mode 100644
index 0000000..9405746
--- /dev/null
+++ b/core/pom.xml
@@ -0,0 +1,10 @@
+
+ 4.0.0
+
+ org.bsc.processor
+ java2ts-processor-parent
+ 1.0.0-SNAPSHOT
+
+ java2ts-processor-core
+ java2ts-processor::core - ${project.version}
+
\ No newline at end of file
diff --git a/processor/src/main/java/org/bsc/processor/TSType.java b/core/src/main/java/org/bsc/java2typescript/TSType.java
similarity index 63%
rename from processor/src/main/java/org/bsc/processor/TSType.java
rename to core/src/main/java/org/bsc/java2typescript/TSType.java
index c532f30..d420936 100644
--- a/processor/src/main/java/org/bsc/processor/TSType.java
+++ b/core/src/main/java/org/bsc/java2typescript/TSType.java
@@ -1,6 +1,13 @@
-package org.bsc.processor;
+package org.bsc.java2typescript;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
import java.util.HashMap;
+import java.util.Set;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
/**
@@ -45,7 +52,7 @@ public static TSType functional( Class> cl, String alias ) {
}};
}
- protected static TSType from( Class> cl ) {
+ public static TSType from( Class> cl ) {
return new TSType() {{ put( "value", cl); }};
}
@@ -115,6 +122,45 @@ public final String getTypeName() {
public final String getSimpleTypeName() {
return (hasAlias()) ? getAlias() : getValue().getSimpleName();
}
+
+ /**
+ *
+ * @param type
+ * @return
+ */
+ public Set getFields() {
+
+ final Predicate std = f ->
+ !f.isSynthetic() &&
+ Modifier.isPublic( f.getModifiers() ) &&
+ Character.isJavaIdentifierStart(f.getName().charAt(0)) &&
+ f.getName().chars().skip(1).allMatch(Character::isJavaIdentifierPart);
+
+ return Stream.concat( Stream.of(getValue().getFields()), Stream.of(getValue().getDeclaredFields()) )
+ .filter(std)
+ .collect( Collectors.toSet( ) );
+
+ }
+
+ /**
+ *
+ * @param type
+ * @return
+ */
+ public Set getMethods() {
+ final Predicate include = m ->
+ !m.isBridge() &&
+ !m.isSynthetic() &&
+ Modifier.isPublic( m.getModifiers() ) &&
+ Character.isJavaIdentifierStart(m.getName().charAt(0)) &&
+ m.getName().chars().skip(1).allMatch(Character::isJavaIdentifierPart);
+
+ return Stream.concat( Stream.of(getValue().getMethods()), Stream.of(getValue().getDeclaredMethods()) )
+ .filter(include)
+ .collect( Collectors.toSet( ) );
+
+ }
+
/**
*
diff --git a/core/src/main/java/org/bsc/java2typescript/TypescriptConverter.java b/core/src/main/java/org/bsc/java2typescript/TypescriptConverter.java
new file mode 100644
index 0000000..21f6a12
--- /dev/null
+++ b/core/src/main/java/org/bsc/java2typescript/TypescriptConverter.java
@@ -0,0 +1,875 @@
+package org.bsc.java2typescript;
+
+import static java.lang.String.format;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Executable;
+import java.lang.reflect.GenericArrayType;
+import java.lang.reflect.Member;
+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.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.BiFunction;
+import java.util.function.BiPredicate;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+public class TypescriptConverter {
+
+ final static String ENDL = ";\n";
+
+
+ /**
+ *
+ */
+ public static BiPredicate, Class>> isPackageMatch = (a, b) ->
+ a.getPackage().equals(b.getPackage()) ;
+
+ /**
+ *
+ */
+ private static BiFunction,Type, Boolean> typeParameterMatch = (declaringClass, type) ->
+ ( type instanceof TypeVariable ) ?
+ Arrays.stream(declaringClass.getTypeParameters())
+ .map( (tp) -> tp.getName())
+ .anyMatch( name -> name.equals(((TypeVariable>)type).getName())) :
+ false
+ ;
+
+ private final static void log( String fmt, Object ...args ) {
+ //System.out.println( format( fmt, (Object[])args));
+ }
+
+ private final static void debug( String fmt, Object ...args ) {
+ System.out.print( "DEBUG: ");
+ System.out.println( format( fmt, (Object[])args));
+ }
+
+ /**
+ *
+ * @param type
+ * @return
+ */
+ public static final String processFunctionalInterface( TSType type ) {
+ Objects.requireNonNull(type, "argument 'type' is not defined!");
+
+ return null;
+ }
+
+ /**
+ *
+ * @param type
+ * @param alias
+ * @return
+ */
+ public static final String getAliasDeclaration( Class> type, String alias ) {
+ Objects.requireNonNull(type, "argument 'type' is not defined!");
+ Objects.requireNonNull(alias, "argument 'alias' is not defined!");
+
+ final TypeVariable>[] typeParameters = type.getTypeParameters();
+
+ if( typeParameters!=null && typeParameters.length > 0 ) {
+
+ final String pp = Arrays.stream(typeParameters).map( tp -> tp.getName() ).collect( Collectors.joining(",","<", ">"));
+ return format( "type %s%s = %s%s;\n\n", alias, pp, type.getName(), pp );
+
+ }
+
+ return format( "type %s = %s;\n\n", alias, type.getName() );
+ }
+
+ /**
+ *
+ * @param p
+ * @return
+ */
+ public static final String getParameterName( Parameter p ) {
+ final String name = p.getName();
+
+ switch( name ) {
+ case "function":
+ return "func";
+ default:
+ return name;
+ }
+ }
+
+ /**
+ *
+ * @param m
+ * @return
+ */
+ public static boolean isStatic( M m ) {
+
+ final int modifier = m.getModifiers();
+
+ return (Modifier.isStatic( modifier) &&
+ Modifier.isPublic( modifier )) ;
+ }
+
+ /**
+ *
+ * @param m
+ * @return
+ */
+ static boolean isFactoryMethod( Method m ) {
+
+ return (isStatic(m) &&
+ m.getReturnType().equals(m.getDeclaringClass()));
+ }
+
+
+ /**
+ *
+ * @param type_parameters_list
+ * @return
+ */
+ static String getClassParametersDecl( java.util.List type_parameters_list ) {
+
+ if( type_parameters_list.isEmpty() ) return "";
+
+ return format("<%s>", type_parameters_list
+ .stream()
+ .collect( Collectors.joining(", ")) );
+ }
+
+ /**
+ *
+ * @param type
+ * @param declaringType
+ * @return
+ */
+ static String getTypeName( TSType type, TSType declaringType, boolean packageResolution ) {
+
+ final java.util.List dc_parameters_list =
+ Arrays.stream(declaringType.getValue().getTypeParameters())
+ .map( (tp) -> tp.getName())
+ .collect(Collectors.toList());
+
+ final java.util.List type_parameters_list =
+ Arrays.stream(type.getValue().getTypeParameters())
+ .map( tp -> (dc_parameters_list.contains(tp.getName()) ) ? tp.getName() : "any" )
+ .collect(Collectors.toList());
+
+ final java.util.List parameters =
+ dc_parameters_list.size() == type_parameters_list.size() ? dc_parameters_list : type_parameters_list ;
+
+ final Package currentNS = (packageResolution) ? declaringType.getValue().getPackage() : null;
+
+ return new StringBuilder()
+ .append(
+ type.getValue().getPackage().equals(currentNS) || type.isFunctionalInterface() ?
+ type.getSimpleTypeName() :
+ type.getTypeName()
+ )
+ .append( getClassParametersDecl(parameters) )
+ .toString();
+ }
+
+ /**
+ *
+ * @param type
+ * @param declaringMember
+ * @param declaredTypeMap
+ * @param packageResolution
+ * @param typeMatch
+ * @param onTypeMismatch
+ * @return
+ */
+ public static String convertJavaToTS( Type type,
+ M declaringMember,
+ TSType declaringType,
+ java.util.Map declaredTypeMap,
+ boolean packageResolution,
+ Optional>> onTypeMismatch)
+ {
+ Objects.requireNonNull(type, "Type argument is null!");
+ Objects.requireNonNull(declaringMember, "declaringMethod argument is null!");
+ Objects.requireNonNull(declaringType, "declaringType argument is null!");
+ Objects.requireNonNull(declaredTypeMap, "declaredTypeMap argument is null!");
+
+ /**
+ *
+ */
+ final Predicate> typeMismatch = ( tv ) -> {
+ if( isStatic(declaringMember) ) return true;
+ if( declaringMember instanceof Constructor ) return true;
+ return !typeParameterMatch.apply(declaringType.getValue(), tv );
+ };
+
+ if( type instanceof ParameterizedType ) {
+
+ final ParameterizedType pType = (ParameterizedType) type;
+
+ final Class> rawType = (Class>)pType.getRawType();
+
+ final TSType tstype = declaredTypeMap.get(rawType.getName());
+ if( tstype==null ) {
+ return format("any /*%s*/",rawType.getName());
+ }
+
+ String result = pType.getTypeName()
+ .replace( rawType.getName(), tstype.getTypeName()) // use Alias
+ ;
+
+ if( tstype.isFunctionalInterface() || (packageResolution && isPackageMatch.test(tstype.getValue(), declaringType.getValue())) ) {
+ result = result.replace( tstype.getTypeName(), tstype.getSimpleTypeName());
+ }
+
+ final Type[] typeArgs = pType.getActualTypeArguments();
+
+ for( Type t : typeArgs ) {
+ if( t instanceof ParameterizedType ) {
+
+ final String typeName = convertJavaToTS( t,
+ declaringMember,
+ declaringType,
+ declaredTypeMap,
+ packageResolution,
+ onTypeMismatch);
+ log( "Parameterized Type %s - %s", t, typeName );
+ result = result.replace( t.getTypeName(), typeName);
+
+ }
+ else if( t instanceof TypeVariable ) {
+
+ log( "type variable: %s", t );
+
+ final TypeVariable> tv = (TypeVariable>)t;
+
+ if( typeMismatch.test(tv)) {
+
+ if( onTypeMismatch.isPresent() ) {
+ onTypeMismatch.get().accept(tv);
+ }
+ else {
+ final String name = tv.getName();
+ result = result.replaceAll( "<"+name, "", "any>")
+ ;
+ }
+ }
+ continue;
+ }
+ else if( t instanceof Class ) {
+ log( "class: %s", t.getTypeName() );
+
+ final String name = convertJavaToTS( (Class>)t, declaringType, declaredTypeMap, packageResolution);
+
+ final String commented = format("/*%s*/", t.getTypeName());
+ result = result.replace( commented, "/*@*/")
+ .replace(t.getTypeName(), name)
+ .replace( "/*@*/", commented )
+ ;
+ }
+ else if( t instanceof WildcardType ) {
+ final WildcardType wt = (WildcardType) t;
+
+ final Type[] lb = wt.getLowerBounds();
+ final Type[] ub = wt.getUpperBounds();
+
+ log( "Wildcard Type : %s lb:%d up:%d", type.getTypeName(), lb.length, ub.length );
+
+ if( lb.length <= 1 && ub.length==1) {
+ final Type tt = (lb.length==1) ? lb[0] : ub[0];
+
+ final String s = convertJavaToTS( tt,
+ declaringMember,
+ declaringType,
+ declaredTypeMap,
+ packageResolution,
+ onTypeMismatch);
+
+ result = result.replace( wt.getTypeName(), s);
+
+ // CHECK FOR NESTED WILDCARDTYPE
+ if( tt instanceof ParameterizedType &&
+ Stream.of((Type[])((ParameterizedType)tt).getActualTypeArguments())
+ .anyMatch( arg -> (arg instanceof WildcardType) ))
+ {
+ final Class> clazz = (Class>) (((ParameterizedType)tt).getRawType());
+ final String typeName = wt.getTypeName().replace( clazz.getName(), clazz.getSimpleName());
+ result = result.replace( typeName, s);
+ }
+ }
+ else {
+ result = result.replace(wt.getTypeName(), format( "any/*%s*/", wt));
+ }
+ }
+ 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( "class: %s", type.getTypeName() );
+
+ final TypeVariable> tv = (TypeVariable>)type;
+
+ if( typeMismatch.test(tv) ) {
+
+ final String name = tv.getName();
+
+ if( onTypeMismatch.isPresent() ) {
+ onTypeMismatch.get().accept(tv);
+ return name;
+ }
+
+ return format("any/*%s*/", name);
+ }
+
+ return type.getTypeName();
+ }
+ else if( type instanceof Class ) {
+ log( "class: %s", type.getTypeName() );
+
+ final String name = convertJavaToTS( (Class>)type, declaringType, declaredTypeMap, 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;
+
+ final Type componentType = t.getGenericComponentType();
+
+ log( "generic array type: %s", componentType.getTypeName() );
+
+ final String tt = convertJavaToTS( componentType,
+ declaringMember,
+ declaringType,
+ declaredTypeMap,
+ packageResolution,
+ onTypeMismatch);
+ return format("[%s]", tt);
+
+
+ //return ( typeParameterMatch.apply(declaringType.getValue(), componentType )) ?
+ // format("[%s]", componentType ) :
+ // format("[any/*%s*/]", componentType );
+
+ }
+
+ throw new IllegalArgumentException( "type is a not recognised type!");
+ }
+
+ /**
+ *
+ * @param type
+ * @param declaringType
+ * @param declaredTypeMap
+ * @param packageResolution
+ * @return
+ */
+ private static String convertJavaToTS( Class> type,
+ TSType declaringType,
+ java.util.Map declaredTypeMap,
+ boolean packageResolution )
+ {
+
+ if( type == null ) return "any";
+
+ if( Void.class.isAssignableFrom(type) || type.equals(Void.TYPE) ) return "void";
+ if( Boolean.class.isAssignableFrom(type) || type.equals(Boolean.TYPE) ) return type.isPrimitive() ? "boolean" : "boolean|null" ;
+ if( Integer.class.isAssignableFrom(type) || type.equals(Integer.TYPE)) return type.isPrimitive() ? "int" : "int|null" ;
+ if( Long.class.isAssignableFrom(type) || type.equals(Long.TYPE)) return type.isPrimitive() ? "long" : "long|null" ;
+ if( Float.class.isAssignableFrom(type) || type.equals(Float.TYPE)) return type.isPrimitive() ? "float" : "float|null" ;
+ if( Double.class.isAssignableFrom(type) || type.equals(Double.TYPE)) return type.isPrimitive() ? "double" : "double|null" ;
+ if( Integer.class.isAssignableFrom(type) || type.equals(Integer.TYPE)) return type.isPrimitive() ? "int" : "int|null" ;
+ if( String.class.isAssignableFrom(type) ) return "string";
+
+ if( char[].class.equals(type) ) return "chararray";
+ if( byte[].class.equals(type) ) return "bytearray";
+
+ if( type.isArray()) {
+ return format( "[%s]", convertJavaToTS(type.getComponentType(), declaringType, declaredTypeMap,packageResolution));
+ }
+
+ final TSType tt = declaredTypeMap.get( type.getName() );
+ if( tt!=null ) {
+ return getTypeName(tt, declaringType, packageResolution);
+ }
+
+ return format("any /*%s*/",type.getName());
+
+ }
+
+ /**
+ *
+ * @param keyExtractor
+ * @return
+ */
+ public static Predicate distinctByKey(Function super T,Object> keyExtractor) {
+ Map