2021-03-01 21:04:06 +01:00
package it.cavallium.data.generator ;
import com.squareup.javapoet.ArrayTypeName ;
import com.squareup.javapoet.ClassName ;
import com.squareup.javapoet.CodeBlock ;
import com.squareup.javapoet.FieldSpec ;
import com.squareup.javapoet.JavaFile ;
import com.squareup.javapoet.MethodSpec ;
import com.squareup.javapoet.ParameterSpec ;
import com.squareup.javapoet.ParameterizedTypeName ;
import com.squareup.javapoet.TypeName ;
import com.squareup.javapoet.TypeSpec ;
import com.squareup.javapoet.TypeSpec.Builder ;
import com.squareup.javapoet.TypeVariableName ;
import com.squareup.javapoet.WildcardTypeName ;
2021-05-21 00:20:02 +02:00
import io.soabase.recordbuilder.core.RecordBuilder ;
2021-03-01 21:04:06 +01:00
import it.cavallium.data.generator.nativedata.IGenericNullable ;
2021-03-10 03:37:09 +01:00
import it.cavallium.data.generator.nativedata.Int52Serializer ;
2021-03-01 21:04:06 +01:00
import it.cavallium.data.generator.nativedata.StringSerializer ;
2021-05-25 11:17:44 +02:00
import it.unimi.dsi.fastutil.booleans.BooleanList ;
import it.unimi.dsi.fastutil.bytes.ByteList ;
import it.unimi.dsi.fastutil.chars.CharList ;
import it.unimi.dsi.fastutil.doubles.DoubleList ;
import it.unimi.dsi.fastutil.floats.FloatList ;
import it.unimi.dsi.fastutil.ints.IntList ;
import it.unimi.dsi.fastutil.longs.LongList ;
2021-03-01 21:04:06 +01:00
import it.unimi.dsi.fastutil.objects.Object2IntLinkedOpenHashMap ;
2021-04-27 00:08:19 +02:00
import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap ;
2021-03-01 21:04:06 +01:00
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet ;
2021-05-25 11:17:44 +02:00
import it.unimi.dsi.fastutil.shorts.ShortList ;
2021-03-01 21:04:06 +01:00
import java.io.DataInput ;
import java.io.DataOutput ;
import java.io.File ;
import java.io.IOError ;
import java.io.IOException ;
import java.io.InputStream ;
import java.io.Serializable ;
import java.lang.reflect.Array ;
import java.nio.file.Files ;
import java.nio.file.Path ;
import java.util.ArrayList ;
import java.util.Collection ;
import java.util.Collections ;
import java.util.HashMap ;
import java.util.LinkedHashMap ;
import java.util.List ;
import java.util.Map ;
import java.util.Map.Entry ;
import java.util.Objects ;
import java.util.Optional ;
import java.util.Set ;
import java.util.concurrent.atomic.AtomicBoolean ;
import java.util.concurrent.atomic.AtomicInteger ;
import java.util.concurrent.atomic.AtomicReference ;
import java.util.function.Supplier ;
import java.util.stream.Collectors ;
import java.util.stream.Stream ;
import javax.lang.model.element.Modifier ;
import org.jetbrains.annotations.NotNull ;
import org.jetbrains.annotations.Nullable ;
import org.slf4j.Logger ;
import org.slf4j.LoggerFactory ;
import org.yaml.snakeyaml.Yaml ;
@SuppressWarnings ( { " SameParameterValue " , " unused " } )
public class SourcesGenerator {
private static final Logger logger = LoggerFactory . getLogger ( SourcesGenerator . class ) ;
private final SourcesGeneratorConfiguration configuration ;
private SourcesGenerator ( InputStream yamlDataStream ) {
Yaml yaml = new Yaml ( ) ;
this . configuration = yaml . loadAs ( yamlDataStream , SourcesGeneratorConfiguration . class ) ;
}
public static SourcesGenerator load ( InputStream yamlData ) {
return new SourcesGenerator ( yamlData ) ;
}
public static SourcesGenerator load ( Path yamlPath ) throws IOException {
try ( InputStream in = Files . newInputStream ( yamlPath ) ) {
return new SourcesGenerator ( in ) ;
}
}
public static SourcesGenerator load ( File yamlPath ) throws IOException {
try ( InputStream in = Files . newInputStream ( yamlPath . toPath ( ) ) ) {
return new SourcesGenerator ( in ) ;
}
}
/ * *
* @param basePackageName org . example
* @param outPath path / to / output
* /
public void generateSources ( String basePackageName , Path outPath ) throws IOException {
// Fix the configuration
for ( Entry < String , InterfaceDataConfiguration > interfacesDatum : configuration . interfacesData . entrySet ( ) ) {
String k = interfacesDatum . getKey ( ) ;
InterfaceDataConfiguration value = interfacesDatum . getValue ( ) ;
value . commonData . replaceAll ( ( field , fieldType ) - > fixType ( fieldType ) ) ;
}
for ( Entry < String , InterfaceDataConfiguration > interfacesDatum : configuration . interfacesData . entrySet ( ) ) {
String name = interfacesDatum . getKey ( ) ;
InterfaceDataConfiguration value = interfacesDatum . getValue ( ) ;
value . commonGetters . replaceAll ( ( field , fieldType ) - > fixType ( fieldType ) ) ;
}
for ( Entry < String , VersionConfiguration > stringVersionConfigurationEntry : configuration . versions . entrySet ( ) ) {
String k = stringVersionConfigurationEntry . getKey ( ) ;
VersionConfiguration config = stringVersionConfigurationEntry . getValue ( ) ;
for ( Entry < String , ClassConfiguration > entry : config . classes . entrySet ( ) ) {
String clazz = entry . getKey ( ) ;
ClassConfiguration classConfiguration = entry . getValue ( ) ;
classConfiguration . getData ( ) . replaceAll ( ( field , fieldType ) - > fixType ( fieldType ) ) ;
}
}
// Create the Versions class
var versionsClass = TypeSpec . classBuilder ( " Versions " ) ;
versionsClass . addModifiers ( Modifier . PUBLIC ) ;
versionsClass . addModifiers ( Modifier . FINAL ) ;
var versionsInstances = FieldSpec . builder ( ArrayTypeName . of ( ClassName . get ( joinPackage ( basePackageName , " " ) , " IVersion " ) ) ,
" VERSIONS " ,
Modifier . PUBLIC ,
Modifier . STATIC ,
Modifier . FINAL
) ;
List < CodeBlock > versionsInstancesValue = new ArrayList < > ( ) ;
for ( Entry < String , VersionConfiguration > stringVersionConfigurationEntry : configuration . versions . entrySet ( ) ) {
String version = stringVersionConfigurationEntry . getKey ( ) ;
VersionConfiguration value = stringVersionConfigurationEntry . getValue ( ) ;
// Add a static variable for this version, containing the normalized version number
var versionNumberField = FieldSpec
. builder ( TypeName . INT , getVersionVarName ( version ) )
. addModifiers ( Modifier . PUBLIC )
. addModifiers ( Modifier . STATIC )
. addModifiers ( Modifier . FINAL )
. initializer ( getVersionShortInt ( version ) )
. build ( ) ;
// Add the fields to the class
versionsClass . addField ( versionNumberField ) ;
var versionPackage = getVersionPackage ( configuration . currentVersion , basePackageName , version ) ;
var versionClassType = ClassName . get ( joinPackage ( versionPackage , " " ) , " Version " ) ;
versionsInstancesValue . add ( CodeBlock . builder ( ) . add ( " $T.INSTANCE " , versionClassType ) . build ( ) ) ;
}
versionsInstances . initializer ( CodeBlock
. builder ( )
. add ( " { \ n " )
. add ( CodeBlock . join ( versionsInstancesValue , " , \ n " ) )
. add ( " \ n} " )
. build ( ) ) ;
versionsClass . addField ( versionsInstances . build ( ) ) ;
// Save the resulting class in the main package
writeClass ( outPath , joinPackage ( basePackageName , " " ) , versionsClass ) ;
// Create the BasicType class
{
var basicTypeClass = TypeSpec . enumBuilder ( " BasicType " ) ;
basicTypeClass . addModifiers ( Modifier . PUBLIC ) ;
for ( Entry < String , VersionConfiguration > stringVersionConfigurationEntry : configuration . versions . entrySet ( ) ) {
String k = stringVersionConfigurationEntry . getKey ( ) ;
VersionConfiguration value = stringVersionConfigurationEntry . getValue ( ) ;
for ( String basicTypeName : value . classes . keySet ( ) ) {
if ( ! basicTypeClass . enumConstants . containsKey ( basicTypeName ) ) {
basicTypeClass . addEnumConstant ( basicTypeName ) ;
}
}
}
// Save the resulting class in the main package
writeClass ( outPath , joinPackage ( basePackageName , " " ) , basicTypeClass ) ;
}
2021-04-27 00:08:19 +02:00
// Create the GenericType class
{
var genericTypeClass = TypeSpec . enumBuilder ( " GenericType " ) ;
genericTypeClass . addModifiers ( Modifier . PUBLIC ) ;
for ( Entry < String , VersionConfiguration > stringVersionConfigurationEntry : configuration . versions . entrySet ( ) ) {
String k = stringVersionConfigurationEntry . getKey ( ) ;
VersionConfiguration value = stringVersionConfigurationEntry . getValue ( ) ;
for ( String superTypeName : value . superTypes . keySet ( ) ) {
if ( ! genericTypeClass . enumConstants . containsKey ( superTypeName ) ) {
genericTypeClass . addEnumConstant ( superTypeName ) ;
}
}
}
// Save the resulting class in the main package
writeClass ( outPath , joinPackage ( basePackageName , " " ) , genericTypeClass ) ;
}
2021-03-01 21:04:06 +01:00
// Create the IVersion class
{
var iVersionClass = TypeSpec . interfaceBuilder ( " IVersion " ) ;
iVersionClass . addModifiers ( Modifier . PUBLIC ) ;
iVersionClass . addTypeVariable ( TypeVariableName . get ( " B " ) ) ;
// Add getClass method
{
var getClassMethodBuilder = MethodSpec
. methodBuilder ( " getClass " )
. addModifiers ( Modifier . PUBLIC )
. addModifiers ( Modifier . ABSTRACT )
. returns ( ParameterizedTypeName . get ( ClassName . get ( Class . class ) ,
WildcardTypeName . subtypeOf ( TypeVariableName . get ( " B " ) )
) )
. addParameter ( ParameterSpec
. builder ( ClassName . get ( joinPackage ( basePackageName , " " ) , " BasicType " ) , " type " )
. build ( ) ) ;
iVersionClass . addMethod ( getClassMethodBuilder . build ( ) ) ;
}
// Add getSerializer method
{
var getSerializerMethodBuilder = MethodSpec
. methodBuilder ( " getSerializer " )
. addModifiers ( Modifier . PUBLIC )
. addModifiers ( Modifier . ABSTRACT )
. addTypeVariable ( TypeVariableName . get ( " T " ,
TypeVariableName . get ( " B " )
) )
. returns ( ParameterizedTypeName . get ( ClassName . get ( DataSerializer . class ) , TypeVariableName . get ( " T " ) ) )
. addException ( IOException . class )
. addParameter ( ParameterSpec
. builder ( ClassName . get ( joinPackage ( basePackageName , " " ) , " BasicType " ) , " type " )
. build ( ) ) ;
iVersionClass . addMethod ( getSerializerMethodBuilder . build ( ) ) ;
}
// Add getVersion method
{
var getVersionMethod = MethodSpec
. methodBuilder ( " getVersion " )
. addModifiers ( Modifier . PUBLIC )
. addModifiers ( Modifier . ABSTRACT )
. returns ( TypeName . INT )
. build ( ) ;
iVersionClass . addMethod ( getVersionMethod ) ;
}
// Save the resulting class in the main package
writeClass ( outPath , joinPackage ( basePackageName , " " ) , iVersionClass ) ;
}
// Create the CurrentVersion class
{
var currentVersionClass = TypeSpec . classBuilder ( " CurrentVersion " ) ;
currentVersionClass . addModifiers ( Modifier . PUBLIC ) ;
currentVersionClass . addModifiers ( Modifier . FINAL ) ;
// Add a static variable for the current version
{
var versionNumberField = FieldSpec . builder ( ClassName
. get ( getVersionPackage ( configuration . currentVersion , basePackageName , configuration . currentVersion ) ,
" Version " ) , " VERSION " ) . addModifiers ( Modifier . PUBLIC ) . addModifiers ( Modifier . STATIC )
. addModifiers ( Modifier . FINAL ) . initializer (
" new " + getVersionPackage ( configuration . currentVersion , basePackageName , configuration . currentVersion )
+ " .Version() " ) . build ( ) ;
currentVersionClass . addField ( versionNumberField ) ;
}
// Check latest version method
{
var isLatestVersionMethod = MethodSpec . methodBuilder ( " isLatestVersion " ) . addModifiers ( Modifier . PUBLIC )
. addModifiers ( Modifier . FINAL ) . addModifiers ( Modifier . STATIC ) . returns ( TypeName . BOOLEAN )
. addParameter ( ParameterSpec . builder ( TypeName . INT , " version " ) . build ( ) )
. addCode ( " return version == VERSION.getVersion(); " ) . build ( ) ;
currentVersionClass . addMethod ( isLatestVersionMethod ) ;
}
// Get super type classes method
{
var getSuperTypeClasses = MethodSpec . methodBuilder ( " getSuperTypeClasses " ) . addModifiers ( Modifier . PUBLIC )
. addModifiers ( Modifier . FINAL ) . addModifiers ( Modifier . STATIC ) . returns ( ParameterizedTypeName . get (
ClassName . get ( Set . class ) , ParameterizedTypeName . get (
ClassName . get ( Class . class ) , WildcardTypeName . subtypeOf ( ClassName
. get ( joinPackage ( getVersionPackage ( configuration . currentVersion , basePackageName , configuration . currentVersion ) , " data " ) ,
" IType " ) ) ) ) )
. addCode ( " return $T.of( \ n " , Set . class ) ;
AtomicBoolean isFirst = new AtomicBoolean ( true ) ;
for ( Entry < String , Set < String > > entry : configuration . versions . get ( configuration . currentVersion ) . superTypes . entrySet ( ) ) {
String superTypeName = entry . getKey ( ) ;
Set < String > superTypeConfig = entry . getValue ( ) ;
if ( ! isFirst . getAndSet ( false ) ) {
getSuperTypeClasses . addCode ( " , \ n " ) ;
}
getSuperTypeClasses . addCode ( " $T.class " ,
ClassName . get ( joinPackage ( getVersionPackage ( configuration . currentVersion ,
basePackageName ,
configuration . currentVersion
) , " data " ) , superTypeName )
) ;
}
getSuperTypeClasses . addCode ( " \ n); " ) ;
currentVersionClass . addMethod ( getSuperTypeClasses . build ( ) ) ;
}
// Get super type subtypes classes method
{
var getSuperTypeSubtypesClasses = MethodSpec . methodBuilder ( " getSuperTypeSubtypesClasses " ) . addModifiers ( Modifier . PUBLIC )
. addModifiers ( Modifier . FINAL ) . addModifiers ( Modifier . STATIC ) . returns ( ParameterizedTypeName . get (
ClassName . get ( Set . class ) , ParameterizedTypeName . get (
ClassName . get ( Class . class ) , WildcardTypeName . subtypeOf ( ClassName
. get ( joinPackage ( getVersionPackage ( configuration . currentVersion , basePackageName , configuration . currentVersion ) , " data " ) ,
" IBasicType " ) ) ) ) ) ;
getSuperTypeSubtypesClasses . addParameter ( ParameterSpec . builder ( ParameterizedTypeName . get ( ClassName . get ( Class . class ) , WildcardTypeName . subtypeOf ( ClassName
. get ( joinPackage ( getVersionPackage ( configuration . currentVersion , basePackageName , configuration . currentVersion ) , " data " ) ,
" IType " ) ) ) , " superTypeClass " ) . build ( ) ) ;
getSuperTypeSubtypesClasses . beginControlFlow ( " switch (superTypeClass.getCanonicalName()) " ) ;
for ( Entry < String , Set < String > > entry : configuration . versions . get ( configuration . currentVersion ) . superTypes . entrySet ( ) ) {
String superTypeName = entry . getKey ( ) ;
Set < String > subTypes = entry . getValue ( ) ;
getSuperTypeSubtypesClasses . beginControlFlow ( " case \" " + ClassName
. get ( joinPackage ( getVersionPackage ( configuration . currentVersion ,
basePackageName ,
configuration . currentVersion
) , " data " ) , superTypeName )
. canonicalName ( ) + " \" : " ) ;
getSuperTypeSubtypesClasses . addCode ( " return $T.of( \ n " , Set . class ) ;
AtomicBoolean isFirst = new AtomicBoolean ( true ) ;
for ( String subTypeName : subTypes ) {
if ( ! isFirst . getAndSet ( false ) ) {
getSuperTypeSubtypesClasses . addCode ( " , \ n " ) ;
}
getSuperTypeSubtypesClasses . addCode ( " $T.class " ,
ClassName . get ( joinPackage ( getVersionPackage ( configuration . currentVersion ,
basePackageName ,
configuration . currentVersion
) , " data " ) , subTypeName )
) ;
}
getSuperTypeSubtypesClasses . addCode ( " \ n); \ n " ) ;
getSuperTypeSubtypesClasses . endControlFlow ( ) ;
}
getSuperTypeSubtypesClasses . beginControlFlow ( " default: " ) ;
getSuperTypeSubtypesClasses . addStatement ( " throw new $T() " , IllegalArgumentException . class ) ;
getSuperTypeSubtypesClasses . endControlFlow ( ) ;
getSuperTypeSubtypesClasses . endControlFlow ( ) ;
currentVersionClass . addMethod ( getSuperTypeSubtypesClasses . build ( ) ) ;
}
// UpgradeDataToLatestVersion1 Method
{
var upgradeDataToLatestVersion1MethodBuilder = MethodSpec . methodBuilder ( " upgradeDataToLatestVersion " ) . addTypeVariable ( TypeVariableName . get ( " U " , ClassName
. get ( joinPackage ( getVersionPackage ( configuration . currentVersion , basePackageName , configuration . currentVersion ) , " data " ) ,
" IBasicType " ) ) )
. addModifiers ( Modifier . PUBLIC ) . addModifiers ( Modifier . STATIC ) . addModifiers ( Modifier . FINAL ) . returns ( TypeVariableName . get ( " U " ) )
. addParameter ( ParameterSpec . builder ( TypeName . INT , " oldVersion " ) . build ( ) ) . addParameter (
ParameterSpec . builder ( ClassName . get ( joinPackage ( basePackageName , " " ) , " BasicType " ) , " type " ) . build ( ) )
. addParameter ( ParameterSpec . builder ( DataInput . class , " oldDataInput " ) . build ( ) )
. addException ( IOException . class ) . beginControlFlow ( " switch (oldVersion) " ) ;
AtomicInteger seqNumber = new AtomicInteger ( 0 ) ;
for ( Entry < String , VersionConfiguration > entry : configuration . versions . entrySet ( ) ) {
String version = entry . getKey ( ) ;
VersionConfiguration versionConfiguration = entry . getValue ( ) ;
// Add a case in which the data version deserializes the serialized data and upgrades it
upgradeDataToLatestVersion1MethodBuilder . beginControlFlow ( " case $T. " + getVersionVarName ( version ) + " : " ,
ClassName . get ( joinPackage ( basePackageName , " " ) , " Versions " )
) ;
upgradeDataToLatestVersion1MethodBuilder . addStatement (
" var deserialized " + seqNumber . incrementAndGet ( ) + " = " + getVersionPackage ( configuration . currentVersion ,
basePackageName ,
version
) + " .Version.INSTANCE.getSerializer(type).deserialize(oldDataInput) " ) ;
upgradeDataToLatestVersion1MethodBuilder . addStatement (
" return upgradeDataToLatestVersion(Versions. " + getVersionVarName ( version ) + " , deserialized "
+ seqNumber . get ( ) + " ) " ) ;
upgradeDataToLatestVersion1MethodBuilder . endControlFlow ( ) ;
}
var upgradeDataToLatestVersion1Method = upgradeDataToLatestVersion1MethodBuilder . beginControlFlow ( " default: " )
. addStatement ( " throw new $T( \" Unknown version: \" + oldVersion) " , IOException . class ) . endControlFlow ( )
. endControlFlow ( ) . build ( ) ;
currentVersionClass . addMethod ( upgradeDataToLatestVersion1Method ) ;
}
// UpgradeDataToLatestVersion2 Method
{
2021-10-28 12:02:05 +02:00
var versionsClassName = ClassName . get ( joinPackage ( basePackageName , " " ) , " Versions " ) ;
2021-03-01 21:04:06 +01:00
var upgradeDataToLatestVersion2MethodBuilder = MethodSpec . methodBuilder ( " upgradeDataToLatestVersion " )
. addModifiers ( Modifier . PUBLIC ) . addModifiers ( Modifier . STATIC ) . addModifiers ( Modifier . FINAL ) . addTypeVariable ( TypeVariableName . get ( " T " ) )
. addTypeVariable ( TypeVariableName . get ( " U " , ClassName
. get ( joinPackage ( getVersionPackage ( configuration . currentVersion , basePackageName , configuration . currentVersion ) , " data " ) ,
" IBasicType " ) ) ) . returns ( TypeVariableName . get ( " U " ) )
. addParameter ( ParameterSpec . builder ( TypeName . INT , " oldVersion " ) . build ( ) )
. addParameter ( ParameterSpec . builder ( TypeVariableName . get ( " T " ) , " oldData " ) . build ( ) )
2021-10-28 12:02:05 +02:00
. addException ( IOException . class )
. addStatement ( " int intermediateVersion = oldVersion " )
. addStatement ( " $T intermediateData = oldData " , Object . class )
. beginControlFlow ( " while (true) " )
2021-10-28 13:13:34 +02:00
. beginControlFlow ( " switch (intermediateVersion) " ) ;
2021-03-01 21:04:06 +01:00
for ( Entry < String , VersionConfiguration > entry : configuration . versions . entrySet ( ) ) {
String version = entry . getKey ( ) ;
VersionConfiguration versionConfiguration = entry . getValue ( ) ;
// Add a case in which the data version deserializes the serialized data and upgrades it
upgradeDataToLatestVersion2MethodBuilder . beginControlFlow ( " case $T. " + getVersionVarName ( version ) + " : " ,
2021-10-28 12:02:05 +02:00
versionsClassName
2021-03-01 21:04:06 +01:00
) ;
if ( version . equalsIgnoreCase ( configuration . currentVersion ) ) {
// This is the latest version, don't upgrade.
2021-10-28 12:02:05 +02:00
upgradeDataToLatestVersion2MethodBuilder . addStatement ( " return ($T) intermediateData " , TypeVariableName . get ( " U " ) ) ;
2021-03-01 21:04:06 +01:00
} else {
// Upgrade
2021-10-28 12:02:05 +02:00
upgradeDataToLatestVersion2MethodBuilder
. addStatement (
" intermediateData = " + getVersionPackage ( configuration . currentVersion , basePackageName , version )
+ " .Version.upgradeToNextVersion(($T) intermediateData) " ,
ClassName . get ( joinPackage ( getVersionPackage ( configuration . currentVersion , basePackageName , version ) ,
" data "
) , " IBasicType " )
)
. addStatement ( " intermediateVersion = $T. "
+ getVersionVarName ( findNextVersion ( configuration , version ) . orElseThrow ( ) ) , versionsClassName )
. addStatement ( " break " ) ;
2021-03-01 21:04:06 +01:00
}
upgradeDataToLatestVersion2MethodBuilder . endControlFlow ( ) ;
}
var upgradeDataToLatestVersion2Method = upgradeDataToLatestVersion2MethodBuilder . beginControlFlow ( " default: " )
. addStatement ( " throw new $T( \" Unknown version: \" + oldVersion) " , IOException . class ) . endControlFlow ( )
2021-10-28 12:02:05 +02:00
. endControlFlow ( )
. endControlFlow ( )
. build ( ) ;
2021-03-01 21:04:06 +01:00
currentVersionClass . addMethod ( upgradeDataToLatestVersion2Method ) ;
}
// Save the resulting class in the main package
writeClass ( outPath , joinPackage ( basePackageName , " current " ) , currentVersionClass ) ;
}
for ( Entry < String , VersionConfiguration > mapEntry : configuration . versions . entrySet ( ) ) {
String version = mapEntry . getKey ( ) ;
VersionConfiguration versionConfiguration = mapEntry . getValue ( ) ;
var versionPackage = getVersionPackage ( configuration . currentVersion , basePackageName , version ) ;
var versionClassType = ClassName . get ( joinPackage ( versionPackage , " " ) , " Version " ) ;
var nextVersion = findNextVersion ( configuration , version ) ;
var nextVersionPackage = nextVersion . map ( ( nextVersionValue ) - > getVersionPackage ( configuration . currentVersion ,
basePackageName ,
nextVersionValue
) ) ;
logger . info (
" Found version configuration: \ n{ \ n \ tversion: \" " + version + " \" , \ n \ tversionPackage: \" " + versionPackage
+ " \" , \ n \ tnextVersion: \" " + nextVersion . orElse ( " unknown " ) + " \" , \ n \ tnextVersionPackage: \" "
+ nextVersionPackage . orElse ( " unknown " ) + " \" \ n} " ) ;
HashMap < String , TypeName > typeOptionalSerializers = new LinkedHashMap < > ( ) ;
HashMap < String , TypeName > typeOptionalUpgraders = new LinkedHashMap < > ( ) ;
HashMap < String , SerializeCodeBlockGenerator > typeSerializeStatement = new HashMap < > ( ) ;
HashMap < String , CodeBlock > typeDeserializeStatement = new HashMap < > ( ) ;
HashMap < String , Boolean > typeMustGenerateSerializer = new HashMap < > ( ) ;
HashMap < String , TypeName > typeTypes = new LinkedHashMap < > ( Map . of ( " boolean " ,
TypeName . BOOLEAN ,
" short " ,
TypeName . SHORT ,
" char " ,
TypeName . CHAR ,
" int " ,
TypeName . INT ,
" long " ,
TypeName . LONG ,
" float " ,
TypeName . FLOAT ,
" double " ,
TypeName . DOUBLE ,
" byte " ,
TypeName . BYTE
) ) ;
2021-04-27 00:08:19 +02:00
HashMap < String , Family > typeFamily = new LinkedHashMap < > ( Map . of ( " boolean " ,
Family . SPECIAL_NATIVE ,
" short " ,
Family . SPECIAL_NATIVE ,
" char " ,
Family . SPECIAL_NATIVE ,
" int " ,
Family . SPECIAL_NATIVE ,
" long " ,
Family . SPECIAL_NATIVE ,
" float " ,
Family . SPECIAL_NATIVE ,
" double " ,
Family . SPECIAL_NATIVE ,
" byte " ,
Family . SPECIAL_NATIVE
) ) ;
2021-03-01 21:04:06 +01:00
@Nullable HashMap < String , TypeName > nextVersionTypeTypes ;
2021-04-27 00:08:19 +02:00
@Nullable HashMap < String , Family > nextVersionTypeFamily ;
2021-03-01 21:04:06 +01:00
if ( nextVersion . isPresent ( ) ) {
nextVersionTypeTypes = new LinkedHashMap < > ( Map . of ( " boolean " ,
TypeName . BOOLEAN ,
" short " ,
TypeName . SHORT ,
" char " ,
TypeName . CHAR ,
" int " ,
TypeName . INT ,
" long " ,
TypeName . LONG ,
" float " ,
TypeName . FLOAT ,
" double " ,
TypeName . DOUBLE ,
" byte " ,
TypeName . BYTE
) ) ;
2021-04-27 00:08:19 +02:00
nextVersionTypeFamily = new LinkedHashMap < > ( Map . of ( " boolean " ,
Family . SPECIAL_NATIVE ,
" short " ,
Family . SPECIAL_NATIVE ,
" char " ,
Family . SPECIAL_NATIVE ,
" int " ,
Family . SPECIAL_NATIVE ,
" long " ,
Family . SPECIAL_NATIVE ,
" float " ,
Family . SPECIAL_NATIVE ,
" double " ,
Family . SPECIAL_NATIVE ,
" byte " ,
Family . SPECIAL_NATIVE
) ) ;
2021-03-01 21:04:06 +01:00
} else {
nextVersionTypeTypes = null ;
2021-04-27 00:08:19 +02:00
nextVersionTypeFamily = null ;
2021-03-01 21:04:06 +01:00
}
Set < String > specialNativeTypes = Set . of ( " String " ,
" boolean " ,
" short " ,
" char " ,
" int " ,
" long " ,
" float " ,
" double " ,
2021-03-10 03:37:09 +01:00
" byte " ,
" Int52 "
2021-03-01 21:04:06 +01:00
) ;
// Generate the type statements
{
// Generate the native types
for ( String specialNativeType : specialNativeTypes ) {
if ( Character . isUpperCase ( specialNativeType . charAt ( 0 ) ) ) {
2021-03-10 03:37:09 +01:00
typeTypes . put ( specialNativeType , ClassName . get ( getSpecialNativePackage ( specialNativeType ) , specialNativeType ) ) ;
2021-04-27 00:08:19 +02:00
typeFamily . put ( specialNativeType , Family . SPECIAL_NATIVE ) ;
2021-03-01 21:04:06 +01:00
if ( nextVersion . isPresent ( ) ) {
2021-03-10 03:37:09 +01:00
nextVersionTypeTypes . put ( specialNativeType , ClassName . get ( getSpecialNativePackage ( specialNativeType ) , specialNativeType ) ) ;
2021-04-27 00:08:19 +02:00
nextVersionTypeFamily . put ( specialNativeType , Family . SPECIAL_NATIVE ) ;
2021-03-01 21:04:06 +01:00
}
if ( specialNativeType . equals ( " String " ) ) {
typeSerializeStatement . put ( specialNativeType ,
new SerializeCodeBlockGenerator ( CodeBlock
. builder ( )
. add ( " $T.INSTANCE.serialize(dataOutput, " , StringSerializer . class )
. build ( ) , CodeBlock . builder ( ) . add ( " ) " ) . build ( ) )
) ;
typeDeserializeStatement . put ( specialNativeType ,
CodeBlock . builder ( ) . add ( " $T.INSTANCE.deserialize(dataInput) " , StringSerializer . class ) . build ( )
) ;
2021-03-10 03:37:09 +01:00
} else if ( specialNativeType . equals ( " Int52 " ) ) {
typeSerializeStatement . put ( specialNativeType ,
new SerializeCodeBlockGenerator ( CodeBlock
. builder ( )
. add ( " $T.INSTANCE.serialize(dataOutput, " , Int52Serializer . class )
. build ( ) , CodeBlock . builder ( ) . add ( " ) " ) . build ( ) )
) ;
typeDeserializeStatement . put ( specialNativeType ,
CodeBlock . builder ( ) . add ( " $T.INSTANCE.deserialize(dataInput) " , Int52Serializer . class ) . build ( )
) ;
2021-03-01 21:04:06 +01:00
} else {
typeSerializeStatement . put ( specialNativeType ,
new SerializeCodeBlockGenerator ( CodeBlock
. builder ( )
. add ( " dataOutput.write " + specialNativeType + " ( " )
. build ( ) , CodeBlock . builder ( ) . add ( " ) " ) . build ( ) )
) ;
typeDeserializeStatement . put ( specialNativeType ,
CodeBlock . builder ( ) . add ( " dataInput.read " + specialNativeType + " () " ) . build ( )
) ;
}
} else {
var uppercasedType = Character . toUpperCase ( specialNativeType . charAt ( 0 ) ) + specialNativeType . substring ( 1 ) ;
// don't put the special type, because it's already in the hashmap.
typeSerializeStatement . put ( specialNativeType ,
new SerializeCodeBlockGenerator ( CodeBlock
. builder ( )
. add ( " dataOutput.write " + uppercasedType + " ( " )
. build ( ) , CodeBlock . builder ( ) . add ( " ) " ) . build ( ) )
) ;
typeDeserializeStatement . put ( specialNativeType ,
CodeBlock . builder ( ) . add ( " dataInput.read " + uppercasedType + " () " ) . build ( )
) ;
}
typeMustGenerateSerializer . put ( specialNativeType , false ) ;
typeTypes . put ( " - " + specialNativeType ,
ClassName . get ( " it.cavallium.data.generator.nativedata " , " Nullable " + specialNativeType )
) ;
2021-04-27 00:08:19 +02:00
typeFamily . put ( " - " + specialNativeType , Family . NULLABLE_OTHER ) ;
2021-05-25 11:17:44 +02:00
typeTypes . put ( " § " + specialNativeType , getImmutableArrayType ( typeTypes , specialNativeType ) ) ;
2021-04-27 00:08:19 +02:00
typeFamily . put ( " § " + specialNativeType , Family . OTHER ) ;
2021-03-01 21:04:06 +01:00
if ( nextVersion . isPresent ( ) ) {
nextVersionTypeTypes . put ( " - " + specialNativeType ,
ClassName . get ( " it.cavallium.data.generator.nativedata " , " Nullable " + specialNativeType )
) ;
2021-04-27 00:08:19 +02:00
nextVersionTypeFamily . put ( " - " + specialNativeType , Family . NULLABLE_OTHER ) ;
2021-05-25 11:17:44 +02:00
nextVersionTypeTypes . put ( " § " + specialNativeType , getImmutableArrayType ( typeTypes , specialNativeType ) ) ;
2021-04-27 00:08:19 +02:00
nextVersionTypeFamily . put ( " § " + specialNativeType , Family . OTHER ) ;
2021-03-01 21:04:06 +01:00
}
typeOptionalSerializers . put ( " - " + specialNativeType ,
ClassName . get ( " it.cavallium.data.generator.nativedata " ,
" Nullable " + specialNativeType + " Serializer "
)
) ;
typeOptionalSerializers . put ( " § " + specialNativeType ,
ClassName . get ( " it.cavallium.data.generator.nativedata " ,
" Array " + specialNativeType + " Serializer "
)
) ;
typeSerializeStatement . put ( " - " + specialNativeType ,
new SerializeCodeBlockGenerator ( CodeBlock
. builder ( )
. add ( " $T.Nullable " + specialNativeType + " SerializerInstance.serialize(dataOutput, " ,
ClassName . get ( joinPackage ( versionPackage , " " ) , " Version " )
)
. build ( ) , CodeBlock . builder ( ) . add ( " ) " ) . build ( ) )
) ;
typeSerializeStatement . put ( " § " + specialNativeType ,
new SerializeCodeBlockGenerator ( CodeBlock
. builder ( )
. add ( " $T.Array " + specialNativeType + " SerializerInstance.serialize(dataOutput, " ,
ClassName . get ( joinPackage ( versionPackage , " " ) , " Version " )
)
. build ( ) , CodeBlock . builder ( ) . add ( " ) " ) . build ( ) )
) ;
typeDeserializeStatement . put ( " - " + specialNativeType ,
CodeBlock
. builder ( )
. add ( " $T.Nullable " + specialNativeType + " SerializerInstance.deserialize(dataInput) " ,
ClassName . get ( joinPackage ( versionPackage , " " ) , " Version " )
)
. build ( )
) ;
typeDeserializeStatement . put ( " § " + specialNativeType ,
CodeBlock
. builder ( )
. add ( " $T.Array " + specialNativeType + " SerializerInstance.deserialize(dataInput) " ,
ClassName . get ( joinPackage ( versionPackage , " " ) , " Version " )
)
. build ( )
) ;
typeMustGenerateSerializer . put ( " - " + specialNativeType , false ) ;
typeMustGenerateSerializer . put ( " § " + specialNativeType , false ) ;
}
// Setup only the basic types upgraders variables
for ( String s : versionConfiguration . classes . keySet ( ) ) {
if ( nextVersion . isPresent ( ) ) {
typeOptionalUpgraders . put ( s , ClassName . get ( joinPackage ( versionPackage , " upgraders " ) , s + " Upgrader " ) ) ;
}
}
// Generate the basic and super types
Stream
. concat ( versionConfiguration . classes . keySet ( ) . stream ( ) , versionConfiguration . superTypes . keySet ( ) . stream ( ) )
. forEach ( ( type ) - > {
2021-04-27 00:08:19 +02:00
boolean isBasic = versionConfiguration . classes . containsKey ( type ) ;
2021-03-01 21:04:06 +01:00
typeOptionalSerializers . put ( type ,
ClassName . get ( joinPackage ( versionPackage , " serializers " ) , type + " Serializer " )
) ;
typeSerializeStatement . put ( type ,
new SerializeCodeBlockGenerator ( CodeBlock
. builder ( )
. add ( " $T. " + type + " SerializerInstance.serialize(dataOutput, " , versionClassType )
. build ( ) , CodeBlock . builder ( ) . add ( " ) " ) . build ( ) )
) ;
typeDeserializeStatement . put ( type ,
CodeBlock
. builder ( )
. add ( " $T. " + type + " SerializerInstance.deserialize(dataInput) " , versionClassType )
. build ( )
) ;
typeMustGenerateSerializer . put ( type , true ) ;
typeTypes . put ( type , ClassName . get ( joinPackage ( versionPackage , " data " ) , type ) ) ;
2021-04-27 00:08:19 +02:00
typeFamily . put ( type , ! isBasic ? Family . GENERIC : Family . BASIC ) ;
2021-03-01 21:04:06 +01:00
if ( nextVersion . isPresent ( ) ) {
nextVersionTypeTypes . put ( type , ClassName . get ( joinPackage ( nextVersionPackage . get ( ) , " data " ) , type ) ) ;
2021-04-27 00:08:19 +02:00
nextVersionTypeFamily . put ( type , ! isBasic ? Family . GENERIC : Family . BASIC ) ;
2021-03-01 21:04:06 +01:00
}
NeededTypes neededTypes = registerNeededTypes ( versionConfiguration ,
2021-04-27 00:08:19 +02:00
! isBasic ? Family . GENERIC : Family . BASIC ,
2021-03-01 21:04:06 +01:00
type ,
nextVersion ,
nextVersionPackage ,
versionClassType ,
versionPackage ,
typeOptionalSerializers ,
typeSerializeStatement ,
typeDeserializeStatement ,
typeMustGenerateSerializer ,
typeTypes ,
2021-04-27 00:08:19 +02:00
typeFamily ,
2021-03-01 21:04:06 +01:00
nextVersionTypeTypes ,
2021-04-27 00:08:19 +02:00
nextVersionTypeFamily ,
2021-03-01 21:04:06 +01:00
( ) - > ClassName . get ( joinPackage ( versionPackage , " data " ) , type ) ,
( ) - > ClassName . get ( joinPackage ( nextVersionPackage . orElseThrow ( ) , " data " ) , type )
) ;
} ) ;
// Generate the special types
for ( Entry < String , CustomTypesConfiguration > entry : versionConfiguration . customTypes . entrySet ( ) ) {
String key = entry . getKey ( ) ;
CustomTypesConfiguration customTypeConfiguration = entry . getValue ( ) ;
2021-06-07 16:23:01 +02:00
Optional < CustomTypesConfiguration > nextVersionCustomTypeConfiguration = nextVersion
. map ( s - > Objects . requireNonNull ( configuration . versions . get ( s ) . customTypes . get ( key ) ,
( ) - > " Custom type " + key + " not found in version " + s
) ) ;
2021-03-01 21:04:06 +01:00
typeOptionalSerializers . put ( key , ClassName . bestGuess ( customTypeConfiguration . serializer ) ) ;
typeSerializeStatement . put ( key ,
new SerializeCodeBlockGenerator ( CodeBlock
. builder ( )
. add ( " $T. " + key + " SerializerInstance.serialize(dataOutput, " , versionClassType )
. build ( ) , CodeBlock . builder ( ) . add ( " ) " ) . build ( ) )
) ;
typeDeserializeStatement . put ( key ,
CodeBlock
. builder ( )
. add ( " $T. " + key + " SerializerInstance.deserialize(dataInput) " , versionClassType )
. build ( )
) ;
typeMustGenerateSerializer . put ( key , false ) ;
2022-02-28 13:29:58 +01:00
typeTypes . put ( key , customTypeConfiguration . getJavaClassType ( ) ) ;
2021-04-27 00:08:19 +02:00
typeFamily . put ( key , Family . OTHER ) ;
2021-06-07 16:23:01 +02:00
if ( nextVersionCustomTypeConfiguration . isPresent ( ) ) {
2022-02-28 13:29:58 +01:00
nextVersionTypeTypes . put ( key , nextVersionCustomTypeConfiguration . get ( ) . getJavaClassType ( ) ) ;
2021-04-27 00:08:19 +02:00
nextVersionTypeFamily . put ( key , Family . OTHER ) ;
2021-03-01 21:04:06 +01:00
}
2022-02-28 13:29:58 +01:00
var arrayClassName = customTypeConfiguration . getJavaClassType ( ) ;
2021-03-01 21:04:06 +01:00
var neededTypes = registerNeededTypes ( versionConfiguration ,
2021-04-27 00:08:19 +02:00
Family . OTHER ,
2021-03-01 21:04:06 +01:00
key ,
nextVersion ,
nextVersionPackage ,
versionClassType ,
versionPackage ,
typeOptionalSerializers ,
typeSerializeStatement ,
typeDeserializeStatement ,
typeMustGenerateSerializer ,
typeTypes ,
2021-04-27 00:08:19 +02:00
typeFamily ,
2021-03-01 21:04:06 +01:00
nextVersionTypeTypes ,
2021-04-27 00:08:19 +02:00
nextVersionTypeFamily ,
2021-03-01 21:04:06 +01:00
( ) - > arrayClassName ,
( ) - > arrayClassName
) ;
}
for ( String type : typeTypes . keySet ( ) ) {
var a1 = typeSerializeStatement . get ( type ) . toString ( ) . replace ( " \ n " , " " ) ;
var a2 = typeDeserializeStatement . get ( type ) . toString ( ) . replace ( " \ n " , " " ) ;
var a3 = typeTypes . get ( type ) . toString ( ) . replace ( " \ n " , " " ) ;
if ( logger . isDebugEnabled ( ) ) {
logger . debug ( " Found type: { \ n \ ttype: \" " + type + " \" , \ n \ tclass: \" " + a3 + " \" , \ n \ tserialize: \" " + a1
+ " \" , \ n \ tdeserialize: \" " + a2 + " \" \ n} " ) ;
} else {
switch ( type . charAt ( 0 ) ) {
2021-05-21 00:20:02 +02:00
case '$' - > logger . debug ( " Found array: " + type . substring ( 1 ) ) ;
case '-' - > logger . debug ( " Found nullable type: " + type . substring ( 1 ) ) ;
default - > logger . debug ( " Found type: " + type ) ;
2021-03-01 21:04:06 +01:00
}
}
}
}
// Check if all types exist
{
for ( Entry < String , ClassConfiguration > e : versionConfiguration . classes . entrySet ( ) ) {
String type = e . getKey ( ) ;
ClassConfiguration typeConfig = e . getValue ( ) ;
for ( Entry < String , String > entry : typeConfig . data . entrySet ( ) ) {
String field = entry . getKey ( ) ;
String fieldType = entry . getValue ( ) ;
if ( ! typeTypes . containsKey ( fieldType ) ) {
throw new UnsupportedOperationException (
" Unknown type ' " + fieldType + " ' of field ' " + field + " ' in class ' " + type + " ' in version ' "
+ version + " ' " ) ;
}
}
}
}
// Generate the nullable types and serializers
{
for ( String type : typeTypes . keySet ( ) ) {
if ( type . startsWith ( " - " ) ) {
if ( typeMustGenerateSerializer . get ( type ) ) {
String substring = type . substring ( 1 ) ;
var nullableClassType = ClassName . get ( joinPackage ( versionPackage , " data.nullables " ) ,
" Nullable " + substring
) ;
// Create the nullable X serializer class
{
var nullableSerializerClass = TypeSpec . classBuilder ( " Nullable " + substring + " Serializer " ) ;
nullableSerializerClass . addModifiers ( Modifier . PUBLIC ) ;
nullableSerializerClass . addModifiers ( Modifier . FINAL ) ;
nullableSerializerClass . addSuperinterface ( ParameterizedTypeName . get ( ClassName . get (
" it.cavallium.data.generator " ,
" DataSerializer "
) , nullableClassType ) ) ;
// Create the INSTANCE field
{
var thisSerializerClassType = ClassName . get ( joinPackage ( versionPackage , " serializers " ) ,
" Nullable " + substring + " Serializer "
) ;
var instanceField = FieldSpec
. builder ( thisSerializerClassType , " INSTANCE " )
. initializer ( " new $T() " , thisSerializerClassType ) ;
instanceField . addModifiers ( Modifier . PUBLIC ) ;
instanceField . addModifiers ( Modifier . STATIC ) ;
instanceField . addModifiers ( Modifier . FINAL ) ;
nullableSerializerClass . addField ( instanceField . build ( ) ) ;
}
// Create the serialize method
{
var serializeMethod = createEmptySerializeMethod ( nullableClassType ) ;
serializeMethod . beginControlFlow ( " if (data.isEmpty()) " ) ;
serializeMethod . addStatement ( " dataOutput.writeBoolean(false) " ) ;
serializeMethod . nextControlFlow ( " else " ) ;
serializeMethod . addStatement ( " dataOutput.writeBoolean(true) " ) ;
serializeMethod . addStatement ( " var dataContent = data.get() " ) ;
serializeMethod . addStatement ( typeSerializeStatement . get ( substring ) . generate ( " dataContent " ) ) ;
serializeMethod . endControlFlow ( ) ;
nullableSerializerClass . addMethod ( serializeMethod . build ( ) ) ;
}
// Create the deserialize method
{
var deserializeMethod = createEmptyDeserializeMethod ( nullableClassType ) ;
deserializeMethod . addStatement ( " var isPresent = dataInput.readBoolean() " ) ;
deserializeMethod . beginControlFlow ( " if (!isPresent) " ) ;
deserializeMethod . addStatement ( " return $T.empty() " , nullableClassType ) ;
deserializeMethod . nextControlFlow ( " else " ) ;
deserializeMethod . addCode ( " $[return $T.of( " , nullableClassType ) ;
deserializeMethod . addCode ( typeDeserializeStatement . get ( substring ) ) ;
deserializeMethod . addCode ( " ); \ n$] " ) ;
deserializeMethod . endControlFlow ( ) ;
nullableSerializerClass . addMethod ( deserializeMethod . build ( ) ) ;
}
// Save the resulting class in the main package
try {
writeClass ( outPath , joinPackage ( versionPackage , " serializers " ) , nullableSerializerClass ) ;
} catch ( IOException e ) {
throw new IOError ( e ) ;
}
}
// Create the nullable X types classes
{
var typeType = typeTypes . get ( substring ) ;
2021-04-27 00:08:19 +02:00
var family = typeFamily . get ( substring ) ;
2021-03-01 21:04:06 +01:00
var nullableTypeType = typeTypes . get ( " - " + substring ) ;
2021-05-21 00:20:02 +02:00
var nullableTypeClass = TypeSpec . recordBuilder ( " Nullable " + capitalize ( substring ) ) ;
2021-03-01 21:04:06 +01:00
nullableTypeClass . addModifiers ( Modifier . PUBLIC ) ;
2021-05-21 00:20:02 +02:00
nullableTypeClass . addModifiers ( Modifier . STATIC ) ;
2021-03-01 21:04:06 +01:00
nullableTypeClass . addModifiers ( Modifier . FINAL ) ;
2021-04-27 00:08:19 +02:00
if ( family = = Family . BASIC ) {
nullableTypeClass . addSuperinterface ( ClassName . get ( joinPackage ( versionPackage , " data.nullables " ) ,
" INullableBasicType "
) ) ;
}
if ( family = = Family . GENERIC ) {
nullableTypeClass . addSuperinterface ( ClassName . get ( joinPackage ( versionPackage , " data.nullables " ) ,
" INullableGenericType "
) ) ;
}
2021-03-01 21:04:06 +01:00
nullableTypeClass . addSuperinterface ( ClassName . get ( joinPackage ( versionPackage , " data.nullables " ) ,
2021-04-27 00:08:19 +02:00
" INullableIType "
2021-03-01 21:04:06 +01:00
) ) ;
nullableTypeClass . addSuperinterface ( IGenericNullable . class ) ;
2022-02-20 18:20:05 +01:00
var nullInstance = FieldSpec . builder ( nullableTypeType , " NULL " , Modifier . PRIVATE , Modifier . STATIC , Modifier . FINAL ) ;
nullInstance . initializer ( " new $T(null) " , nullableTypeType ) ;
nullableTypeClass . addField ( nullInstance . build ( ) ) ;
2021-05-21 00:20:02 +02:00
var valueField = ParameterSpec . builder ( typeType , " value " ) ;
nullableTypeClass . addRecordComponent ( valueField . build ( ) ) ;
2021-03-01 21:04:06 +01:00
var ofMethod = MethodSpec . methodBuilder ( " of " ) ;
ofMethod . addModifiers ( Modifier . PUBLIC ) ;
ofMethod . addModifiers ( Modifier . STATIC ) ;
ofMethod . addModifiers ( Modifier . FINAL ) ;
ofMethod . addException ( NullPointerException . class ) ;
ofMethod . returns ( nullableClassType ) ;
ofMethod . addParameter ( ParameterSpec . builder ( typeType , " value " ) . build ( ) ) ;
2022-02-20 18:20:05 +01:00
ofMethod . beginControlFlow ( " if (value != null) " ) ;
2021-03-01 21:04:06 +01:00
ofMethod . addStatement ( " return new $T(value) " , nullableTypeType ) ;
2022-02-20 18:20:05 +01:00
ofMethod . nextControlFlow ( " else " ) ;
ofMethod . addStatement ( " throw new $T() " , NullPointerException . class ) ;
2021-03-01 21:04:06 +01:00
ofMethod . endControlFlow ( ) ;
nullableTypeClass . addMethod ( ofMethod . build ( ) ) ;
var ofNullableMethod = MethodSpec . methodBuilder ( " ofNullable " ) ;
ofNullableMethod . addModifiers ( Modifier . PUBLIC ) ;
ofNullableMethod . addModifiers ( Modifier . STATIC ) ;
ofNullableMethod . addModifiers ( Modifier . FINAL ) ;
ofNullableMethod . returns ( nullableClassType ) ;
ofNullableMethod . addParameter ( ParameterSpec . builder ( typeType , " value " ) . build ( ) ) ;
2022-02-20 18:20:05 +01:00
ofNullableMethod . beginControlFlow ( " if (value != null) " ) ;
2021-03-01 21:04:06 +01:00
ofNullableMethod . addStatement ( " return new $T(value) " , nullableTypeType ) ;
2022-02-20 18:20:05 +01:00
ofNullableMethod . nextControlFlow ( " else " ) ;
ofNullableMethod . addStatement ( " return NULL " ) ;
ofNullableMethod . endControlFlow ( ) ;
2021-03-01 21:04:06 +01:00
nullableTypeClass . addMethod ( ofNullableMethod . build ( ) ) ;
var emptyMethod = MethodSpec . methodBuilder ( " empty " ) ;
emptyMethod . addModifiers ( Modifier . PUBLIC ) ;
emptyMethod . addModifiers ( Modifier . STATIC ) ;
emptyMethod . addModifiers ( Modifier . FINAL ) ;
emptyMethod . returns ( nullableClassType ) ;
2022-02-20 18:20:05 +01:00
emptyMethod . addStatement ( " return NULL " ) ;
2021-03-01 21:04:06 +01:00
nullableTypeClass . addMethod ( emptyMethod . build ( ) ) ;
var isEmptyMethod = MethodSpec . methodBuilder ( " isEmpty " ) ;
isEmptyMethod . addModifiers ( Modifier . PUBLIC ) ;
isEmptyMethod . addModifiers ( Modifier . FINAL ) ;
isEmptyMethod . returns ( TypeName . BOOLEAN ) ;
isEmptyMethod . addStatement ( " return value == null " ) ;
nullableTypeClass . addMethod ( isEmptyMethod . build ( ) ) ;
var isPresentMethod = MethodSpec . methodBuilder ( " isPresent " ) ;
isPresentMethod . addModifiers ( Modifier . PUBLIC ) ;
isPresentMethod . addModifiers ( Modifier . FINAL ) ;
isPresentMethod . returns ( TypeName . BOOLEAN ) ;
isPresentMethod . addStatement ( " return value != null " ) ;
nullableTypeClass . addMethod ( isPresentMethod . build ( ) ) ;
var getMethod = MethodSpec . methodBuilder ( " get " ) ;
getMethod . addModifiers ( Modifier . PUBLIC ) ;
getMethod . addModifiers ( Modifier . FINAL ) ;
getMethod . addException ( NullPointerException . class ) ;
getMethod . addAnnotation ( NotNull . class ) ;
getMethod . returns ( typeType ) ;
getMethod . beginControlFlow ( " if (value == null) " ) ;
getMethod . addStatement ( " throw new $T() " , NullPointerException . class ) ;
getMethod . nextControlFlow ( " else " ) ;
getMethod . addStatement ( " return value " ) ;
getMethod . endControlFlow ( ) ;
nullableTypeClass . addMethod ( getMethod . build ( ) ) ;
var orElseMethod = MethodSpec . methodBuilder ( " orElse " ) ;
orElseMethod . addParameter ( ParameterSpec
. builder ( typeType , " defaultValue " )
. addAnnotation ( NotNull . class )
. build ( ) ) ;
orElseMethod . addModifiers ( Modifier . PUBLIC ) ;
orElseMethod . addModifiers ( Modifier . FINAL ) ;
orElseMethod . addException ( NullPointerException . class ) ;
orElseMethod . addAnnotation ( NotNull . class ) ;
orElseMethod . returns ( typeType ) ;
orElseMethod . beginControlFlow ( " if (value == null) " ) ;
orElseMethod . addStatement ( " return defaultValue " ) ;
orElseMethod . nextControlFlow ( " else " ) ;
orElseMethod . addStatement ( " return value " ) ;
orElseMethod . endControlFlow ( ) ;
nullableTypeClass . addMethod ( orElseMethod . build ( ) ) ;
var getNullableMethod = MethodSpec . methodBuilder ( " getNullable " ) ;
getNullableMethod . addModifiers ( Modifier . PUBLIC ) ;
getNullableMethod . addModifiers ( Modifier . FINAL ) ;
getNullableMethod . addAnnotation ( Nullable . class ) ;
getNullableMethod . returns ( typeType ) ;
getNullableMethod . addStatement ( " return value " ) ;
nullableTypeClass . addMethod ( getNullableMethod . build ( ) ) ;
var getDollarNullableMethod = MethodSpec . methodBuilder ( " $getNullable " ) ;
getDollarNullableMethod . addModifiers ( Modifier . PUBLIC ) ;
getDollarNullableMethod . addModifiers ( Modifier . FINAL ) ;
getDollarNullableMethod . addAnnotation ( Nullable . class ) ;
getDollarNullableMethod . addAnnotation ( Override . class ) ;
getDollarNullableMethod . returns ( typeType ) ;
getDollarNullableMethod . addStatement ( " return this.getNullable() " ) ;
nullableTypeClass . addMethod ( getDollarNullableMethod . build ( ) ) ;
2021-04-27 00:08:19 +02:00
if ( family = = Family . BASIC ) {
var getBasicType = MethodSpec . methodBuilder ( " getBasicType$ " ) ;
getBasicType . addModifiers ( Modifier . PUBLIC ) ;
getBasicType . addModifiers ( Modifier . FINAL ) ;
getBasicType . addException ( NullPointerException . class ) ;
getBasicType . addAnnotation ( NotNull . class ) ;
getBasicType . returns ( ClassName . get ( joinPackage ( basePackageName , " " ) , " BasicType " ) ) ;
getBasicType . addStatement ( " return $T. " + capitalize ( substring ) , ClassName . get ( joinPackage ( basePackageName , " " ) , " BasicType " ) ) ;
nullableTypeClass . addMethod ( getBasicType . build ( ) ) ;
}
if ( family = = Family . GENERIC ) {
var getBasicType = MethodSpec . methodBuilder ( " getGenericType$ " ) ;
getBasicType . addModifiers ( Modifier . PUBLIC ) ;
getBasicType . addModifiers ( Modifier . FINAL ) ;
getBasicType . addException ( NullPointerException . class ) ;
getBasicType . addAnnotation ( NotNull . class ) ;
getBasicType . returns ( ClassName . get ( joinPackage ( basePackageName , " " ) , " GenericType " ) ) ;
getBasicType . addStatement ( " return $T. " + capitalize ( substring ) , ClassName . get ( joinPackage ( basePackageName , " " ) , " GenericType " ) ) ;
nullableTypeClass . addMethod ( getBasicType . build ( ) ) ;
}
2021-03-01 21:04:06 +01:00
try {
writeClass ( outPath , joinPackage ( versionPackage , " data.nullables " ) , nullableTypeClass ) ;
} catch ( IOException e ) {
throw new IOError ( e ) ;
}
}
}
}
}
}
// Generate the array types and serializers
{
for ( String type : typeTypes . keySet ( ) ) {
if ( type . startsWith ( " § " ) ) {
if ( typeMustGenerateSerializer . get ( type ) ) {
String substring = type . substring ( 1 ) ;
var classType = ClassName . get ( joinPackage ( versionPackage , " data " ) , substring ) ;
2021-05-25 11:17:44 +02:00
var arrayClassType = getImmutableArrayType ( classType ) ;
2021-03-01 21:04:06 +01:00
// Create the array X serializer class
{
var arraySerializerClass = TypeSpec . classBuilder ( " Array " + substring + " Serializer " ) ;
arraySerializerClass . addModifiers ( Modifier . PUBLIC ) ;
arraySerializerClass . addModifiers ( Modifier . FINAL ) ;
arraySerializerClass . addSuperinterface ( ParameterizedTypeName . get ( ClassName . get (
" it.cavallium.data.generator " ,
" DataSerializer "
) , arrayClassType ) ) ;
// Create the INSTANCE field
{
var thisSerializerClassType = ClassName . get ( joinPackage ( versionPackage , " serializers " ) ,
" Array " + substring + " Serializer "
) ;
var instanceField = FieldSpec
. builder ( thisSerializerClassType , " INSTANCE " )
. initializer ( " new $T() " , thisSerializerClassType ) ;
instanceField . addModifiers ( Modifier . PUBLIC ) ;
instanceField . addModifiers ( Modifier . STATIC ) ;
instanceField . addModifiers ( Modifier . FINAL ) ;
arraySerializerClass . addField ( instanceField . build ( ) ) ;
}
// Create the serialize method
{
var serializeMethod = createEmptySerializeMethod ( arrayClassType ) ;
2021-05-25 11:17:44 +02:00
serializeMethod . addStatement ( " dataOutput.writeInt(data.size()) " ) ;
serializeMethod . beginControlFlow ( " for (int i = 0; i < data.size(); i++) " ) ;
serializeMethod . addStatement ( typeSerializeStatement . get ( substring ) . generate ( " data.get(i) " ) ) ;
2021-03-01 21:04:06 +01:00
serializeMethod . endControlFlow ( ) ;
arraySerializerClass . addMethod ( serializeMethod . build ( ) ) ;
}
// Create the deserialize method
{
var deserializeMethod = createEmptyDeserializeMethod ( arrayClassType ) ;
2021-05-25 11:17:44 +02:00
deserializeMethod . addStatement ( " int length = dataInput.readInt() " ) ;
deserializeMethod . addStatement ( " var data = new $T[length] " , classType ) ;
deserializeMethod . beginControlFlow ( " for (int i = 0; i < length; i++) " ) ;
deserializeMethod . addStatement ( CodeBlock . join ( List . of ( CodeBlock . of ( " data[i] = " ) ,
2021-03-01 21:04:06 +01:00
typeDeserializeStatement . get ( substring )
2021-05-25 11:17:44 +02:00
) , " " ) ) ;
2021-03-01 21:04:06 +01:00
deserializeMethod . endControlFlow ( ) ;
2021-05-25 11:17:44 +02:00
deserializeMethod . addStatement ( " return $T.of(data) " ,
( arrayClassType instanceof ParameterizedTypeName
? ( ( ParameterizedTypeName ) arrayClassType ) . rawType : arrayClassType )
) ;
2021-03-01 21:04:06 +01:00
arraySerializerClass . addMethod ( deserializeMethod . build ( ) ) ;
}
// Save the resulting class in the main package
try {
writeClass ( outPath , joinPackage ( versionPackage , " serializers " ) , arraySerializerClass ) ;
} catch ( IOException e ) {
throw new IOError ( e ) ;
}
}
}
}
}
}
// Generate the basic types serializers
{
for ( Entry < String , ClassConfiguration > classConfigurationEntry : versionConfiguration . classes . entrySet ( ) ) {
String type = classConfigurationEntry . getKey ( ) ;
ClassConfiguration basicTypeConfiguration = classConfigurationEntry . getValue ( ) ;
var classType = ClassName . get ( joinPackage ( versionPackage , " data " ) , type ) ;
// Create the basic X serializer class
{
var serializerClass = TypeSpec . classBuilder ( type + " Serializer " ) ;
serializerClass . addModifiers ( Modifier . PUBLIC ) ;
serializerClass . addModifiers ( Modifier . FINAL ) ;
serializerClass . addSuperinterface ( ParameterizedTypeName . get ( ClassName . get (
" it.cavallium.data.generator " ,
" DataSerializer "
) , classType ) ) ;
// Create the INSTANCE field
{
var thisSerializerClassType = ClassName . get ( joinPackage ( versionPackage , " serializers " ) ,
type + " Serializer "
) ;
var instanceField = FieldSpec
. builder ( thisSerializerClassType , " INSTANCE " )
. initializer ( " new $T() " , thisSerializerClassType ) ;
instanceField . addModifiers ( Modifier . PUBLIC ) ;
instanceField . addModifiers ( Modifier . STATIC ) ;
instanceField . addModifiers ( Modifier . FINAL ) ;
serializerClass . addField ( instanceField . build ( ) ) ;
}
// Create the serialize method
{
var serializeMethod = createEmptySerializeMethod ( classType ) ;
if ( basicTypeConfiguration . data ! = null ) {
for ( Entry < String , String > entry : basicTypeConfiguration . data . entrySet ( ) ) {
String field = entry . getKey ( ) ;
String fieldType = entry . getValue ( ) ;
serializeMethod . addStatement ( typeSerializeStatement
. get ( fieldType )
2021-07-13 16:37:02 +02:00
. generate ( " data. " + field + " () " ) ) ;
2021-03-01 21:04:06 +01:00
}
}
serializerClass . addMethod ( serializeMethod . build ( ) ) ;
}
// Create the deserialize method
{
var deserializeMethod = createEmptyDeserializeMethod ( classType ) ;
deserializeMethod . addCode ( " $[return $T.of( \ n$] " , classType ) ;
deserializeMethod . addCode ( " $> " ) ;
AtomicInteger i = new AtomicInteger ( basicTypeConfiguration . data . size ( ) ) ;
for ( Entry < String , String > entry : basicTypeConfiguration . data . entrySet ( ) ) {
String field = entry . getKey ( ) ;
String fieldType = entry . getValue ( ) ;
boolean isLast = i . decrementAndGet ( ) = = 0 ;
deserializeMethod . addCode ( typeDeserializeStatement . get ( fieldType ) ) . addCode ( ( isLast ? " " : " , " ) + " \ n " ) ;
}
deserializeMethod . addCode ( " $< " ) ;
deserializeMethod . addStatement ( " ) " ) ;
serializerClass . addMethod ( deserializeMethod . build ( ) ) ;
}
// Save the resulting class in the main package
try {
writeClass ( outPath , joinPackage ( versionPackage , " serializers " ) , serializerClass ) ;
} catch ( IOException e ) {
throw new IOError ( e ) ;
}
}
// Create the basic X upgrader class
{
if ( nextVersion . isPresent ( ) ) {
var nextVersionClassType = ClassName . get ( joinPackage ( nextVersionPackage . get ( ) , " data " ) , type ) ;
var upgraderClass = TypeSpec . classBuilder ( type + " Upgrader " ) ;
upgraderClass . addModifiers ( Modifier . PUBLIC ) ;
upgraderClass . addModifiers ( Modifier . FINAL ) ;
upgraderClass . addSuperinterface ( ParameterizedTypeName . get ( ClassName . get (
" it.cavallium.data.generator " ,
" DataUpgrader "
) , classType , nextVersionClassType ) ) ;
// Create the INSTANCE field
{
var thisUpgraderClassType = ClassName . get ( joinPackage ( versionPackage , " upgraders " ) , type + " Upgrader " ) ;
var instanceField = FieldSpec
. builder ( thisUpgraderClassType , " INSTANCE " )
. initializer ( " new $T() " , thisUpgraderClassType ) ;
instanceField . addModifiers ( Modifier . PUBLIC ) ;
instanceField . addModifiers ( Modifier . STATIC ) ;
instanceField . addModifiers ( Modifier . FINAL ) ;
upgraderClass . addField ( instanceField . build ( ) ) ;
}
// Create the upgrade method
{
var deserializeMethod = MethodSpec . methodBuilder ( " upgrade " ) ;
deserializeMethod . addAnnotation ( NotNull . class ) ;
deserializeMethod . addAnnotation ( Override . class ) ;
deserializeMethod . addModifiers ( Modifier . PUBLIC ) ;
deserializeMethod . addModifiers ( Modifier . FINAL ) ;
deserializeMethod . returns ( nextVersionClassType ) ;
deserializeMethod . addParameter ( ParameterSpec
. builder ( classType , " data " )
. addAnnotation ( NotNull . class )
. build ( ) ) ;
deserializeMethod . addException ( IOException . class ) ;
Object2IntLinkedOpenHashMap < String > currentVarNumber = new Object2IntLinkedOpenHashMap < > (
basicTypeConfiguration . getData ( ) . size ( ) ) ;
2021-04-27 00:08:19 +02:00
Object2ObjectLinkedOpenHashMap < String , String > currentVarTypeName = new Object2ObjectLinkedOpenHashMap < > (
basicTypeConfiguration . getData ( ) . size ( ) ) ;
Object2ObjectLinkedOpenHashMap < String , TypeName > currentVarTypeClass = new Object2ObjectLinkedOpenHashMap < > (
basicTypeConfiguration . getData ( ) . size ( ) ) ;
Object2ObjectLinkedOpenHashMap < String , Family > currentVarFamily = new Object2ObjectLinkedOpenHashMap < > (
basicTypeConfiguration . getData ( ) . size ( ) ) ;
ObjectOpenHashSet < String > currentVarUpgraded = new ObjectOpenHashSet < > (
basicTypeConfiguration . getData ( ) . size ( ) ) ;
2021-03-01 21:04:06 +01:00
ObjectOpenHashSet < String > currentVarDeleted = new ObjectOpenHashSet < > ( ) ;
currentVarNumber . defaultReturnValue ( - 1 ) ;
deserializeMethod . addStatement ( " $T.requireNonNull(data) " , Objects . class ) ;
for ( Entry < String , String > stringStringEntry : basicTypeConfiguration . getData ( ) . entrySet ( ) ) {
String k = stringStringEntry . getKey ( ) ;
String value = stringStringEntry . getValue ( ) ;
currentVarNumber . addTo ( k , 1 ) ;
2021-04-27 00:08:19 +02:00
currentVarTypeName . put ( k , value ) ;
currentVarTypeClass . put ( k , typeTypes . get ( value ) ) ;
currentVarFamily . put ( k , typeFamily . get ( value ) ) ;
currentVarUpgraded . remove ( k ) ;
2021-07-13 16:37:02 +02:00
deserializeMethod . addStatement ( " var $$field$$ " + 0 + " $$ " + k + " = data. " + k + " () " ) ;
2021-03-01 21:04:06 +01:00
}
List < VersionTransformation > list = new ArrayList < > ( ) ;
for ( VersionTransformation versionTransformation : configuration . versions . get ( nextVersion . get ( ) ) . transformations ) {
if ( versionTransformation . isForClass ( type ) ) {
list . add ( versionTransformation ) ;
}
}
var transformations = Collections . unmodifiableList ( list ) ;
AtomicInteger transformationNumber = new AtomicInteger ( 0 ) ;
HashMap < String , TypeName > currentTransformedFieldTypes = new HashMap < > ( ) ;
for ( Entry < String , ClassConfiguration > stringClassConfigurationEntry : versionConfiguration . classes . entrySet ( ) ) {
String className = stringClassConfigurationEntry . getKey ( ) ;
ClassConfiguration classConfiguration = stringClassConfigurationEntry . getValue ( ) ;
for ( Entry < String , String > entry : classConfiguration . getData ( ) . entrySet ( ) ) {
String fieldName = entry . getKey ( ) ;
String fieldType = entry . getValue ( ) ;
currentTransformedFieldTypes . put ( className + " . " + fieldName , typeTypes . get ( fieldType ) ) ;
}
}
2021-04-26 18:07:18 +02:00
HashMap < ClassName , String > dataUpgraderInstanceFieldName = new HashMap < > ( ) ;
AtomicInteger nextDataUpgraderInstanceFieldId = new AtomicInteger ( 0 ) ;
HashMap < ClassName , String > dataInitializerInstanceFieldName = new HashMap < > ( ) ;
AtomicInteger nextDataInitializerInstanceFieldId = new AtomicInteger ( 0 ) ;
2021-03-01 21:04:06 +01:00
for ( VersionTransformation transformationConfig : transformations ) {
var transformation = transformationConfig . getTransformation ( ) ;
deserializeMethod . addCode ( " \ n " ) ;
deserializeMethod . addComment ( " TRANSFORMATION # " + transformationNumber . incrementAndGet ( ) + " : "
+ transformation . getTransformName ( ) ) ;
switch ( transformation . getTransformName ( ) ) {
case " remove-data " :
var removeDataTransformation = ( RemoveDataConfiguration ) transformation ;
{
deserializeMethod . addComment (
" Deleted $$field$$ " + currentVarNumber . getInt ( removeDataTransformation . from ) + " $$ "
+ removeDataTransformation . from ) ;
currentVarNumber . addTo ( removeDataTransformation . from , 1 ) ;
2021-04-27 00:08:19 +02:00
currentVarTypeName . remove ( removeDataTransformation . from ) ;
currentVarTypeClass . remove ( removeDataTransformation . from ) ;
currentVarFamily . remove ( removeDataTransformation . from ) ;
currentVarUpgraded . remove ( removeDataTransformation . from ) ;
2021-03-01 21:04:06 +01:00
currentVarDeleted . add ( removeDataTransformation . from ) ;
}
Objects . requireNonNull ( currentTransformedFieldTypes . remove (
removeDataTransformation . transformClass + " . " + removeDataTransformation . from ) ) ;
break ;
case " move-data " :
var moveDataTransformation = ( MoveDataConfiguration ) transformation ;
{
currentVarNumber . addTo ( moveDataTransformation . to , 1 ) ;
2021-04-27 00:08:19 +02:00
currentVarTypeName . put ( moveDataTransformation . to ,
Objects . requireNonNull ( currentVarTypeName . get ( moveDataTransformation . from ) )
) ;
currentVarTypeClass . put ( moveDataTransformation . to ,
Objects . requireNonNull ( currentVarTypeClass . get ( moveDataTransformation . from ) )
) ;
currentVarFamily . put ( moveDataTransformation . to ,
Objects . requireNonNull ( currentVarFamily . get ( moveDataTransformation . from ) )
) ;
if ( currentVarUpgraded . remove ( moveDataTransformation . from ) ) {
currentVarUpgraded . add ( moveDataTransformation . to ) ;
}
2021-03-01 21:04:06 +01:00
currentVarDeleted . remove ( moveDataTransformation . to ) ;
deserializeMethod . addStatement (
" var $$field$$ " + currentVarNumber . getInt ( moveDataTransformation . to ) + " $$ "
+ moveDataTransformation . to + " = $$field$$ " + currentVarNumber . getInt (
moveDataTransformation . from ) + " $$ " + moveDataTransformation . from ) ;
}
{
deserializeMethod . addComment (
" Deleted $$field$$ " + currentVarNumber . getInt ( moveDataTransformation . from ) + " $$ "
+ moveDataTransformation . from ) ;
currentVarNumber . addTo ( moveDataTransformation . from , 1 ) ;
2021-04-27 00:08:19 +02:00
currentVarTypeName . remove ( moveDataTransformation . from ) ;
currentVarTypeClass . remove ( moveDataTransformation . from ) ;
currentVarFamily . remove ( moveDataTransformation . from ) ;
currentVarUpgraded . remove ( moveDataTransformation . from ) ;
2021-03-01 21:04:06 +01:00
currentVarDeleted . add ( moveDataTransformation . from ) ;
}
currentTransformedFieldTypes . put (
moveDataTransformation . transformClass + " . " + moveDataTransformation . to ,
Objects . requireNonNull ( currentTransformedFieldTypes . remove (
moveDataTransformation . transformClass + " . " + moveDataTransformation . from ) )
) ;
break ;
case " upgrade-data " :
var upgradeDataTransformation = ( UpgradeDataConfiguration ) transformation ;
TypeName fromType = currentTransformedFieldTypes . get (
upgradeDataTransformation . transformClass + " . " + upgradeDataTransformation . from ) ;
TypeName fromTypeBoxed = fromType . isPrimitive ( ) ? fromType . box ( ) : fromType ;
String toTypeName = configuration . versions . get ( nextVersion . get ( ) ) . classes
. get ( upgradeDataTransformation . transformClass )
. getData ( )
. get ( upgradeDataTransformation . from ) ;
TypeName toType = nextVersionTypeTypes . get ( toTypeName ) ;
TypeName toTypeBoxed = toType . isPrimitive ( ) ? toType . box ( ) : toType ;
deserializeMethod . addStatement (
" $T $$field$$ " + ( currentVarNumber . getInt ( upgradeDataTransformation . from ) + 1 ) + " $$ "
+ upgradeDataTransformation . from , toType ) ;
2021-04-26 18:07:18 +02:00
var dataUpgraderClass = ClassName . bestGuess ( upgradeDataTransformation . upgrader ) ;
var dataUpgraderFieldName = dataUpgraderInstanceFieldName . computeIfAbsent ( dataUpgraderClass ,
_unused - > {
var dataUpgraderType = ParameterizedTypeName . get ( ClassName . get ( DataUpgrader . class ) ,
fromTypeBoxed ,
toTypeBoxed
) ;
var fieldName = " DATA_UPGRADER_ " + nextDataUpgraderInstanceFieldId . getAndIncrement ( ) ;
var fieldSpec = FieldSpec
. builder ( dataUpgraderType ,
fieldName ,
Modifier . PRIVATE ,
Modifier . STATIC ,
Modifier . FINAL
) ;
fieldSpec . initializer ( " ($T) new $T() " , dataUpgraderType , dataUpgraderClass ) ;
upgraderClass . addField ( fieldSpec . build ( ) ) ;
return fieldName ;
}
2021-03-01 21:04:06 +01:00
) ;
deserializeMethod . addStatement (
2021-04-26 18:07:18 +02:00
" $T upgraded = ($T) " + dataUpgraderFieldName + " .upgrade(($T) $$field$$ " + currentVarNumber . getInt (
2021-03-01 21:04:06 +01:00
upgradeDataTransformation . from ) + " $$ " + upgradeDataTransformation . from + " ) " ,
toType ,
toTypeBoxed ,
fromTypeBoxed
) ;
deserializeMethod . addStatement (
" $$field$$ " + ( currentVarNumber . getInt ( upgradeDataTransformation . from ) + 1 ) + " $$ "
+ upgradeDataTransformation . from + " = upgraded " ) ;
Objects . requireNonNull ( currentTransformedFieldTypes . remove (
upgradeDataTransformation . transformClass + " . " + upgradeDataTransformation . from ) ) ;
currentTransformedFieldTypes . put (
upgradeDataTransformation . transformClass + " . " + upgradeDataTransformation . from , toType ) ;
currentVarNumber . addTo ( upgradeDataTransformation . from , 1 ) ;
2021-04-27 00:08:19 +02:00
currentVarTypeName . put ( upgradeDataTransformation . from , toTypeName ) ;
currentVarTypeClass . put ( upgradeDataTransformation . from , toType ) ;
currentVarFamily . put ( upgradeDataTransformation . from ,
Objects . requireNonNull ( typeFamily . get ( toTypeName ) ,
( ) - > " Type \" " + toTypeName + " \" has no type family! "
)
) ;
currentVarUpgraded . add ( upgradeDataTransformation . from ) ;
2021-03-01 21:04:06 +01:00
currentVarDeleted . remove ( upgradeDataTransformation . from ) ;
break ;
case " new-data " :
var newDataTransformation = ( NewDataConfiguration ) transformation ;
String newTypeName = configuration . versions . get ( nextVersion . get ( ) ) . classes
. get ( newDataTransformation . transformClass )
. getData ( )
. get ( newDataTransformation . to ) ;
TypeName newType = nextVersionTypeTypes . get ( newTypeName ) ;
2022-02-20 18:20:05 +01:00
Objects . requireNonNull ( newType , ( ) - > " Type \" " + newTypeName + " \" is not present from next version " + version + " to version " + nextVersion . get ( ) + " in upgrader " + newDataTransformation . transformClass + " . " + newDataTransformation . to ) ;
2021-03-01 21:04:06 +01:00
TypeName newTypeBoxed = newType . isPrimitive ( ) ? newType . box ( ) : newType ;
2021-06-07 16:23:01 +02:00
{
currentVarNumber . addTo ( newDataTransformation . to , 1 ) ;
currentVarTypeName . put ( newDataTransformation . to , newTypeName ) ;
currentVarTypeClass . put ( newDataTransformation . to , newType ) ;
currentVarFamily . put ( newDataTransformation . to , Objects . requireNonNull ( typeFamily . get ( newTypeName ) ,
( ) - > " Type \" " + newTypeName + " \" has no type family! "
) ) ;
currentVarUpgraded . add ( newDataTransformation . to ) ;
currentVarDeleted . remove ( newDataTransformation . to ) ;
var dataInitializerClass = ClassName . bestGuess ( newDataTransformation . initializer ) ;
var dataInitializerFieldName = dataInitializerInstanceFieldName . computeIfAbsent ( dataInitializerClass ,
_unused - > {
var dataInitializerType = ParameterizedTypeName . get ( ClassName . get ( DataInitializer . class ) ,
newTypeBoxed
) ;
var fieldName = " DATA_INITIALIZER_ " + nextDataInitializerInstanceFieldId . getAndIncrement ( ) ;
var fieldSpec = FieldSpec
. builder ( dataInitializerType ,
fieldName ,
Modifier . PRIVATE ,
Modifier . STATIC ,
Modifier . FINAL
) ;
fieldSpec . initializer ( " ($T) new $T() " , dataInitializerType , dataInitializerClass ) ;
upgraderClass . addField ( fieldSpec . build ( ) ) ;
return fieldName ;
}
) ;
deserializeMethod . addStatement (
" var $$field$$ " + currentVarNumber . getInt ( newDataTransformation . to ) + " $$ "
+ newDataTransformation . to + " = " + dataInitializerFieldName + " .initialize() "
) ;
}
if ( currentTransformedFieldTypes . put (
newDataTransformation . transformClass + " . " + newDataTransformation . to , newType ) ! = null ) {
throw new IllegalStateException ( ) ;
}
break ;
2021-03-01 21:04:06 +01:00
default :
throw new UnsupportedOperationException (
" Unknown transform type: " + transformation . getTransformName ( ) ) ;
}
}
deserializeMethod . addCode ( " \ n " ) ;
deserializeMethod . addComment (
" Upgrade the remaining untouched values to the new version before returning " ) ;
var nextVersionFieldTypes = configuration . versions . get ( nextVersion . get ( ) ) . classes . get ( type ) . getData ( ) ;
for ( var e : currentVarNumber . object2IntEntrySet ( ) ) {
String key = e . getKey ( ) ;
int number = e . getIntValue ( ) ;
2021-04-27 00:08:19 +02:00
if ( ! currentVarDeleted . contains ( key ) & & ! currentVarUpgraded . contains ( key ) ) {
Family currentFamily = Objects . requireNonNull ( currentVarFamily . get ( key ) ) ;
switch ( currentFamily ) {
case OTHER :
case SPECIAL_NATIVE :
// Don't upgrade "other" families because they are already upgraded
continue ;
}
2021-03-01 21:04:06 +01:00
String toTypeName = nextVersionFieldTypes . get ( key ) ;
2021-04-27 00:08:19 +02:00
Family toFamily = typeFamily . get ( toTypeName ) ;
2021-03-01 21:04:06 +01:00
TypeName toType = nextVersionTypeTypes . get ( toTypeName ) ;
TypeName toTypeBoxed = toType . isPrimitive ( ) ? toType . box ( ) : toType ;
{
currentVarNumber . addTo ( key , 1 ) ;
2021-04-27 00:08:19 +02:00
currentVarTypeName . put ( key , toTypeName ) ;
currentVarTypeClass . put ( key , toType ) ;
currentVarFamily . put ( key , toFamily ) ;
currentVarUpgraded . add ( key ) ;
2021-03-01 21:04:06 +01:00
currentVarDeleted . remove ( key ) ;
2021-04-27 00:08:19 +02:00
switch ( currentFamily ) {
case BASIC :
case GENERIC :
deserializeMethod . addCode ( buildStatementUpgradeBasicType (
versionPackage ,
nextVersionPackage . get ( ) ,
number ,
key ,
toTypeName ,
toFamily ,
toType ,
toTypeBoxed
) ) ;
break ;
case I_TYPE_ARRAY :
deserializeMethod . addCode ( buildStatementUpgradeITypeArrayField (
versionPackage ,
nextVersionPackage . get ( ) ,
number ,
key ,
toTypeName ,
toFamily ,
toType ,
toTypeBoxed
) ) ;
break ;
case NULLABLE_BASIC :
deserializeMethod . addCode ( buildStatementUpgradeNullableBasicField (
versionPackage ,
nextVersionPackage . get ( ) ,
number ,
key ,
toTypeName ,
toFamily ,
toType ,
toTypeBoxed ,
nextVersionTypeTypes . get ( toTypeName . substring ( 1 ) )
) ) ;
break ;
case NULLABLE_GENERIC :
deserializeMethod . addCode ( buildStatementUpgradeNullableGenericField (
versionPackage ,
nextVersionPackage . get ( ) ,
number ,
key ,
toTypeName ,
toFamily ,
toType ,
toTypeBoxed ,
nextVersionTypeTypes . get ( toTypeName . substring ( 1 ) )
) ) ;
break ;
case NULLABLE_OTHER :
deserializeMethod . addCode ( buildStatementUpgradeNullableOtherField (
versionPackage ,
nextVersionPackage . get ( ) ,
number ,
key ,
toTypeName ,
toFamily ,
toType ,
toTypeBoxed
) ) ;
break ;
default :
throw new IllegalStateException ( " Unexpected value: " + currentFamily ) ;
}
2021-03-01 21:04:06 +01:00
}
}
}
deserializeMethod . addCode ( " return $T.of( " , nextVersionClassType ) ;
AtomicBoolean isFirst = new AtomicBoolean ( true ) ;
for ( Entry < String , String > entry : nextVersionFieldTypes . entrySet ( ) ) {
String field = entry . getKey ( ) ;
String fieldType = entry . getValue ( ) ;
if ( ! isFirst . getAndSet ( false ) ) {
deserializeMethod . addCode ( " , " ) ;
}
2021-06-07 16:23:01 +02:00
if ( currentVarNumber . getInt ( field ) < 0 ) {
throw new IllegalStateException (
" Field " + field + " in class " + type + " has an invalid var number ( "
+ currentVarNumber . getInt ( field ) + " ) after upgrading from version " + version
+ " to version " + nextVersion . orElse ( " --- " ) ) ;
}
2021-03-01 21:04:06 +01:00
deserializeMethod . addCode ( " $$field$$ " + currentVarNumber . getInt ( field ) + " $$ " + field ) ;
}
deserializeMethod . addStatement ( " ) " ) ;
upgraderClass . addMethod ( deserializeMethod . build ( ) ) ;
}
// Save the resulting class in the main package
try {
writeClass ( outPath , joinPackage ( versionPackage , " upgraders " ) , upgraderClass ) ;
} catch ( IOException e ) {
throw new IOError ( e ) ;
}
}
}
}
}
// Generate the super types serializers
{
for ( Entry < String , Set < String > > entry : versionConfiguration . superTypes . entrySet ( ) ) {
String type = entry . getKey ( ) ;
Set < String > superTypeConfiguration = entry . getValue ( ) ;
var classType = ClassName . get ( joinPackage ( versionPackage , " data " ) , type ) ;
// Create the super X serializer class
{
var serializerClass = TypeSpec . classBuilder ( type + " Serializer " ) ;
serializerClass . addModifiers ( Modifier . PUBLIC ) ;
serializerClass . addModifiers ( Modifier . FINAL ) ;
serializerClass . addSuperinterface ( ParameterizedTypeName . get ( ClassName . get (
" it.cavallium.data.generator " ,
" DataSerializer "
) , classType ) ) ;
// Create the INSTANCE field
{
var thisSerializerClassType = ClassName . get ( joinPackage ( versionPackage , " serializers " ) ,
type + " Serializer "
) ;
var instanceField = FieldSpec
. builder ( thisSerializerClassType , " INSTANCE " )
. initializer ( " new $T() " , thisSerializerClassType ) ;
instanceField . addModifiers ( Modifier . PUBLIC ) ;
instanceField . addModifiers ( Modifier . STATIC ) ;
instanceField . addModifiers ( Modifier . FINAL ) ;
serializerClass . addField ( instanceField . build ( ) ) ;
}
// Create the checkIdValidity method
{
var checkIdValidityMethod = MethodSpec . methodBuilder ( " checkIdValidity " ) ;
checkIdValidityMethod . addModifiers ( Modifier . PUBLIC ) ;
checkIdValidityMethod . addModifiers ( Modifier . STATIC ) ;
checkIdValidityMethod . addModifiers ( Modifier . FINAL ) ;
checkIdValidityMethod . returns ( TypeName . VOID ) ;
checkIdValidityMethod . addParameter ( ParameterSpec . builder ( TypeName . INT , " id " ) . build ( ) ) ;
checkIdValidityMethod . addException ( IOException . class ) ;
checkIdValidityMethod . addStatement ( " if (id < 0) throw new $T(new $T(id)) " ,
IOException . class ,
IndexOutOfBoundsException . class
) ;
checkIdValidityMethod . addStatement (
" if (id >= " + superTypeConfiguration . size ( ) + " ) throw new $T(new $T(id)) " ,
IOException . class ,
IndexOutOfBoundsException . class
) ;
serializerClass . addMethod ( checkIdValidityMethod . build ( ) ) ;
}
// Create the serialize method
{
var serializeMethod = createEmptySerializeMethod ( classType ) ;
serializeMethod . addStatement ( " int id = data.getMetaId$$ " + type + " () " ) ;
serializeMethod . addCode ( " \ n " ) ;
serializeMethod . addStatement ( " checkIdValidity(id) " ) ;
serializeMethod . addCode ( " \ n " ) ;
serializeMethod . addStatement ( " dataOutput.writeByte(id) " ) ;
serializeMethod . addCode ( " \ n " ) ;
serializeMethod . beginControlFlow ( " switch (id) " ) ;
int i = 0 ;
for ( String subType : superTypeConfiguration ) {
serializeMethod . beginControlFlow ( " case " + i + " : " ) ;
serializeMethod . addStatement ( " $T. " + subType + " SerializerInstance.serialize(dataOutput, ($T) data) " ,
versionClassType ,
ClassName . get ( joinPackage ( versionPackage , " data " ) , subType )
) ;
serializeMethod . addStatement ( " break " ) ;
serializeMethod . endControlFlow ( ) ;
i + + ;
}
serializeMethod . addStatement ( " default: throw new $T(new $T()) " ,
IOException . class ,
IndexOutOfBoundsException . class
) ;
serializeMethod . endControlFlow ( ) ;
serializerClass . addMethod ( serializeMethod . build ( ) ) ;
}
// Create the deserialize method
{
var deserializeMethod = createEmptyDeserializeMethod ( classType ) ;
deserializeMethod . addStatement ( " int id = dataInput.readUnsignedByte() " ) ;
deserializeMethod . addCode ( " \ n " ) ;
deserializeMethod . addStatement ( " checkIdValidity(id) " ) ;
deserializeMethod . addCode ( " \ n " ) ;
deserializeMethod . beginControlFlow ( " switch (id) " ) ;
int i = 0 ;
for ( String subType : superTypeConfiguration ) {
deserializeMethod . addStatement ( " case " + i + " : return " + typeDeserializeStatement . get ( subType ) ) ;
i + + ;
}
deserializeMethod . addStatement ( " default: throw new $T(new $T()) " ,
IOException . class ,
IndexOutOfBoundsException . class
) ;
deserializeMethod . endControlFlow ( ) ;
serializerClass . addMethod ( deserializeMethod . build ( ) ) ;
}
// Save the resulting class in the main package
try {
writeClass ( outPath , joinPackage ( versionPackage , " serializers " ) , serializerClass ) ;
} catch ( IOException e ) {
throw new IOError ( e ) ;
}
}
}
}
// Create the Version class
{
var versionClass = TypeSpec . classBuilder ( " Version " ) ;
versionClass . addSuperinterface ( ParameterizedTypeName . get ( ClassName . get ( joinPackage ( basePackageName , " " ) , " IVersion " ) ,
ClassName . get ( joinPackage ( versionPackage , " data " ) , " IBasicType " )
) ) ;
versionClass . addModifiers ( Modifier . PUBLIC ) ;
versionClass . addModifiers ( Modifier . FINAL ) ;
// Add a static variable for the current version
{
var versionNumberXField = FieldSpec
. builder ( TypeName . INT , " VERSION " )
. addModifiers ( Modifier . PUBLIC )
. addModifiers ( Modifier . STATIC )
. addModifiers ( Modifier . FINAL )
. initializer ( " $T. " + getVersionVarName ( version ) ,
ClassName . get ( joinPackage ( basePackageName , " " ) , " Versions " )
)
. build ( ) ;
versionClass . addField ( versionNumberXField ) ;
}
// Add a static instance for the current version
{
var versionInstanceField = FieldSpec
. builder ( versionClassType , " INSTANCE " )
. addModifiers ( Modifier . PUBLIC )
. addModifiers ( Modifier . STATIC )
. addModifiers ( Modifier . FINAL )
. initializer ( " new $T() " , versionClassType )
. build ( ) ;
versionClass . addField ( versionInstanceField ) ;
}
// Add upgrader instances static fields
{
for ( Entry < String , TypeName > entry : typeOptionalUpgraders . entrySet ( ) ) {
String type = entry . getKey ( ) ;
TypeName upgraderType = entry . getValue ( ) ;
var typeName = type ;
if ( type . startsWith ( " § " ) ) {
typeName = " Array " + type . substring ( 1 ) ;
} else if ( type . startsWith ( " - " ) ) {
typeName = " Nullable " + type . substring ( 1 ) ;
} else {
typeName = type ;
}
var upgraderStaticField = FieldSpec . builder ( upgraderType , typeName + " UpgraderInstance " ) ;
upgraderStaticField . addModifiers ( Modifier . PUBLIC ) ;
upgraderStaticField . addModifiers ( Modifier . STATIC ) ;
upgraderStaticField . addModifiers ( Modifier . FINAL ) ;
upgraderStaticField . initializer ( " new $T() " , upgraderType ) ;
versionClass . addField ( upgraderStaticField . build ( ) ) ;
}
}
// Add serializer instances static fields
{
for ( Entry < String , TypeName > entry : typeOptionalSerializers . entrySet ( ) ) {
String type = entry . getKey ( ) ;
TypeName serializerType = entry . getValue ( ) ;
var typeName = type ;
if ( type . startsWith ( " § " ) ) {
typeName = " Array " + type . substring ( 1 ) ;
} else if ( type . startsWith ( " - " ) ) {
typeName = " Nullable " + type . substring ( 1 ) ;
} else {
typeName = type ;
}
var serializerStaticField = FieldSpec . builder ( serializerType , typeName + " SerializerInstance " ) ;
serializerStaticField . addModifiers ( Modifier . PUBLIC ) ;
serializerStaticField . addModifiers ( Modifier . STATIC ) ;
serializerStaticField . addModifiers ( Modifier . FINAL ) ;
serializerStaticField . initializer ( " new $T() " , serializerType ) ;
versionClass . addField ( serializerStaticField . build ( ) ) ;
}
}
// Add upgradeToNextVersion method
{
if ( nextVersion . isPresent ( ) ) {
var upgradeToNextVersionMethodBuilder = MethodSpec
. methodBuilder ( " upgradeToNextVersion " )
. addModifiers ( Modifier . PUBLIC )
. addModifiers ( Modifier . STATIC )
. addModifiers ( Modifier . FINAL )
. returns ( ClassName . get ( joinPackage ( nextVersionPackage . get ( ) , " data " ) , " IBasicType " ) )
. addException ( IOException . class )
. addParameter ( ParameterSpec
. builder ( ClassName . get ( joinPackage ( versionPackage , " data " ) , " IBasicType " ) , " oldData " )
. build ( ) )
. beginControlFlow ( " switch (oldData.getBasicType$$()) " ) ;
for ( Entry < String , ClassConfiguration > entry : versionConfiguration . classes . entrySet ( ) ) {
String type = entry . getKey ( ) ;
ClassConfiguration typeConfiguration = entry . getValue ( ) ;
var data = typeConfiguration . data ;
upgradeToNextVersionMethodBuilder . addStatement (
" case " + type + " : return $T. " + type + " UpgraderInstance.upgrade(($T) oldData) " ,
versionClassType ,
ClassName . get ( joinPackage ( versionPackage , " data " ) , type )
) ;
}
var upgradeToNextVersionMethod = upgradeToNextVersionMethodBuilder
. beginControlFlow ( " default: " )
. addStatement ( " throw new $T( \" Unknown type: \" + oldData.getBasicType$$()) " , IOException . class )
. endControlFlow ( )
. endControlFlow ( )
. build ( ) ;
versionClass . addMethod ( upgradeToNextVersionMethod ) ;
}
}
// Add getClass method
{
var getClassMethodBuilder = MethodSpec
. methodBuilder ( " getClass " )
. addModifiers ( Modifier . PUBLIC )
. addAnnotation ( Override . class )
. returns ( ParameterizedTypeName . get ( ClassName . get ( Class . class ) ,
WildcardTypeName . subtypeOf ( ClassName . get ( joinPackage ( versionPackage , " data " ) , " IBasicType " ) )
) )
. addParameter ( ParameterSpec
. builder ( ClassName . get ( joinPackage ( basePackageName , " " ) , " BasicType " ) , " type " )
. build ( ) )
. beginControlFlow ( " switch (type) " ) ;
for ( Entry < String , ClassConfiguration > entry : versionConfiguration . classes . entrySet ( ) ) {
String type = entry . getKey ( ) ;
ClassConfiguration typeConfiguration = entry . getValue ( ) ;
var data = typeConfiguration . data ;
getClassMethodBuilder . addStatement ( " case " + type + " : return $T.class " ,
ClassName . get ( joinPackage ( versionPackage , " data " ) , type )
) ;
}
getClassMethodBuilder
. beginControlFlow ( " default: " )
. addStatement ( " throw new $T( \" Unknown type: \" + type) " , IllegalArgumentException . class )
. endControlFlow ( )
. endControlFlow ( ) ;
versionClass . addMethod ( getClassMethodBuilder . build ( ) ) ;
}
// Add getSerializer method
{
var getSerializerMethodBuilder = MethodSpec
. methodBuilder ( " getSerializer " )
. addModifiers ( Modifier . PUBLIC )
. addAnnotation ( Override . class )
. addTypeVariable ( TypeVariableName . get ( " T " ,
ClassName . get ( joinPackage ( versionPackage , " data " ) , " IBasicType " )
) )
. returns ( ParameterizedTypeName . get ( ClassName . get ( DataSerializer . class ) , TypeVariableName . get ( " T " ) ) )
. addException ( IOException . class )
. addParameter ( ParameterSpec
. builder ( ClassName . get ( joinPackage ( basePackageName , " " ) , " BasicType " ) , " type " )
. build ( ) )
. beginControlFlow ( " switch (type) " ) ;
for ( Entry < String , ClassConfiguration > entry : versionConfiguration . classes . entrySet ( ) ) {
String type = entry . getKey ( ) ;
ClassConfiguration typeConfiguration = entry . getValue ( ) ;
var data = typeConfiguration . data ;
getSerializerMethodBuilder . addStatement ( " case " + type + " : return ($T) $T. " + type + " SerializerInstance " ,
ParameterizedTypeName . get ( ClassName . get ( DataSerializer . class ) , TypeVariableName . get ( " T " ) ) ,
versionClassType
) ;
}
getSerializerMethodBuilder
. beginControlFlow ( " default: " )
. addStatement ( " throw new $T( \" Unknown type: \" + type) " , IOException . class )
. endControlFlow ( )
. endControlFlow ( ) ;
versionClass . addMethod ( getSerializerMethodBuilder . build ( ) ) ;
}
// Add createNullableOf method
{
var createNullableOfMethod = MethodSpec
. methodBuilder ( " createNullableOf " )
. addModifiers ( Modifier . PUBLIC )
. addModifiers ( Modifier . FINAL )
2021-04-27 00:08:19 +02:00
. returns ( ClassName . get ( joinPackage ( versionPackage , " data.nullables " ) , " INullableIType " ) )
2021-03-01 21:04:06 +01:00
. addException ( IOException . class )
. addParameter ( ParameterSpec . builder ( String . class , " type " ) . build ( ) )
. addParameter ( ParameterSpec . builder ( Object . class , " content " ) . build ( ) )
. beginControlFlow ( " switch (type) " ) ;
for ( String item : typeTypes . keySet ( ) ) {
if ( item . startsWith ( " - " ) ) {
String type = item . substring ( 1 ) ;
if ( ! specialNativeTypes . contains ( type ) ) {
var nullableType = " Nullable " + type ;
createNullableOfMethod . addStatement ( " case \" " + nullableType + " \" : return $T.ofNullable(($T) content) " ,
typeTypes . get ( " - " + type ) ,
typeTypes . get ( type )
) ;
}
}
}
createNullableOfMethod
. beginControlFlow ( " default: " )
. addStatement ( " throw new $T( \" Unknown nullable type: \" + type) " , IOException . class )
. endControlFlow ( )
. endControlFlow ( ) ;
versionClass . addMethod ( createNullableOfMethod . build ( ) ) ;
}
2021-04-27 00:08:19 +02:00
// Add createNullableOfBasicType method
{
var createNullableOfBasicTypeMethod = MethodSpec
. methodBuilder ( " createNullableOfBasicType " )
. addModifiers ( Modifier . PUBLIC )
. addModifiers ( Modifier . FINAL )
. returns ( ClassName . get ( joinPackage ( versionPackage , " data.nullables " ) , " INullableBasicType " ) )
. addException ( IOException . class )
. addParameter ( ParameterSpec . builder ( ClassName . get ( joinPackage ( basePackageName , " " ) , " BasicType " ) , " type " ) . build ( ) )
. addParameter ( ParameterSpec . builder ( Object . class , " content " ) . build ( ) )
. beginControlFlow ( " switch (type) " ) ;
for ( String item : typeTypes . keySet ( ) ) {
if ( typeFamily . get ( item ) = = Family . NULLABLE_BASIC ) {
String type = item . substring ( 1 ) ;
if ( ! specialNativeTypes . contains ( type ) ) {
createNullableOfBasicTypeMethod . addStatement ( " case " + type + " : return $T.ofNullable(($T) content) " ,
typeTypes . get ( " - " + type ) ,
typeTypes . get ( type )
) ;
}
}
}
createNullableOfBasicTypeMethod
. beginControlFlow ( " default: " )
. addStatement ( " throw new $T( \" Unknown nullable type: \" + type) " , IOException . class )
. endControlFlow ( )
. endControlFlow ( ) ;
versionClass . addMethod ( createNullableOfBasicTypeMethod . build ( ) ) ;
}
// Add createNullableOfGenericType method
{
var createNullableOfGenericTypeMethod = MethodSpec
. methodBuilder ( " createNullableOfGenericType " )
. addModifiers ( Modifier . PUBLIC )
. addModifiers ( Modifier . FINAL )
. returns ( ClassName . get ( joinPackage ( versionPackage , " data.nullables " ) , " INullableGenericType " ) )
. addException ( IOException . class )
. addParameter ( ParameterSpec . builder ( ClassName . get ( joinPackage ( basePackageName , " " ) , " GenericType " ) , " type " ) . build ( ) )
. addParameter ( ParameterSpec . builder ( Object . class , " content " ) . build ( ) )
. beginControlFlow ( " switch (type) " ) ;
for ( String item : typeTypes . keySet ( ) ) {
if ( typeFamily . get ( item ) = = Family . NULLABLE_GENERIC ) {
String type = item . substring ( 1 ) ;
if ( ! specialNativeTypes . contains ( type ) ) {
createNullableOfGenericTypeMethod . addStatement ( " case " + type + " : return $T.ofNullable(($T) content) " ,
typeTypes . get ( " - " + type ) ,
typeTypes . get ( type )
) ;
}
}
}
createNullableOfGenericTypeMethod
. beginControlFlow ( " default: " )
. addStatement ( " throw new $T( \" Unknown nullable type: \" + type) " , IOException . class )
. endControlFlow ( )
. endControlFlow ( ) ;
versionClass . addMethod ( createNullableOfGenericTypeMethod . build ( ) ) ;
}
2021-03-01 21:04:06 +01:00
// Add createArrayOf method
{
var createArrayOfMethod = MethodSpec
. methodBuilder ( " createArrayOf " )
. addModifiers ( Modifier . PUBLIC )
. addModifiers ( Modifier . FINAL )
. returns ( Object . class )
. addException ( IOException . class )
. addParameter ( ParameterSpec . builder ( String . class , " type " ) . build ( ) )
. addParameter ( ParameterSpec . builder ( TypeName . INT , " length " ) . build ( ) )
. beginControlFlow ( " switch (type) " ) ;
for ( String item : typeTypes . keySet ( ) ) {
if ( item . startsWith ( " § " ) ) {
String type = item . substring ( 1 ) ;
if ( ! specialNativeTypes . contains ( type ) ) {
createArrayOfMethod . addStatement ( " case \" " + type + " \" : return new $T[length] " , typeTypes . get ( type ) ) ;
}
}
}
createArrayOfMethod
. beginControlFlow ( " default: " )
. addStatement ( " throw new $T( \" Unknown nullable type: \" + type) " , IOException . class )
. endControlFlow ( )
. endControlFlow ( ) ;
versionClass . addMethod ( createArrayOfMethod . build ( ) ) ;
}
// Add getVersion method
{
var getVersionMethod = MethodSpec
. methodBuilder ( " getVersion " )
. addModifiers ( Modifier . PUBLIC )
. addAnnotation ( Override . class )
. returns ( TypeName . INT )
. addStatement ( " return VERSION " )
. build ( ) ;
versionClass . addMethod ( getVersionMethod ) ;
}
// Save the resulting class in the main package
try {
writeClass ( outPath , joinPackage ( versionPackage , " " ) , versionClass ) ;
} catch ( IOException e ) {
throw new IOError ( e ) ;
}
}
// Create the interface IType
{
var iTypeInterfaceType = ClassName . get ( joinPackage ( versionPackage , " data " ) , " IType " ) ;
var iTypeInterface = TypeSpec . interfaceBuilder ( " IType " ) ;
iTypeInterface . addModifiers ( Modifier . PUBLIC ) ;
iTypeInterface . addSuperinterface ( ClassName . get ( Serializable . class ) ) ;
try {
writeClass ( outPath , joinPackage ( versionPackage , " data " ) , iTypeInterface ) ;
} catch ( IOException e ) {
throw new IOError ( e ) ;
}
}
// Create the interface IBasicType
{
var iTypeInterfaceType = ClassName . get ( joinPackage ( versionPackage , " data " ) , " IType " ) ;
var ibasicTypeInterfaceType = ClassName . get ( joinPackage ( versionPackage , " data " ) , " IBasicType " ) ;
var ibasicTypeInterface = TypeSpec . interfaceBuilder ( " IBasicType " ) ;
ibasicTypeInterface . addModifiers ( Modifier . PUBLIC ) ;
ibasicTypeInterface . addSuperinterface ( iTypeInterfaceType ) ;
// Create getBasicType$type method
{
var getBasicTypeMethod = MethodSpec
. methodBuilder ( " getBasicType$ " )
. addModifiers ( Modifier . PUBLIC )
. addModifiers ( Modifier . ABSTRACT )
. returns ( ClassName . get ( joinPackage ( basePackageName , " " ) , " BasicType " ) ) ;
ibasicTypeInterface . addMethod ( getBasicTypeMethod . build ( ) ) ;
}
try {
writeClass ( outPath , joinPackage ( versionPackage , " data " ) , ibasicTypeInterface ) ;
} catch ( IOException e ) {
throw new IOError ( e ) ;
}
}
2021-04-27 00:08:19 +02:00
// Create the interface INullableIType
2021-03-01 21:04:06 +01:00
{
var iTypeInterfaceType = ClassName . get ( joinPackage ( versionPackage , " data " ) , " IType " ) ;
2021-04-27 00:08:19 +02:00
var inullableITypeInterface = TypeSpec . interfaceBuilder ( " INullableIType " ) ;
inullableITypeInterface . addModifiers ( Modifier . PUBLIC ) ;
inullableITypeInterface . addSuperinterface ( iTypeInterfaceType ) ;
inullableITypeInterface . addSuperinterface ( IGenericNullable . class ) ;
try {
writeClass ( outPath , joinPackage ( versionPackage , " data.nullables " ) , inullableITypeInterface ) ;
} catch ( IOException e ) {
throw new IOError ( e ) ;
}
}
// Create the interface INullableBasicType
{
var iTypeInterfaceType = ClassName . get ( joinPackage ( versionPackage , " data.nullables " ) , " INullableIType " ) ;
2021-03-01 21:04:06 +01:00
var inullableBasicTypeInterface = TypeSpec . interfaceBuilder ( " INullableBasicType " ) ;
inullableBasicTypeInterface . addModifiers ( Modifier . PUBLIC ) ;
inullableBasicTypeInterface . addSuperinterface ( iTypeInterfaceType ) ;
2021-04-27 00:08:19 +02:00
var getBasicTypeMethod = MethodSpec
. methodBuilder ( " getBasicType$ " )
. addModifiers ( Modifier . PUBLIC )
. addModifiers ( Modifier . ABSTRACT )
. returns ( ClassName . get ( joinPackage ( basePackageName , " " ) , " BasicType " ) ) ;
inullableBasicTypeInterface . addMethod ( getBasicTypeMethod . build ( ) ) ;
2021-03-01 21:04:06 +01:00
try {
writeClass ( outPath , joinPackage ( versionPackage , " data.nullables " ) , inullableBasicTypeInterface ) ;
} catch ( IOException e ) {
throw new IOError ( e ) ;
}
}
2021-04-27 00:08:19 +02:00
// Create the interface INullableGenericType
{
var iTypeInterfaceType = ClassName . get ( joinPackage ( versionPackage , " data.nullables " ) , " INullableIType " ) ;
var inullablegenericTypeInterface = TypeSpec . interfaceBuilder ( " INullableGenericType " ) ;
inullablegenericTypeInterface . addModifiers ( Modifier . PUBLIC ) ;
inullablegenericTypeInterface . addSuperinterface ( iTypeInterfaceType ) ;
var getBasicTypeMethod = MethodSpec
. methodBuilder ( " getGenericType$ " )
. addModifiers ( Modifier . PUBLIC )
. addModifiers ( Modifier . ABSTRACT )
. returns ( ClassName . get ( joinPackage ( basePackageName , " " ) , " GenericType " ) ) ;
inullablegenericTypeInterface . addMethod ( getBasicTypeMethod . build ( ) ) ;
try {
writeClass ( outPath , joinPackage ( versionPackage , " data.nullables " ) , inullablegenericTypeInterface ) ;
} catch ( IOException e ) {
throw new IOError ( e ) ;
}
}
2021-03-01 21:04:06 +01:00
// Create the interfaces
{
for ( Entry < String , Set < String > > superType : versionConfiguration . superTypes . entrySet ( ) ) {
String type = superType . getKey ( ) ;
Set < String > superTypeConfiguration = superType . getValue ( ) ;
2021-04-27 00:08:19 +02:00
var iBasicTypeInterfaceType = ClassName . get ( joinPackage ( versionPackage , " data " ) , " IBasicType " ) ;
2021-03-01 21:04:06 +01:00
var typeInterfaceType = ClassName . get ( joinPackage ( versionPackage , " data " ) , type ) ;
var typeInterface = TypeSpec . interfaceBuilder ( type ) ;
typeInterface . addModifiers ( Modifier . PUBLIC ) ;
2021-04-27 00:08:19 +02:00
typeInterface . addSuperinterface ( iBasicTypeInterfaceType ) ;
2021-03-01 21:04:06 +01:00
var getMetaTypeMethod = MethodSpec
. methodBuilder ( " getMetaId$ " + type )
. addModifiers ( Modifier . PUBLIC )
. addModifiers ( Modifier . ABSTRACT )
. returns ( TypeName . INT ) ;
typeInterface . addMethod ( getMetaTypeMethod . build ( ) ) ;
var getBasicTypeMethod = MethodSpec
. methodBuilder ( " getBasicType$ " )
. addModifiers ( Modifier . PUBLIC )
. addModifiers ( Modifier . ABSTRACT )
. returns ( ClassName . get ( joinPackage ( basePackageName , " " ) , " BasicType " ) ) ;
typeInterface . addMethod ( getBasicTypeMethod . build ( ) ) ;
// If it's the latest version, add the common methods
if ( nextVersion . isEmpty ( ) ) {
var interfaceDataConfiguration = configuration . interfacesData . get ( type ) ;
if ( interfaceDataConfiguration ! = null ) {
// Extend this interface
for ( String extendedInterface : interfaceDataConfiguration . extendInterfaces ) {
typeInterface . addSuperinterface ( ClassName . get ( joinPackage ( versionPackage , " data " ) , extendedInterface ) ) ;
}
Map < String , CommonField > commonFields = new HashMap < > ( ) ;
for ( Entry < String , String > e : interfaceDataConfiguration . commonData . entrySet ( ) ) {
String key = e . getKey ( ) ;
String value = e . getValue ( ) ;
commonFields . put ( key , new CommonField ( key , value , true ) ) ;
}
for ( Entry < String , String > entry : interfaceDataConfiguration . commonGetters . entrySet ( ) ) {
String field = entry . getKey ( ) ;
String fieldType = entry . getValue ( ) ;
commonFields . put ( field , new CommonField ( field , fieldType , false ) ) ;
}
for ( CommonField fieldInfo : commonFields . values ( ) ) {
var fieldTypeType = fieldInfo . fieldType . equals ( " ? " ) ? ClassName . get ( " java.lang " , " Object " )
: typeTypes . get ( fieldInfo . fieldType ) ;
// Add common data getter
{
2021-05-21 00:20:02 +02:00
var getterMethod = MethodSpec
. methodBuilder ( fieldInfo . fieldName )
. addModifiers ( Modifier . PUBLIC )
. addModifiers ( Modifier . ABSTRACT )
2021-03-01 21:04:06 +01:00
. returns ( fieldTypeType ) ;
if ( ! fieldTypeType . isPrimitive ( ) ) {
getterMethod . addAnnotation ( NotNull . class ) ;
}
typeInterface . addMethod ( getterMethod . build ( ) ) ;
}
// Add common data setter
if ( fieldInfo . hasSetter ) {
if ( ! fieldInfo . fieldType . equals ( " ? " ) ) {
var param = ParameterSpec . builder ( fieldTypeType , fieldInfo . fieldName , Modifier . FINAL ) ;
if ( ! fieldTypeType . isPrimitive ( ) ) {
param . addAnnotation ( NotNull . class ) ;
}
var setterMethod = MethodSpec
. methodBuilder ( " set " + capitalize ( fieldInfo . fieldName ) )
. addModifiers ( Modifier . PUBLIC )
. addModifiers ( Modifier . ABSTRACT )
. addParameter ( param . build ( ) )
. returns ( typeInterfaceType )
2021-05-21 00:20:02 +02:00
. addAnnotation ( NotNull . class ) ;
2021-03-01 21:04:06 +01:00
typeInterface . addMethod ( setterMethod . build ( ) ) ;
}
}
}
}
}
try {
writeClass ( outPath , joinPackage ( versionPackage , " data " ) , typeInterface ) ;
} catch ( IOException e ) {
throw new IOError ( e ) ;
}
}
}
// Create the basic types classes
{
for ( Entry < String , ClassConfiguration > stringClassConfigurationEntry : versionConfiguration . classes . entrySet ( ) ) {
String type = stringClassConfigurationEntry . getKey ( ) ;
ClassConfiguration classConfiguration = stringClassConfigurationEntry . getValue ( ) ;
2021-05-21 00:20:02 +02:00
var typeClass = TypeSpec . recordBuilder ( type ) ;
2021-03-01 21:04:06 +01:00
typeClass . addModifiers ( Modifier . PUBLIC ) ;
2021-05-21 00:20:02 +02:00
typeClass . addModifiers ( Modifier . STATIC ) ;
2021-03-01 21:04:06 +01:00
typeClass . addModifiers ( Modifier . FINAL ) ;
typeClass . addSuperinterface ( ClassName . get ( joinPackage ( versionPackage , " data " ) , " IBasicType " ) ) ;
2021-05-21 00:20:02 +02:00
typeClass . addAnnotation ( RecordBuilder . class ) ;
2021-03-01 21:04:06 +01:00
var getBasicTypeMethod = MethodSpec
. methodBuilder ( " getBasicType$ " )
. addModifiers ( Modifier . PUBLIC )
. addModifiers ( Modifier . FINAL )
. addAnnotation ( Override . class )
. returns ( ClassName . get ( joinPackage ( basePackageName , " " ) , " BasicType " ) )
. addStatement ( " return $T. " + type , ClassName . get ( joinPackage ( basePackageName , " " ) , " BasicType " ) ) ;
typeClass . addMethod ( getBasicTypeMethod . build ( ) ) ;
var superTypes = versionConfiguration . superTypes
. entrySet ( )
. parallelStream ( )
. filter ( ( ( entry ) - > entry . getValue ( ) . contains ( type ) ) )
. map ( ( entry ) - > Map . entry ( entry . getKey ( ) , indexOf ( entry . getValue ( ) , type ) ) )
. collect ( Collectors . toUnmodifiableSet ( ) ) ;
for ( Entry < String , Integer > superType : superTypes ) {
typeClass . addSuperinterface ( ClassName . get ( joinPackage ( versionPackage , " data " ) , superType . getKey ( ) ) ) ;
var getMetaIdTypeField = FieldSpec
. builder ( TypeName . INT , " META_ID$ " + capitalizeAll ( superType . getKey ( ) ) )
. addModifiers ( Modifier . PUBLIC )
. addModifiers ( Modifier . STATIC )
. addModifiers ( Modifier . FINAL )
. initializer ( " " + superType . getValue ( ) ) ;
typeClass . addField ( getMetaIdTypeField . build ( ) ) ;
var getMetaTypeMethod = MethodSpec
. methodBuilder ( " getMetaId$ " + superType . getKey ( ) )
. addModifiers ( Modifier . PUBLIC )
. addModifiers ( Modifier . FINAL )
. addAnnotation ( Override . class )
. returns ( TypeName . INT )
. addStatement ( " return this. " + " META_ID$$ " + capitalizeAll ( superType . getKey ( ) ) ) ;
typeClass . addMethod ( getMetaTypeMethod . build ( ) ) ;
}
for ( Entry < String , String > stringStringEntry : classConfiguration . getData ( ) . entrySet ( ) ) {
String key = stringStringEntry . getKey ( ) ;
String value = stringStringEntry . getValue ( ) ;
var isGetterOverride = false ;
var isSetterOverride = false ;
if ( nextVersion . isEmpty ( ) ) {
for ( Entry < String , Integer > superType : superTypes ) {
if ( superType ! = null ) {
var interfaceCommonDataConfiguration = configuration . interfacesData . getOrDefault ( superType . getKey ( ) ,
null
) ;
if ( interfaceCommonDataConfiguration ! = null
& & interfaceCommonDataConfiguration . commonData . containsKey ( key )
& & ! interfaceCommonDataConfiguration . commonData . get ( key ) . equals ( " ? " ) ) {
isGetterOverride = true ;
isSetterOverride = true ;
}
if ( interfaceCommonDataConfiguration ! = null
& & interfaceCommonDataConfiguration . commonGetters . containsKey ( key )
& & ! interfaceCommonDataConfiguration . commonGetters . get ( key ) . equals ( " ? " ) ) {
isGetterOverride = true ;
}
}
}
}
2021-07-13 16:23:20 +02:00
addField ( typeClass , key , typeTypes . get ( value ) , true , true , false ) ;
2021-03-01 21:04:06 +01:00
addImmutableSetter ( typeClass ,
ClassName . get ( joinPackage ( versionPackage , " data " ) , type ) ,
classConfiguration . getData ( ) . keySet ( ) ,
key ,
typeTypes . get ( value ) ,
isSetterOverride
) ;
}
// Add string representer only if this object is at the current version, the old data don't need a string representation...
if ( nextVersion . isEmpty ( ) & & classConfiguration . getStringRepresenter ( ) ! = null ) {
typeClass . addMethod ( MethodSpec
. methodBuilder ( " toString " )
. addModifiers ( Modifier . PUBLIC )
. addAnnotation ( Override . class )
. returns ( String . class )
. addStatement ( " return " + classConfiguration . getStringRepresenter ( ) + " (this) " )
. build ( ) ) ;
}
2021-05-21 00:20:02 +02:00
var ofConstructor = MethodSpec
. methodBuilder ( " of " )
. returns ( typeTypes . get ( type ) )
. addModifiers ( Modifier . STATIC )
. addModifiers ( Modifier . PUBLIC )
. addModifiers ( Modifier . FINAL ) ;
var mapConstructor = MethodSpec
. methodBuilder ( " parse " )
. returns ( typeTypes . get ( type ) )
. addModifiers ( Modifier . STATIC )
. addModifiers ( Modifier . PUBLIC )
. addModifiers ( Modifier . FINAL ) ;
2021-03-01 21:04:06 +01:00
mapConstructor . addException ( IllegalStateException . class ) ;
mapConstructor . addParameter ( ParameterizedTypeName . get ( Map . class , String . class , Object . class ) , " fields " ) ;
mapConstructor . addStatement (
" if (fields.size() != " + classConfiguration . getData ( ) . size ( ) + " ) throw new $T() " ,
IllegalStateException . class
) ;
2021-05-21 00:20:02 +02:00
var returnMapNewInstanceStamentBuilder = CodeBlock . builder ( ) . add ( " return new $T( " , typeTypes . get ( type ) ) ;
var returnOfNewInstanceStamentBuilder = CodeBlock . builder ( ) . add ( " return new $T( " , typeTypes . get ( type ) ) ;
boolean first = true ;
2021-03-01 21:04:06 +01:00
for ( Entry < String , String > entry : classConfiguration . getData ( ) . entrySet ( ) ) {
2021-05-21 00:20:02 +02:00
if ( ! first ) {
returnMapNewInstanceStamentBuilder . add ( " , " ) ;
returnOfNewInstanceStamentBuilder . add ( " , " ) ;
} else {
first = false ;
}
2021-03-01 21:04:06 +01:00
String field = entry . getKey ( ) ;
String fieldType = entry . getValue ( ) ;
var fieldTypeType = typeTypes . get ( fieldType ) ;
mapConstructor . addStatement ( " if (!fields.containsKey( \" " + field + " \" )) throw new $T() " ,
IllegalStateException . class
) ;
boolean requiresNotNull = ! fieldTypeType . isPrimitive ( ) ;
if ( requiresNotNull ) {
2021-05-21 00:20:02 +02:00
returnMapNewInstanceStamentBuilder . add ( " $T.requireNonNull( " , Objects . class ) ;
returnOfNewInstanceStamentBuilder . add ( " $T.requireNonNull( " , Objects . class ) ;
2021-03-01 21:04:06 +01:00
}
2021-05-21 00:20:02 +02:00
returnMapNewInstanceStamentBuilder . add ( " ($T) fields.get( \" " + field + " \" ) " , typeTypes . get ( fieldType ) ) ;
returnOfNewInstanceStamentBuilder . add ( " ($T) " + field + " " , typeTypes . get ( fieldType ) ) ;
2022-02-28 14:38:57 +01:00
var fieldTypeName = typeTypes . get ( fieldType ) ;
var parameterSpecBuilder = ParameterSpec . builder ( fieldTypeName , " " + field ) ;
if ( ! fieldTypeName . isPrimitive ( ) ) {
parameterSpecBuilder . addAnnotation ( NotNull . class ) ;
}
ofConstructor . addParameter ( parameterSpecBuilder . build ( ) ) ;
2021-03-01 21:04:06 +01:00
if ( requiresNotNull ) {
2021-05-21 00:20:02 +02:00
returnMapNewInstanceStamentBuilder . add ( " ) " ) ;
returnOfNewInstanceStamentBuilder . add ( " ) " ) ;
2021-03-01 21:04:06 +01:00
}
}
2022-02-20 18:20:05 +01:00
if ( first ) {
typeClass . addField ( FieldSpec
. builder ( typeTypes . get ( type ) , " INSTANCE " , Modifier . PRIVATE , Modifier . STATIC , Modifier . FINAL )
. initializer ( " new $T() " , typeTypes . get ( type ) )
. build ( ) ) ;
mapConstructor . addStatement ( " return INSTANCE " ) ;
ofConstructor . addStatement ( " return INSTANCE " ) ;
} else {
mapConstructor . addStatement ( returnMapNewInstanceStamentBuilder . add ( " ) " ) . build ( ) ) ;
ofConstructor . addStatement ( returnOfNewInstanceStamentBuilder . add ( " ) " ) . build ( ) ) ;
}
2021-03-01 21:04:06 +01:00
typeClass . addMethod ( mapConstructor . build ( ) ) ;
2021-05-21 00:20:02 +02:00
typeClass . addMethod ( ofConstructor . build ( ) ) ;
2021-03-01 21:04:06 +01:00
try {
writeClass ( outPath , joinPackage ( versionPackage , " data " ) , typeClass ) ;
} catch ( IOException e ) {
throw new IOError ( e ) ;
}
}
}
// Create an upgrader
}
}
2021-05-25 11:17:44 +02:00
private TypeName getImmutableArrayType ( HashMap < String , TypeName > typeTypes , String typeString ) {
var type = typeTypes . get ( typeString ) ;
return getImmutableArrayType ( type ) ;
}
private TypeName getImmutableArrayType ( TypeName type ) {
return switch ( type . toString ( ) ) {
case " boolean " - > ClassName . get ( BooleanList . class ) ;
case " byte " - > ClassName . get ( ByteList . class ) ;
case " short " - > ClassName . get ( ShortList . class ) ;
case " char " - > ClassName . get ( CharList . class ) ;
case " int " - > ClassName . get ( IntList . class ) ;
case " long " - > ClassName . get ( LongList . class ) ;
case " float " - > ClassName . get ( FloatList . class ) ;
case " double " - > ClassName . get ( DoubleList . class ) ;
default - > ParameterizedTypeName . get ( ClassName . get ( List . class ) , type ) ;
} ;
}
private TypeName getArrayComponentType ( TypeName listType ) {
if ( listType instanceof ParameterizedTypeName ) {
return ( ( ParameterizedTypeName ) listType ) . typeArguments . get ( 0 ) ;
} else {
return switch ( listType . toString ( ) ) {
case " BooleanList " - > ClassName . BOOLEAN ;
case " ByteList " - > ClassName . BYTE ;
case " ShortList " - > ClassName . SHORT ;
case " CharList " - > ClassName . CHAR ;
case " IntList " - > ClassName . INT ;
case " LongList " - > ClassName . LONG ;
case " FloatList " - > ClassName . FLOAT ;
case " DoubleList " - > ClassName . DOUBLE ;
default - > throw new IllegalStateException ( " Unexpected value: " + listType ) ;
} ;
}
}
2021-04-27 00:08:19 +02:00
private CodeBlock buildStatementUpgradeBasicType (
String versionPackage ,
String nextVersionPackage ,
int number ,
String key ,
String toTypeName ,
Family toFamily ,
TypeName toType ,
TypeName toTypeBoxed ) {
var deserializeMethod = CodeBlock . builder ( ) ;
String inputFieldName = " $field$ " + number + " $ " + key ;
String resultFieldName = " $field$ " + ( number + 1 ) + " $ " + key ;
deserializeMethod . addStatement ( " $T $N " , toType , resultFieldName ) ;
deserializeMethod . beginControlFlow ( " " ) ;
deserializeMethod . addStatement ( " var value = $N " , inputFieldName ) ;
var oldIBasicType = ClassName . get ( joinPackage ( versionPackage , " data " ) , " IBasicType " ) ;
var upgradeBasicTypeField = MethodSpec . methodBuilder ( " upgradeBasicTypeField " ) ;
upgradeBasicTypeField . addModifiers ( Modifier . PRIVATE ) ;
upgradeBasicTypeField . addModifiers ( Modifier . STATIC ) ;
upgradeBasicTypeField . addModifiers ( Modifier . FINAL ) ;
upgradeBasicTypeField . returns ( Object . class ) ;
upgradeBasicTypeField . addParameter ( ParameterSpec
. builder ( oldIBasicType , " value " )
. addAnnotation ( NotNull . class )
. build ( ) ) ;
upgradeBasicTypeField . addException ( IOException . class ) ;
var oldVersionType = ClassName . get ( joinPackage ( versionPackage , " " ) , " Version " ) ;
var oldIType = ClassName . get ( joinPackage ( versionPackage , " data " ) , " IType " ) ;
var oldINullableBasicType = ClassName . get ( joinPackage ( versionPackage , " data.nullables " ) ,
" INullableBasicType "
) ;
upgradeBasicTypeField . addStatement ( " $T.requireNonNull(value) " , Objects . class ) ;
deserializeMethod . addStatement ( " $N = ($T) $T.upgradeToNextVersion(($T) value) " ,
resultFieldName ,
toTypeBoxed ,
oldVersionType ,
oldIBasicType
) ;
deserializeMethod . endControlFlow ( ) ;
return deserializeMethod . build ( ) ;
}
private CodeBlock buildStatementUpgradeNullableOtherField (
String versionPackage ,
String nextVersionPackage ,
int number ,
String key ,
String toTypeName ,
Family toFamily ,
TypeName toType ,
TypeName toTypeBoxed ) {
var deserializeMethod = CodeBlock . builder ( ) ;
String inputFieldName = " $field$ " + number + " $ " + key ;
String resultFieldName = " $field$ " + ( number + 1 ) + " $ " + key ;
deserializeMethod . addStatement ( " $T $N " , toType , resultFieldName ) ;
deserializeMethod . beginControlFlow ( " " ) ;
deserializeMethod . addStatement ( " var value = $N " , inputFieldName ) ;
var oldINullableGenericType = ClassName . get ( joinPackage ( versionPackage , " data.nullables " ) ,
" INullableGenericType "
) ;
var oldVersionType = ClassName . get ( joinPackage ( versionPackage , " " ) , " Version " ) ;
var oldIBasicType = ClassName . get ( joinPackage ( versionPackage , " data " ) , " IBasicType " ) ;
var oldIType = ClassName . get ( joinPackage ( versionPackage , " data " ) , " IType " ) ;
deserializeMethod . addStatement ( " $T.requireNonNull(value) " , Objects . class ) ;
deserializeMethod . addStatement ( " var content = value.$$getNullable() " ) ;
deserializeMethod . addStatement ( " $N = $T.ofNullable(content) " , resultFieldName , toTypeBoxed ) ;
deserializeMethod . endControlFlow ( ) ;
return deserializeMethod . build ( ) ;
}
private CodeBlock buildStatementUpgradeNullableGenericField (
String versionPackage ,
String nextVersionPackage ,
int number ,
String key ,
String toTypeName ,
Family toFamily ,
TypeName toType ,
TypeName toTypeBoxed ,
TypeName toGenericType ) {
var deserializeMethod = CodeBlock . builder ( ) ;
String inputFieldName = " $field$ " + number + " $ " + key ;
String resultFieldName = " $field$ " + ( number + 1 ) + " $ " + key ;
deserializeMethod . addStatement ( " $T $N " , toType , resultFieldName ) ;
deserializeMethod . beginControlFlow ( " " ) ;
deserializeMethod . addStatement ( " var value = $N " , inputFieldName ) ;
var oldINullableGenericType = ClassName . get ( joinPackage ( versionPackage , " data.nullables " ) ,
" INullableGenericType "
) ;
var oldVersionType = ClassName . get ( joinPackage ( versionPackage , " " ) , " Version " ) ;
var oldIBasicType = ClassName . get ( joinPackage ( versionPackage , " data " ) , " IBasicType " ) ;
var oldIType = ClassName . get ( joinPackage ( versionPackage , " data " ) , " IType " ) ;
deserializeMethod . addStatement ( " $T.requireNonNull(value) " , Objects . class ) ;
deserializeMethod . addStatement ( " var content = value.$$getNullable() " ) ;
2021-04-27 00:59:11 +02:00
deserializeMethod . addStatement ( " var newContent = content == null ? null : $T.requireNonNull(($T) $T.upgradeToNextVersion(($T) content)) " ,
ClassName . get ( Objects . class ) ,
2021-04-27 00:08:19 +02:00
toGenericType ,
oldVersionType ,
oldIBasicType
) ;
deserializeMethod . addStatement ( " $N = $T.ofNullable(newContent) " , resultFieldName , toTypeBoxed ) ;
deserializeMethod . endControlFlow ( ) ;
return deserializeMethod . build ( ) ;
}
private CodeBlock buildStatementUpgradeNullableBasicField (
String versionPackage ,
String nextVersionPackage ,
int number ,
String key ,
String toTypeName ,
Family toFamily ,
TypeName toType ,
TypeName toTypeBoxed ,
TypeName toBasicType ) {
var deserializeMethod = CodeBlock . builder ( ) ;
String inputFieldName = " $field$ " + number + " $ " + key ;
String resultFieldName = " $field$ " + ( number + 1 ) + " $ " + key ;
deserializeMethod . addStatement ( " $T $N " , toType , resultFieldName ) ;
deserializeMethod . beginControlFlow ( " " ) ;
deserializeMethod . addStatement ( " var value = $N " , inputFieldName ) ;
var oldINullableBasicType = ClassName . get ( joinPackage ( versionPackage , " data.nullables " ) ,
" INullableBasicType "
) ;
var oldVersionType = ClassName . get ( joinPackage ( versionPackage , " " ) , " Version " ) ;
var oldIBasicType = ClassName . get ( joinPackage ( versionPackage , " data " ) , " IBasicType " ) ;
var oldIType = ClassName . get ( joinPackage ( versionPackage , " data " ) , " IType " ) ;
deserializeMethod . addStatement ( " $T.requireNonNull(value) " , Objects . class ) ;
deserializeMethod . addStatement ( " var content = value.$$getNullable() " ) ;
2021-04-27 00:59:11 +02:00
deserializeMethod . addStatement ( " var newContent = content == null ? null : $T.requireNonNull(($T) $T.upgradeToNextVersion(($T) content)) " ,
ClassName . get ( Objects . class ) ,
2021-04-27 00:08:19 +02:00
toBasicType ,
oldVersionType ,
oldIBasicType
) ;
deserializeMethod . addStatement ( " $N = $T.ofNullable(newContent) " , resultFieldName , toTypeBoxed ) ;
deserializeMethod . endControlFlow ( ) ;
return deserializeMethod . build ( ) ;
}
private CodeBlock buildStatementUpgradeITypeArrayField (
String versionPackage ,
@Nullable String nextVersionPackage ,
int number ,
String key ,
String toTypeName ,
Family toFamily ,
TypeName toType ,
TypeName toTypeBoxed ) {
var deserializeMethod = CodeBlock . builder ( ) ;
String inputFieldName = " $field$ " + number + " $ " + key ;
String resultFieldName = " $field$ " + ( number + 1 ) + " $ " + key ;
deserializeMethod . addStatement ( " $T $N " , toType , resultFieldName ) ;
deserializeMethod . beginControlFlow ( " " ) ;
deserializeMethod . addStatement ( " var value = $N " , inputFieldName ) ;
var oldVersionType = ClassName . get ( joinPackage ( versionPackage , " " ) , " Version " ) ;
var oldIBasicType = ClassName . get ( joinPackage ( versionPackage , " data " ) , " IBasicType " ) ;
var oldINullableBasicType = ClassName . get ( joinPackage ( versionPackage , " data.nullables " ) ,
" INullableBasicType "
) ;
deserializeMethod . addStatement ( " $T.requireNonNull(value) " , Objects . class ) ;
2021-05-25 11:17:44 +02:00
deserializeMethod . addStatement ( " var newArray = new $T[value.size()] " , getArrayComponentType ( toType ) ) ;
deserializeMethod . beginControlFlow ( " for (int i = 0; i < value.size(); i++) " ) ;
deserializeMethod . addStatement ( " var item = value.get(i) " , Array . class ) ;
2021-04-27 00:08:19 +02:00
deserializeMethod . addStatement ( " var updatedItem = ($T) $T.upgradeToNextVersion(($T) item) " ,
2021-05-25 11:17:44 +02:00
getArrayComponentType ( toType ) ,
2021-04-27 00:08:19 +02:00
oldVersionType ,
oldIBasicType
) ;
deserializeMethod . addStatement ( " newArray[i] = updatedItem " ) ;
deserializeMethod . endControlFlow ( ) ;
2021-05-25 11:17:44 +02:00
deserializeMethod . addStatement ( " $N = $T.of(newArray) " ,
resultFieldName ,
toType instanceof ParameterizedTypeName ? ( ( ParameterizedTypeName ) toType ) . rawType : toType
) ;
2021-04-27 00:08:19 +02:00
deserializeMethod . endControlFlow ( ) ;
return deserializeMethod . build ( ) ;
}
2021-03-10 03:37:09 +01:00
private static String getSpecialNativePackage ( String specialNativeType ) {
//noinspection SwitchStatementWithTooFewBranches
2021-05-21 00:20:02 +02:00
return switch ( specialNativeType ) {
case " Int52 " - > " it.cavallium.data.generator.nativedata " ;
default - > " java.lang " ;
} ;
2021-03-10 03:37:09 +01:00
}
2021-03-01 21:04:06 +01:00
private void registerArrayType ( String versionPackage ,
ClassName versionClassType ,
HashMap < String , TypeName > typeOptionalSerializers ,
HashMap < String , SerializeCodeBlockGenerator > typeSerializeStatement ,
HashMap < String , CodeBlock > typeDeserializeStatement ,
HashMap < String , Boolean > typeMustGenerateSerializer ,
String type ) {
typeOptionalSerializers . put ( " § " + type ,
ClassName . get ( joinPackage ( versionPackage , " serializers " ) , " Array " + type + " Serializer " ) ) ;
typeSerializeStatement . put ( " § " + type , new SerializeCodeBlockGenerator (
CodeBlock . builder ( ) . add ( " $T.Array " + type + " SerializerInstance.serialize(dataOutput, " , versionClassType )
. build ( ) , CodeBlock . builder ( ) . add ( " ) " ) . build ( ) ) ) ;
typeDeserializeStatement . put ( " § " + type ,
CodeBlock . builder ( ) . add ( " $T.Array " + type + " SerializerInstance.deserialize(dataInput) " , versionClassType )
. build ( ) ) ;
typeMustGenerateSerializer . put ( " § " + type , true ) ;
}
private MethodSpec . Builder createEmptySerializeMethod ( TypeName classType ) {
var serializeMethod = MethodSpec . methodBuilder ( " serialize " ) ;
serializeMethod . addAnnotation ( Override . class ) ;
serializeMethod . addModifiers ( Modifier . PUBLIC ) ;
serializeMethod . addModifiers ( Modifier . FINAL ) ;
serializeMethod . returns ( TypeName . VOID ) ;
serializeMethod . addParameter ( ParameterSpec . builder ( DataOutput . class , " dataOutput " ) . build ( ) ) ;
serializeMethod
2021-05-21 00:20:02 +02:00
. addParameter ( ParameterSpec . builder ( classType , " data " ) . addAnnotation ( NotNull . class ) . build ( ) ) ;
2021-03-01 21:04:06 +01:00
serializeMethod . addException ( IOException . class ) ;
serializeMethod . addStatement ( " $T.requireNonNull(data) " , Objects . class ) ;
return serializeMethod ;
}
private MethodSpec . Builder createEmptyDeserializeMethod ( TypeName classType ) {
var deserializeMethod = MethodSpec . methodBuilder ( " deserialize " ) ;
deserializeMethod . addAnnotation ( Override . class ) ;
deserializeMethod . addAnnotation ( NotNull . class ) ;
deserializeMethod . addModifiers ( Modifier . PUBLIC ) ;
deserializeMethod . addModifiers ( Modifier . FINAL ) ;
deserializeMethod . returns ( classType ) ;
deserializeMethod . addParameter ( ParameterSpec . builder ( DataInput . class , " dataInput " ) . build ( ) ) ;
deserializeMethod . addException ( IOException . class ) ;
return deserializeMethod ;
}
2021-05-21 00:20:02 +02:00
public static record NeededTypes (
boolean nullableTypeNeeded ,
boolean nextVersionNullableTypeNeeded ,
boolean arrayTypeNeeded ,
boolean nextVersionArrayTypeNeeded ) { }
2021-03-01 21:04:06 +01:00
@SuppressWarnings ( " OptionalUsedAsFieldOrParameterType " )
public NeededTypes registerNeededTypes ( VersionConfiguration versionConfiguration ,
2021-04-27 00:08:19 +02:00
Family family ,
2021-03-01 21:04:06 +01:00
String type ,
Optional < String > nextVersion ,
Optional < String > nextVersionPackage ,
ClassName versionClassType ,
String versionPackage ,
HashMap < String , TypeName > typeOptionalSerializers ,
HashMap < String , SerializeCodeBlockGenerator > typeSerializeStatement ,
HashMap < String , CodeBlock > typeDeserializeStatement ,
HashMap < String , Boolean > typeMustGenerateSerializer ,
2021-04-27 00:08:19 +02:00
HashMap < String , TypeName > typeTypes ,
HashMap < String , Family > typeFamily ,
@Nullable HashMap < String , TypeName > nextVersionTypeTypes ,
@Nullable HashMap < String , Family > nextVersionTypeFamily ,
2022-02-28 13:29:58 +01:00
Supplier < TypeName > arrayClassName ,
Supplier < TypeName > nextArrayClassName ) {
2021-03-01 21:04:06 +01:00
// Check if the nullable type is needed
boolean nullableTypeNeeded = versionConfiguration . classes
. values ( )
. parallelStream ( )
. map ( ClassConfiguration : : getData )
. map ( Map : : values )
. flatMap ( Collection : : parallelStream )
. filter ( ( typeZ ) - > typeZ . startsWith ( " - " ) )
. map ( ( typeZ ) - > typeZ . substring ( 1 ) )
. anyMatch ( ( typeZ ) - > typeZ . equals ( type ) ) ;
boolean nextVersionNullableTypeNeeded = nextVersion
. filter ( s - > configuration . versions . get ( s ) . classes
. values ( )
. parallelStream ( )
. map ( ClassConfiguration : : getData )
. map ( Map : : values )
. flatMap ( Collection : : parallelStream )
. filter ( ( typeZ ) - > typeZ . startsWith ( " - " ) )
. map ( ( typeZ ) - > typeZ . substring ( 1 ) )
. anyMatch ( ( typeZ ) - > typeZ . equals ( type ) ) )
. isPresent ( ) ;
2021-05-21 00:20:02 +02:00
Family nullableFamily = switch ( family ) {
case BASIC - > Family . NULLABLE_BASIC ;
case GENERIC - > Family . NULLABLE_GENERIC ;
case OTHER - > Family . NULLABLE_OTHER ;
default - > throw new IllegalStateException ( " Unexpected value: " + family ) ;
} ;
2021-03-01 21:04:06 +01:00
if ( nullableTypeNeeded ) {
typeOptionalSerializers . put ( " - " + type ,
ClassName . get ( joinPackage ( versionPackage , " serializers " ) , " Nullable " + type + " Serializer " ) ) ;
typeSerializeStatement . put ( " - " + type , new SerializeCodeBlockGenerator ( CodeBlock . builder ( )
. add ( " $T.Nullable " + type + " SerializerInstance.serialize(dataOutput, " , versionClassType ) . build ( ) ,
CodeBlock . builder ( ) . add ( " ) " ) . build ( ) ) ) ;
typeDeserializeStatement . put ( " - " + type , CodeBlock . builder ( )
. add ( " $T.Nullable " + type + " SerializerInstance.deserialize(dataInput) " , versionClassType ) . build ( ) ) ;
typeMustGenerateSerializer . put ( " - " + type , true ) ;
typeTypes . put ( " - " + type , ClassName . get ( joinPackage ( versionPackage , " data.nullables " ) , " Nullable " + type ) ) ;
2021-06-07 16:23:01 +02:00
}
if ( typeFamily ! = null ) {
2021-04-27 00:08:19 +02:00
typeFamily . put ( " - " + type , nullableFamily ) ;
2021-03-01 21:04:06 +01:00
}
2021-06-07 16:23:01 +02:00
2021-03-01 21:04:06 +01:00
if ( nextVersionNullableTypeNeeded ) {
assert nextVersionTypeTypes ! = null ;
2021-04-27 00:08:19 +02:00
assert nextVersionTypeFamily ! = null ;
2021-03-01 21:04:06 +01:00
nextVersionTypeTypes . put ( " - " + type , ClassName . get ( joinPackage ( nextVersionPackage . orElseThrow ( ) , " data.nullables " ) , " Nullable " + type ) ) ;
2021-06-07 16:23:01 +02:00
}
if ( nextVersionTypeFamily ! = null ) {
2021-04-27 00:08:19 +02:00
nextVersionTypeFamily . put ( " - " + type , nullableFamily ) ;
2021-03-01 21:04:06 +01:00
}
// Check if the array type is needed
boolean arrayTypeNeeded = versionConfiguration . classes
. values ( )
. parallelStream ( )
. map ( ClassConfiguration : : getData )
. map ( Map : : values )
. flatMap ( Collection : : parallelStream )
. filter ( ( typeZ ) - > typeZ . startsWith ( " § " ) )
. map ( ( typeZ ) - > typeZ . substring ( 1 ) )
. anyMatch ( ( typeZ ) - > typeZ . equals ( type ) ) ;
boolean nextVersionArrayTypeNeeded = nextVersion . filter ( s - > configuration . versions . get ( s ) . classes
. values ( )
. parallelStream ( )
. map ( ClassConfiguration : : getData )
. map ( Map : : values )
. flatMap ( Collection : : parallelStream )
. filter ( ( typeZ ) - > typeZ . startsWith ( " § " ) )
. map ( ( typeZ ) - > typeZ . substring ( 1 ) )
. anyMatch ( ( typeZ ) - > typeZ . equals ( type ) )
) . isPresent ( ) ;
if ( arrayTypeNeeded ) {
registerArrayType ( versionPackage ,
versionClassType ,
typeOptionalSerializers ,
typeSerializeStatement ,
typeDeserializeStatement ,
typeMustGenerateSerializer ,
type
) ;
2021-05-25 11:17:44 +02:00
typeTypes . put ( " § " + type , getImmutableArrayType ( arrayClassName . get ( ) ) ) ;
2021-06-07 16:23:01 +02:00
}
if ( typeFamily ! = null ) {
2021-04-27 00:08:19 +02:00
typeFamily . put ( " § " + type , Family . I_TYPE_ARRAY ) ;
2021-03-01 21:04:06 +01:00
}
2021-06-07 16:23:01 +02:00
2021-03-01 21:04:06 +01:00
if ( nextVersionArrayTypeNeeded ) {
assert nextVersionTypeTypes ! = null ;
2021-04-27 00:08:19 +02:00
assert nextVersionTypeFamily ! = null ;
2021-05-25 11:17:44 +02:00
nextVersionTypeTypes . put ( " § " + type , getImmutableArrayType ( nextArrayClassName . get ( ) ) ) ;
2021-06-07 16:23:01 +02:00
}
if ( nextVersionTypeFamily ! = null ) {
2021-04-27 00:08:19 +02:00
nextVersionTypeFamily . put ( " § " + type , Family . I_TYPE_ARRAY ) ;
2021-03-01 21:04:06 +01:00
}
return new NeededTypes ( nullableTypeNeeded ,
nextVersionNullableTypeNeeded ,
arrayTypeNeeded ,
nextVersionArrayTypeNeeded
) ;
}
private String capitalizeAll ( String text ) {
StringBuilder sb = new StringBuilder ( ) ;
boolean firstChar = true ;
for ( char c : text . toCharArray ( ) ) {
if ( Character . isUpperCase ( c ) & & ! firstChar ) {
sb . append ( '_' ) ;
sb . append ( c ) ;
} else {
sb . append ( Character . toUpperCase ( c ) ) ;
}
firstChar = false ;
}
return sb . toString ( ) ;
}
private String fixType ( String fieldType ) {
if ( fieldType . endsWith ( " [] " ) & & fieldType . startsWith ( " - " ) ) {
throw new UnsupportedOperationException ( " Arrays cannot be null " ) ;
}
if ( fieldType . endsWith ( " [] " ) ) {
return " § " + fieldType . substring ( 0 , fieldType . length ( ) - 2 ) ;
} else {
return fieldType ;
}
}
private void addImmutableSetter ( Builder classBuilder , TypeName classType , Collection < String > fieldNames ,
String fieldName , TypeName fieldType , boolean isOverride ) {
var setterMethod = MethodSpec . methodBuilder ( " set " + capitalize ( fieldName ) ) ;
setterMethod . addModifiers ( Modifier . PUBLIC ) ;
setterMethod . addModifiers ( Modifier . FINAL ) ;
setterMethod . addAnnotation ( NotNull . class ) ;
var param = ParameterSpec . builder ( fieldType , fieldName , Modifier . FINAL ) ;
if ( ! fieldType . isPrimitive ( ) ) {
param . addAnnotation ( NotNull . class ) ;
}
if ( isOverride ) {
setterMethod . addAnnotation ( Override . class ) ;
}
setterMethod . addParameter ( param . build ( ) ) ;
setterMethod . returns ( classType ) ;
if ( ! fieldType . isPrimitive ( ) ) {
setterMethod . addStatement ( " $T.requireNonNull( " + fieldName + " ) " , Objects . class ) ;
2022-02-24 02:24:24 +01:00
setterMethod . beginControlFlow ( " if ($T.equals( " + fieldName + " , this. " + fieldName + " )) " , Objects . class ) ;
setterMethod . addStatement ( " return this " ) ;
setterMethod . endControlFlow ( ) ;
} else {
setterMethod . beginControlFlow ( " if ( " + fieldName + " == this. " + fieldName + " ) " ) ;
setterMethod . addStatement ( " return this " ) ;
setterMethod . endControlFlow ( ) ;
2021-03-01 21:04:06 +01:00
}
setterMethod . addCode ( " $[return $T.of( \ n$] " , classType ) ;
setterMethod . addCode ( " $> " ) ;
AtomicInteger i = new AtomicInteger ( fieldNames . size ( ) ) ;
for ( String otherFieldName : fieldNames ) {
boolean isLast = i . decrementAndGet ( ) = = 0 ;
setterMethod . addCode ( otherFieldName ) . addCode ( ( isLast ? " " : " , " ) + " \ n " ) ;
}
setterMethod . addCode ( " $< " ) ;
setterMethod . addStatement ( " ) " ) ;
classBuilder . addMethod ( setterMethod . build ( ) ) ;
}
2021-05-09 01:24:58 +02:00
private void addField ( Builder classBuilder , String fieldName ,
2021-07-13 16:23:20 +02:00
TypeName fieldType , boolean isRecord , boolean isFinal , boolean hasSetter ) {
2021-03-01 21:04:06 +01:00
if ( isFinal & & hasSetter ) {
throw new IllegalStateException ( ) ;
}
if ( hasSetter ) {
throw new UnsupportedOperationException ( ) ;
}
2021-05-21 00:20:02 +02:00
if ( isRecord ) {
var field = ParameterSpec . builder ( fieldType , fieldName ) ;
if ( ! fieldType . isPrimitive ( ) ) {
field . addAnnotation ( NotNull . class ) ;
}
if ( ! isFinal ) {
throw new IllegalArgumentException ( " Record fields must be final " ) ;
}
classBuilder . addRecordComponent ( field . build ( ) ) ;
} else {
var field = FieldSpec . builder ( fieldType , fieldName , Modifier . PRIVATE ) ;
if ( ! fieldType . isPrimitive ( ) ) {
field . addAnnotation ( NotNull . class ) ;
}
if ( isFinal ) {
field . addModifiers ( Modifier . FINAL ) ;
}
classBuilder . addField ( field . build ( ) ) ;
2021-03-01 21:04:06 +01:00
}
}
private int indexOf ( Set < String > value , String type ) {
if ( ! value . contains ( type ) ) {
return - 1 ;
}
int i = 0 ;
for ( String s : value ) {
if ( type . equals ( s ) ) {
break ;
}
i + + ;
}
return i ;
}
private String capitalize ( String field ) {
return Character . toUpperCase ( field . charAt ( 0 ) ) + field . substring ( 1 ) ;
}
private Optional < String > findNextVersion ( SourcesGeneratorConfiguration config , String version ) {
int currentVersion = Integer . parseInt ( getVersionShortInt ( version ) ) ;
int maxVersion = Integer . parseInt ( getVersionShortInt ( config . currentVersion ) ) ;
if ( currentVersion > = maxVersion ) {
return Optional . empty ( ) ;
}
AtomicInteger smallestNextVersion = new AtomicInteger ( Integer . MAX_VALUE ) ;
AtomicReference < String > smallestNextVersionString = new AtomicReference < > ( null ) ;
for ( Entry < String , VersionConfiguration > entry : config . versions . entrySet ( ) ) {
String possibleNextVersionString = entry . getKey ( ) ;
VersionConfiguration conf = entry . getValue ( ) ;
int possibleNextVersion = Integer . parseInt ( getVersionShortInt ( possibleNextVersionString ) ) ;
if ( possibleNextVersion < = maxVersion & & possibleNextVersion > currentVersion
& & possibleNextVersion < = smallestNextVersion . get ( ) ) {
smallestNextVersion . set ( possibleNextVersion ) ;
smallestNextVersionString . set ( possibleNextVersionString ) ;
}
}
String value = smallestNextVersionString . get ( ) ;
return Optional . ofNullable ( value ) ;
}
private String getVersionPackage ( String latestVersion , String basePackageName , String version ) {
if ( latestVersion . equals ( version ) ) {
return joinPackage ( basePackageName , " current " ) ;
} else {
return joinPackage ( basePackageName , " v " + getVersionCompleteInt ( version ) ) ;
}
}
private String joinPackage ( String basePackageName , String packageName ) {
if ( basePackageName . isBlank ( ) ) {
basePackageName = " org.generated " ;
}
if ( packageName . isBlank ( ) ) {
return basePackageName ;
} else {
return basePackageName + " . " + packageName ;
}
}
private void writeClass ( Path outPath , String classPackage , Builder versionsClass ) throws IOException {
JavaFile . builder ( classPackage , versionsClass . build ( ) ) . build ( ) . writeTo ( outPath ) ;
}
private String getVersionVarName ( String version ) {
return " V " + version . replace ( '-' , '_' ) . replace ( '.' , '_' ) ;
}
private String getVersionCompleteInt ( String version ) {
String [ ] parts = version . split ( " \\ . " ) ;
while ( parts [ 0 ] . length ( ) < 2 ) {
parts [ 0 ] = " 0 " + parts [ 0 ] ;
}
while ( parts [ 1 ] . length ( ) < 3 ) {
parts [ 1 ] = " 0 " + parts [ 1 ] ;
}
while ( parts [ 2 ] . length ( ) < 4 ) {
parts [ 2 ] = " 0 " + parts [ 2 ] ;
}
return parts [ 0 ] + parts [ 1 ] + parts [ 2 ] ;
}
private String getVersionShortInt ( String version ) {
version = getVersionCompleteInt ( version ) ;
while ( version . startsWith ( " 0 " ) ) {
version = version . substring ( 1 ) ;
}
if ( version . isBlank ( ) ) {
return " 0 " ;
}
return version ;
}
2021-04-27 00:08:19 +02:00
private enum Family {
BASIC ,
NULLABLE_BASIC ,
NULLABLE_OTHER ,
I_TYPE_ARRAY ,
SPECIAL_NATIVE , GENERIC , NULLABLE_GENERIC , OTHER
}
2021-03-01 21:04:06 +01:00
}