package cz.habarta.typescript.generator.parser;
import cz.habarta.typescript.generator.compiler.EnumMemberModel;
import cz.habarta.typescript.generator.compiler.EnumKind;
import cz.habarta.typescript.generator.compiler.SymbolTable;
import cz.habarta.typescript.generator.*;
import java.lang.reflect.Member;
import java.lang.reflect.Type;
import java.util.*;
public abstract class ModelParser {
protected final Settings settings;
protected final TypeProcessor typeProcessor;
private final Javadoc javadoc;
private final Queue<SourceType<? extends Type>> typeQueue = new LinkedList<>();
public ModelParser(Settings settings, TypeProcessor typeProcessor) {
this.settings = settings;
this.typeProcessor = typeProcessor;
this.javadoc = new Javadoc(settings.javadocXmlFiles);
}
public Model parseModel(Type type) {
return parseModel(Arrays.asList(new SourceType<>(type)));
}
public Model parseModel(List<SourceType<Type>> types) {
typeQueue.addAll(types);
final Model model = parseQueue();
final Model modelWithSwaggerDoc = Swagger.enrichModel(model);
final Model modelWithJavadoc = javadoc.enrichModel(modelWithSwaggerDoc);
return modelWithJavadoc;
}
private Model parseQueue() {
final JaxrsApplicationParser jaxrsApplicationParser = new JaxrsApplicationParser(settings);
final Collection<Type> parsedTypes = new ArrayList<>(); // do not use hashcodes, we can only count on `equals` since we use custom `ParameterizedType`s
final List<BeanModel> beans = new ArrayList<>();
final List<EnumModel<?>> enums = new ArrayList<>();
SourceType<? extends Type> sourceType;
while ((sourceType = typeQueue.poll()) != null) {
if (parsedTypes.contains(sourceType.type)) {
continue;
}
parsedTypes.add(sourceType.type);
// JAX-RS resource
final JaxrsApplicationParser.Result jaxrsResult = jaxrsApplicationParser.tryParse(sourceType);
if (jaxrsResult != null) {
typeQueue.addAll(jaxrsResult.discoveredTypes);
continue;
}
final TypeProcessor.Result result = processType(sourceType.type);
if (result != null) {
if (sourceType.type instanceof Class<?> && result.getTsType() instanceof TsType.ReferenceType) {
final Class<?> cls = (Class<?>) sourceType.type;
System.out.println("Parsing '" + cls.getName() + "'" +
(sourceType.usedInClass != null ? " used in '" + sourceType.usedInClass.getSimpleName() + "." + sourceType.usedInMember + "'" : ""));
final DeclarationModel model = parseClass(sourceType.asSourceClass());
if (model instanceof EnumModel) {
enums.add((EnumModel) model);
} else if (model instanceof BeanModel) {
beans.add((BeanModel) model);
} else {
throw new RuntimeException();
}
}
for (Class<?> cls : result.getDiscoveredClasses()) {
typeQueue.add(new SourceType<>(cls, sourceType.usedInClass, sourceType.usedInMember));
}
}
}
return new Model(beans, enums, jaxrsApplicationParser.getModel());
}
protected abstract DeclarationModel parseClass(SourceType<Class<?>> sourceClass);
protected static DeclarationModel parseEnum(SourceType<Class<?>> sourceClass) {
final List<EnumMemberModel<String>> values = new ArrayList<>();
if (sourceClass.type.isEnum()) {
@SuppressWarnings("unchecked")
final Class<? extends Enum<?>> enumClass = (Class<? extends Enum<?>>) sourceClass.type;
for (Enum<?> enumConstant : enumClass.getEnumConstants()) {
values.add(new EnumMemberModel<>(enumConstant.name(), enumConstant.name(), null));
}
}
return new EnumModel<>(sourceClass.type, EnumKind.StringBased, values, null);
}
protected void addBeanToQueue(SourceType<? extends Type> sourceType) {
typeQueue.add(sourceType);
}
protected PropertyModel processTypeAndCreateProperty(String name, Type type, boolean optional, Class<?> usedInClass, Member originalMember, PropertyModel.PullProperties pullProperties) {
List<Class<?>> classes = discoverClassesUsedInType(type);
for (Class<?> cls : classes) {
typeQueue.add(new SourceType<>(cls, usedInClass, name));
}
return new PropertyModel(name, type, optional, originalMember, pullProperties, null);
}
private List<Class<?>> discoverClassesUsedInType(Type type) {
final TypeProcessor.Result result = processType(type);
return result != null ? result.getDiscoveredClasses() : Collections.<Class<?>>emptyList();
}
private TypeProcessor.Result processType(Type type) {
return typeProcessor.processType(type, new TypeProcessor.Context(new SymbolTable(settings), typeProcessor));
}
public static boolean containsProperty(List<PropertyModel> properties, String propertyName) {
for (PropertyModel property : properties) {
if (property.getName().equals(propertyName)) {
return true;
}
}
return false;
}
}