package cz.habarta.typescript.generator;
import cz.habarta.typescript.generator.parser.JaxrsApplicationParser;
import cz.habarta.typescript.generator.util.Utils;
import java.lang.reflect.*;
import java.math.*;
import java.util.*;
public class DefaultTypeProcessor implements TypeProcessor {
@Override
public Result processType(Type javaType, Context context) {
if (KnownTypes.containsKey(javaType)) return new Result(KnownTypes.get(javaType));
// map JAX-RS standard types to `any`
for (Class<?> cls : JaxrsApplicationParser.getStandardEntityClasses()) {
final Class<?> rawClass = Utils.getRawClassOrNull(javaType);
if (rawClass != null && cls.isAssignableFrom(rawClass)) {
return new Result(TsType.Any);
}
}
if (javaType instanceof Class) {
final Class<?> javaClass = (Class<?>) javaType;
if (javaClass.isArray()) {
final Result result = context.processType(javaClass.getComponentType());
return new Result(new TsType.BasicArrayType(result.getTsType()), result.getDiscoveredClasses());
}
if (javaClass.isEnum()) {
return new Result(new TsType.EnumReferenceType(context.getSymbol(javaClass)), javaClass);
}
if (Collection.class.isAssignableFrom(javaClass)) {
return new Result(new TsType.BasicArrayType(TsType.Any));
}
if (Map.class.isAssignableFrom(javaClass)) {
return new Result(new TsType.IndexedArrayType(TsType.String, TsType.Any));
}
if (javaClass.getName().equals("java.util.OptionalInt") ||
javaClass.getName().equals("java.util.OptionalLong") ||
javaClass.getName().equals("java.util.OptionalDouble")) {
return new Result(TsType.Number);
}
// generic structural type used without type arguments
if (javaClass.getTypeParameters().length > 0) {
final List<TsType> tsTypeArguments = new ArrayList<>();
for (int i = 0; i < javaClass.getTypeParameters().length; i++) {
tsTypeArguments.add(TsType.Any);
}
return new Result(new TsType.GenericReferenceType(context.getSymbol(javaClass), tsTypeArguments));
}
// structural type
return new Result(new TsType.ReferenceType(context.getSymbol(javaClass)), javaClass);
}
if (javaType instanceof ParameterizedType) {
final ParameterizedType parameterizedType = (ParameterizedType) javaType;
if (parameterizedType.getRawType() instanceof Class) {
final Class<?> javaClass = (Class<?>) parameterizedType.getRawType();
if (Collection.class.isAssignableFrom(javaClass)) {
final Result result = context.processType(parameterizedType.getActualTypeArguments()[0]);
return new Result(new TsType.BasicArrayType(result.getTsType()), result.getDiscoveredClasses());
}
if (Map.class.isAssignableFrom(javaClass)) {
final Result result = context.processType(parameterizedType.getActualTypeArguments()[1]);
return new Result(new TsType.IndexedArrayType(TsType.String, result.getTsType()), result.getDiscoveredClasses());
}
if (javaClass.getName().equals("java.util.Optional")) {
return context.processType(parameterizedType.getActualTypeArguments()[0]);
}
// generic structural type
final List<Class<?>> discoveredClasses = new ArrayList<>();
discoveredClasses.add(javaClass);
final List<TsType> tsTypeArguments = new ArrayList<>();
for (Type typeArgument : parameterizedType.getActualTypeArguments()) {
final TypeProcessor.Result typeArgumentResult = context.processType(typeArgument);
tsTypeArguments.add(typeArgumentResult.getTsType());
discoveredClasses.addAll(typeArgumentResult.getDiscoveredClasses());
}
return new Result(new TsType.GenericReferenceType(context.getSymbol(javaClass), tsTypeArguments), discoveredClasses);
}
}
if (javaType instanceof GenericArrayType) {
final GenericArrayType genericArrayType = (GenericArrayType) javaType;
final Result result = context.processType(genericArrayType.getGenericComponentType());
return new Result(new TsType.BasicArrayType(result.getTsType()), result.getDiscoveredClasses());
}
if (javaType instanceof TypeVariable) {
final TypeVariable<?> typeVariable = (TypeVariable<?>) javaType;
return new Result(new TsType.GenericVariableType(typeVariable.getName()));
}
if (javaType instanceof WildcardType) {
final WildcardType wildcardType = (WildcardType) javaType;
final Type[] upperBounds = wildcardType.getUpperBounds();
return upperBounds.length > 0
? context.processType(upperBounds[0])
: new Result(TsType.Any);
}
return null;
}
private static Map<Type, TsType> getKnownTypes() {
final Map<Type, TsType> knownTypes = new LinkedHashMap<>();
// java.lang
knownTypes.put(Object.class, TsType.Any);
knownTypes.put(Byte.class, TsType.Number);
knownTypes.put(Byte.TYPE, TsType.Number);
knownTypes.put(Short.class, TsType.Number);
knownTypes.put(Short.TYPE, TsType.Number);
knownTypes.put(Integer.class, TsType.Number);
knownTypes.put(Integer.TYPE, TsType.Number);
knownTypes.put(Long.class, TsType.Number);
knownTypes.put(Long.TYPE, TsType.Number);
knownTypes.put(Float.class, TsType.Number);
knownTypes.put(Float.TYPE, TsType.Number);
knownTypes.put(Double.class, TsType.Number);
knownTypes.put(Double.TYPE, TsType.Number);
knownTypes.put(Boolean.class, TsType.Boolean);
knownTypes.put(Boolean.TYPE, TsType.Boolean);
knownTypes.put(Character.class, TsType.String);
knownTypes.put(Character.TYPE, TsType.String);
knownTypes.put(String.class, TsType.String);
knownTypes.put(void.class, TsType.Void);
knownTypes.put(Void.class, TsType.Void);
// other java packages
knownTypes.put(BigDecimal.class, TsType.Number);
knownTypes.put(BigInteger.class, TsType.Number);
knownTypes.put(Date.class, TsType.Date);
knownTypes.put(UUID.class, TsType.String);
// joda time (if present)
try {
final Class<?> jodaTimeClass = Class.forName("org.joda.time.DateTime");
knownTypes.put(jodaTimeClass, TsType.Date);
} catch (ClassNotFoundException e) {
// ignore if joda time is not present
}
return knownTypes;
}
private static final Map<Type, TsType> KnownTypes = getKnownTypes();
}