Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(java): Separate concerns between spec and class generation for … #5568

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,111 +15,35 @@
*/
package com.fern.java.generators;

import com.fern.ir.model.commons.TypeId;
import com.fern.ir.model.types.DeclaredTypeName;
import com.fern.ir.model.types.ObjectProperty;
import com.fern.ir.model.types.ObjectTypeDeclaration;
import com.fern.java.AbstractGeneratorContext;
import com.fern.java.PoetTypeNameMapper;
import com.fern.java.generators.object.EnrichedObjectProperty;
import com.fern.java.generators.object.ImplementsInterface;
import com.fern.java.generators.object.ObjectTypeSpecGenerator;
import com.fern.java.output.GeneratedJavaInterface;
import com.fern.java.output.GeneratedObject;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.TypeSpec;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Queue;
import java.util.Set;
import java.util.stream.Collectors;

public final class ObjectGenerator extends AbstractFileGenerator {
private final ObjectTypeDeclaration objectTypeDeclaration;
private final Optional<GeneratedJavaInterface> selfInterface;
private final Map<TypeId, GeneratedJavaInterface> allGeneratedInterfaces;
private final List<GeneratedJavaInterface> extendedInterfaces = new ArrayList<>();
private final TypeSpec objectTypeSpec;
private final Map<ObjectProperty, EnrichedObjectProperty> objectPropertyGetters;
private final List<EnrichedObjectProperty> extendedPropertyGetters;

public ObjectGenerator(
ObjectTypeDeclaration objectTypeDeclaration,
Optional<GeneratedJavaInterface> selfInterface,
List<GeneratedJavaInterface> extendedInterfaces,
AbstractGeneratorContext<?, ?> generatorContext,
Map<TypeId, GeneratedJavaInterface> allGeneratedInterfaces,
ClassName className) {
ClassName className,
TypeSpec objectTypeSpec,
Map<ObjectProperty, EnrichedObjectProperty> objectPropertyGetters,
List<EnrichedObjectProperty> extendedPropertyGetters) {
super(className, generatorContext);
this.objectTypeDeclaration = objectTypeDeclaration;
this.selfInterface = selfInterface;
selfInterface.ifPresent(this.extendedInterfaces::add);
this.extendedInterfaces.addAll(extendedInterfaces);
this.allGeneratedInterfaces = allGeneratedInterfaces;
this.objectTypeSpec = objectTypeSpec;
this.objectPropertyGetters = objectPropertyGetters;
this.extendedPropertyGetters = extendedPropertyGetters;
}

@Override
public GeneratedObject generateFile() {
PoetTypeNameMapper poetTypeNameMapper = generatorContext.getPoetTypeNameMapper();
List<EnrichedObjectProperty> enrichedObjectProperties = new ArrayList<>();
Map<ObjectProperty, EnrichedObjectProperty> objectPropertyGetters = new HashMap<>();
List<EnrichedObjectProperty> extendedPropertyGetters = new ArrayList<>();
if (selfInterface.isEmpty()) {
enrichedObjectProperties = objectTypeDeclaration.getProperties().stream()
.map(objectProperty -> {
EnrichedObjectProperty enrichedObjectProperty = EnrichedObjectProperty.of(
objectProperty,
false,
poetTypeNameMapper.convertToTypeName(true, objectProperty.getValueType()));
objectPropertyGetters.put(objectProperty, enrichedObjectProperty);
return enrichedObjectProperty;
})
.collect(Collectors.toList());
}
List<ImplementsInterface> implementsInterfaces = new ArrayList<>();
Set<GeneratedJavaInterface> visited = new HashSet<>();
extendedInterfaces.stream()
.map(generatedInterface -> {
List<EnrichedObjectProperty> enrichedProperties = new ArrayList<>();
Queue<GeneratedJavaInterface> interfaceQueue = new LinkedList<>();
interfaceQueue.add(generatedInterface);
while (!interfaceQueue.isEmpty()) {
GeneratedJavaInterface generatedJavaInterface = interfaceQueue.poll();
if (visited.contains(generatedJavaInterface)) {
continue;
}
interfaceQueue.addAll(generatedJavaInterface.extendedInterfaces().stream()
.map(DeclaredTypeName::getTypeId)
.map(allGeneratedInterfaces::get)
.collect(Collectors.toList()));
enrichedProperties.addAll(
getEnrichedObjectProperties(generatedJavaInterface, objectPropertyGetters));
visited.add(generatedJavaInterface);
}
return ImplementsInterface.builder()
.interfaceClassName(generatedInterface.getClassName())
.addAllInterfaceProperties(enrichedProperties)
.build();
})
.forEach(implementsInterface -> {
extendedPropertyGetters.addAll(implementsInterface.interfaceProperties());
implementsInterfaces.add(implementsInterface);
});
ObjectTypeSpecGenerator genericObjectGenerator = new ObjectTypeSpecGenerator(
className,
generatorContext.getPoetClassNameFactory().getObjectMapperClassName(),
enrichedObjectProperties,
implementsInterfaces,
true,
generatorContext.getCustomConfig().enablePublicConstructors(),
generatorContext.deserializeWithAdditionalProperties(),
generatorContext.getCustomConfig().jsonInclude(),
generatorContext.getCustomConfig().disableRequiredPropertyBuilderChecks(),
generatorContext.builderNotNullChecks());
TypeSpec objectTypeSpec = genericObjectGenerator.generate();
JavaFile javaFile =
JavaFile.builder(className.packageName(), objectTypeSpec).build();
return GeneratedObject.builder()
Expand All @@ -129,17 +53,4 @@ public GeneratedObject generateFile() {
.addAllExtendedObjectPropertyGetters(extendedPropertyGetters)
.build();
}

private static List<EnrichedObjectProperty> getEnrichedObjectProperties(
GeneratedJavaInterface generatedJavaInterface,
Map<ObjectProperty, EnrichedObjectProperty> objectPropertyGetters) {
return generatedJavaInterface.propertyMethodSpecs().stream()
.map(propertyMethodSpec -> {
EnrichedObjectProperty enrichedObjectProperty = EnrichedObjectProperty.of(
propertyMethodSpec.objectProperty(), true, propertyMethodSpec.methodSpec().returnType);
objectPropertyGetters.put(propertyMethodSpec.objectProperty(), enrichedObjectProperty);
return enrichedObjectProperty;
})
.collect(Collectors.toList());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import com.fern.ir.model.types.UndiscriminatedUnionTypeDeclaration;
import com.fern.ir.model.types.UnionTypeDeclaration;
import com.fern.java.AbstractGeneratorContext;
import com.fern.java.generators.object.ObjectTypeSpecGenerator;
import com.fern.java.output.GeneratedJavaFile;
import com.fern.java.output.GeneratedJavaInterface;
import com.fern.java.output.GeneratedObject;
Expand All @@ -25,6 +26,7 @@ public final class SingleTypeGenerator implements Type.Visitor<Optional<Generate
private final ClassName className;
private final Map<TypeId, GeneratedJavaInterface> allGeneratedInterfaces;
private final boolean fromErrorDeclaration;
private final SingleTypeSpecGenerator typeSpecGenerator;

public SingleTypeGenerator(
AbstractGeneratorContext<?, ?> generatorContext,
Expand All @@ -37,6 +39,8 @@ public SingleTypeGenerator(
this.allGeneratedInterfaces = allGeneratedInterfaces;
this.declaredTypeName = declaredTypeName;
this.fromErrorDeclaration = fromErrorDeclaration;
this.typeSpecGenerator = new SingleTypeSpecGenerator(
generatorContext, declaredTypeName, className, allGeneratedInterfaces, fromErrorDeclaration);
}

@Override
Expand All @@ -60,13 +64,19 @@ public Optional<GeneratedJavaFile> visitObject(ObjectTypeDeclaration value) {
.map(DeclaredTypeName::getTypeId)
.map(allGeneratedInterfaces::get)
.collect(Collectors.toList());
ObjectGenerator objectGenerator = new ObjectGenerator(
ObjectTypeSpecGenerator objectTypeSpecGenerator = new ObjectTypeSpecGenerator(
value,
Optional.ofNullable(allGeneratedInterfaces.get(declaredTypeName.getTypeId())),
extendedInterfaces,
generatorContext,
allGeneratedInterfaces,
className);
ObjectGenerator objectGenerator = new ObjectGenerator(
generatorContext,
className,
objectTypeSpecGenerator.generate(),
objectTypeSpecGenerator.objectPropertyGetters(),
objectTypeSpecGenerator.extendedPropertyGetters());
GeneratedObject generatedObject = objectGenerator.generateFile();
return Optional.of(GeneratedJavaFile.builder()
.className(generatedObject.getClassName())
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package com.fern.java.generators;

import com.fern.ir.model.commons.TypeId;
import com.fern.ir.model.types.AliasTypeDeclaration;
import com.fern.ir.model.types.DeclaredTypeName;
import com.fern.ir.model.types.EnumTypeDeclaration;
import com.fern.ir.model.types.ObjectTypeDeclaration;
import com.fern.ir.model.types.Type;
import com.fern.ir.model.types.UndiscriminatedUnionTypeDeclaration;
import com.fern.ir.model.types.UnionTypeDeclaration;
import com.fern.java.AbstractGeneratorContext;
import com.fern.java.generators.object.ObjectTypeSpecGenerator;
import com.fern.java.output.GeneratedJavaInterface;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.TypeSpec;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

public class SingleTypeSpecGenerator implements Type.Visitor<Optional<TypeSpec>> {

private final AbstractGeneratorContext<?, ?> generatorContext;
private final DeclaredTypeName declaredTypeName;
private final ClassName className;
private final Map<TypeId, GeneratedJavaInterface> allGeneratedInterfaces;
private final boolean fromErrorDeclaration;

public SingleTypeSpecGenerator(
AbstractGeneratorContext<?, ?> generatorContext,
DeclaredTypeName declaredTypeName,
ClassName className,
Map<TypeId, GeneratedJavaInterface> allGeneratedInterfaces,
boolean fromErrorDeclaration) {
this.generatorContext = generatorContext;
this.className = className;
this.allGeneratedInterfaces = allGeneratedInterfaces;
this.declaredTypeName = declaredTypeName;
this.fromErrorDeclaration = fromErrorDeclaration;
}

@Override
public Optional<TypeSpec> visitAlias(AliasTypeDeclaration value) {
return Optional.empty();
}

@Override
public Optional<TypeSpec> visitEnum(EnumTypeDeclaration value) {
return Optional.empty();
}

@Override
public Optional<TypeSpec> visitObject(ObjectTypeDeclaration value) {
List<GeneratedJavaInterface> extendedInterfaces = value.getExtends().stream()
.map(DeclaredTypeName::getTypeId)
.map(allGeneratedInterfaces::get)
.collect(Collectors.toList());
ObjectTypeSpecGenerator objectTypeSpecGenerator = new ObjectTypeSpecGenerator(
value,
Optional.ofNullable(allGeneratedInterfaces.get(declaredTypeName.getTypeId())),
extendedInterfaces,
generatorContext,
allGeneratedInterfaces,
className);
return Optional.of(objectTypeSpecGenerator.generate());
}

@Override
public Optional<TypeSpec> visitUnion(UnionTypeDeclaration value) {
return Optional.empty();
}

@Override
public Optional<TypeSpec> visitUndiscriminatedUnion(UndiscriminatedUnionTypeDeclaration undiscriminatedUnion) {
return Optional.empty();
}

@Override
public Optional<TypeSpec> _visitUnknown(Object o) {
return Optional.empty();
}
}
Loading
Loading