Skip to content

Commit 0515e5b

Browse files
committed
feat : generate component view
1 parent 991fa63 commit 0515e5b

6 files changed

Lines changed: 167 additions & 71 deletions

File tree

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package com.github.elebras1.flecs;
2+
3+
public interface ComponentView {
4+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package com.github.elebras1.flecs.processor;
2+
3+
import com.github.elebras1.flecs.annotation.FixedArray;
4+
import com.github.elebras1.flecs.annotation.FixedString;
5+
6+
import javax.lang.model.element.Element;
7+
import javax.lang.model.element.PackageElement;
8+
import javax.lang.model.element.TypeElement;
9+
import javax.lang.model.element.VariableElement;
10+
11+
import static com.github.elebras1.flecs.processor.ComponentAbstractGenerator.DEFAULT_STRING_SIZE;
12+
13+
public abstract class AbstractGenerator implements Generator {
14+
15+
protected String getPackageName(TypeElement element) {
16+
Element current = element.getEnclosingElement();
17+
while (current != null && !(current instanceof PackageElement)) {
18+
current = current.getEnclosingElement();
19+
}
20+
return current != null ? ((PackageElement) current).getQualifiedName().toString() : "";
21+
}
22+
23+
protected String getGetterMethod(String type) {
24+
return switch (type) {
25+
case "byte" -> "getByte";
26+
case "short" -> "getShort";
27+
case "int" -> "getInt";
28+
case "long" -> "getLong";
29+
case "float" -> "getFloat";
30+
case "double" -> "getDouble";
31+
case "boolean" -> "getBoolean";
32+
case "byte[]" -> "getByteArray";
33+
case "short[]" -> "getShortArray";
34+
case "int[]" -> "getIntArray";
35+
case "long[]" -> "getLongArray";
36+
case "float[]" -> "getFloatArray";
37+
case "double[]" -> "getDoubleArray";
38+
case "boolean[]" -> "getBooleanArray";
39+
case "java.lang.String" -> "getFixedString";
40+
default -> throw new IllegalArgumentException("Unsupported type: " + type);
41+
};
42+
}
43+
44+
protected int getStringSize(VariableElement field) {
45+
FixedString annotation = field.getAnnotation(FixedString.class);
46+
if (annotation != null) {
47+
int size = annotation.size();
48+
if ((size & (size - 1)) != 0) {
49+
throw new IllegalArgumentException("Field '" + field.getSimpleName() + "': @FixedString size must be a power of 2. Got: " + size);
50+
}
51+
return size;
52+
}
53+
return DEFAULT_STRING_SIZE;
54+
}
55+
56+
protected int getArrayLength(VariableElement field) {
57+
FixedArray annotation = field.getAnnotation(FixedArray.class);
58+
if (annotation != null) {
59+
int length = annotation.length();
60+
if (length <= 0) {
61+
throw new IllegalArgumentException("Field '" + field.getSimpleName() + "': @FixedArray length must be greater than 0. Got: " + length);
62+
}
63+
return length;
64+
}
65+
throw new IllegalArgumentException("Field '" + field.getSimpleName() + "': Missing @FixedArray annotation for array type.");
66+
}
67+
}

src/main/java/com/github/elebras1/flecs/processor/ComponentCodeGenerator.java renamed to src/main/java/com/github/elebras1/flecs/processor/ComponentAbstractGenerator.java

Lines changed: 8 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,19 @@
11
package com.github.elebras1.flecs.processor;
22

3-
4-
import com.github.elebras1.flecs.annotation.FixedArray;
5-
import com.github.elebras1.flecs.annotation.FixedString;
63
import com.palantir.javapoet.*;
74

85
import javax.lang.model.element.*;
96
import java.lang.foreign.MemoryLayout;
107
import java.lang.foreign.MemorySegment;
118
import java.util.List;
129

13-
public class ComponentCodeGenerator {
10+
public class ComponentAbstractGenerator extends AbstractGenerator {
1411

15-
private static final String LAYOUT_FIELD_CLASS = "com.github.elebras1.flecs.util.LayoutField";
16-
private static final String COMPONENT_INTERFACE = "com.github.elebras1.flecs.Component";
17-
private static final int DEFAULT_STRING_SIZE = 32;
12+
protected static final String LAYOUT_FIELD_CLASS = "com.github.elebras1.flecs.util.LayoutField";
13+
protected static final String COMPONENT_INTERFACE = "com.github.elebras1.flecs.Component";
14+
protected static final int DEFAULT_STRING_SIZE = 32;
1815

19-
public JavaFile generateComponentClass(TypeElement recordElement, List<VariableElement> fields) {
16+
public JavaFile generate(TypeElement recordElement, List<VariableElement> fields) {
2017
String packageName = this.getPackageName(recordElement);
2118
String recordName = recordElement.getSimpleName().toString();
2219
String componentClassName = recordName + "Component";
@@ -37,42 +34,7 @@ public JavaFile generateComponentClass(TypeElement recordElement, List<VariableE
3734
.addMethod(this.createGetInstanceMethod(packageName, recordName))
3835
.build();
3936

40-
return JavaFile.builder(packageName, componentClass)
41-
.addFileComment("Generated by ComponentProcessor")
42-
.indent(" ")
43-
.build();
44-
}
45-
46-
private String getPackageName(TypeElement element) {
47-
Element current = element.getEnclosingElement();
48-
while (current != null && !(current instanceof PackageElement)) {
49-
current = current.getEnclosingElement();
50-
}
51-
return current != null ? ((PackageElement) current).getQualifiedName().toString() : "";
52-
}
53-
54-
private int getStringSize(VariableElement field) {
55-
FixedString annotation = field.getAnnotation(FixedString.class);
56-
if (annotation != null) {
57-
int size = annotation.size();
58-
if ((size & (size - 1)) != 0) {
59-
throw new IllegalArgumentException("Field '" + field.getSimpleName() + "': @FixedString size must be a power of 2. Got: " + size);
60-
}
61-
return size;
62-
}
63-
return DEFAULT_STRING_SIZE;
64-
}
65-
66-
private int getArrayLength(VariableElement field) {
67-
FixedArray annotation = field.getAnnotation(FixedArray.class);
68-
if (annotation != null) {
69-
int length = annotation.length();
70-
if (length <= 0) {
71-
throw new IllegalArgumentException("Field '" + field.getSimpleName() + "': @FixedArray length must be greater than 0. Got: " + length);
72-
}
73-
return length;
74-
}
75-
throw new IllegalArgumentException("Field '" + field.getSimpleName() + "': Missing @FixedArray annotation for array type.");
37+
return JavaFile.builder(packageName, componentClass).addFileComment("Generated by ComponentProcessor").indent(" ").build();
7638
}
7739

7840
private FieldSpec createLayoutField(String recordName, List<VariableElement> fields) {
@@ -102,15 +64,15 @@ private FieldSpec createLayoutField(String recordName, List<VariableElement> fie
10264
layoutBuilder.unindent();
10365
}
10466
layoutBuilder.add(")");
105-
return FieldSpec.builder(MemoryLayout.class, "LAYOUT", Modifier.PRIVATE, Modifier.STATIC, Modifier.FINAL).initializer(layoutBuilder.build()).build();
67+
return FieldSpec.builder(MemoryLayout.class, "LAYOUT", Modifier.PROTECTED, Modifier.STATIC, Modifier.FINAL).initializer(layoutBuilder.build()).build();
10668
}
10769

10870
private List<FieldSpec> createOffsetFields(List<VariableElement> fields) {
10971
return fields.stream()
11072
.map(field -> {
11173
String fieldName = field.getSimpleName().toString();
11274
String constantName = "OFFSET_" + fieldName.toUpperCase();
113-
return FieldSpec.builder(long.class, constantName, Modifier.PRIVATE, Modifier.STATIC, Modifier.FINAL)
75+
return FieldSpec.builder(long.class, constantName, Modifier.PROTECTED, Modifier.STATIC, Modifier.FINAL)
11476
.initializer("$L.offsetOf(LAYOUT, $S)", LAYOUT_FIELD_CLASS, fieldName)
11577
.build();
11678
})
@@ -254,27 +216,6 @@ private String getLayoutMethod(String type) {
254216
};
255217
}
256218

257-
private String getGetterMethod(String type) {
258-
return switch (type) {
259-
case "byte" -> "getByte";
260-
case "short" -> "getShort";
261-
case "int" -> "getInt";
262-
case "long" -> "getLong";
263-
case "float" -> "getFloat";
264-
case "double" -> "getDouble";
265-
case "boolean" -> "getBoolean";
266-
case "byte[]" -> "getByteArray";
267-
case "short[]" -> "getShortArray";
268-
case "int[]" -> "getIntArray";
269-
case "long[]" -> "getLongArray";
270-
case "float[]" -> "getFloatArray";
271-
case "double[]" -> "getDoubleArray";
272-
case "boolean[]" -> "getBooleanArray";
273-
case "java.lang.String" -> "getFixedString";
274-
default -> throw new IllegalArgumentException("Unsupported type: " + type);
275-
};
276-
}
277-
278219
private MethodSpec createOffsetOfMethod(List<VariableElement> fields) {
279220
MethodSpec.Builder method = MethodSpec.methodBuilder("offsetOf")
280221
.addAnnotation(Override.class)

src/main/java/com/github/elebras1/flecs/processor/ComponentProcessor.java

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ public class ComponentProcessor extends AbstractProcessor {
1616
private static final Set<String> SUPPORTED_TYPES = Set.of("byte", "short", "int", "long", "float", "double", "boolean", "byte[]", "short[]", "int[]", "long[]", "float[]", "double[]", "boolean[]", "java.lang.String");
1717
private Messager messager;
1818
private Filer filer;
19-
private ComponentCodeGenerator generator;
19+
private ComponentAbstractGenerator componentGenerator;
20+
private ComponentViewAbstractGenerator componentViewGenerator;
2021
private ComponentMapGenerator mapGenerator;
2122
private List<TypeElement> processedComponents;
2223
private boolean mapGenerated;
@@ -26,7 +27,8 @@ public synchronized void init(ProcessingEnvironment processingEnv) {
2627
super.init(processingEnv);
2728
this.messager = processingEnv.getMessager();
2829
this.filer = processingEnv.getFiler();
29-
this.generator = new ComponentCodeGenerator();
30+
this.componentGenerator = new ComponentAbstractGenerator();
31+
this.componentViewGenerator = new ComponentViewAbstractGenerator();
3032
this.mapGenerator = new ComponentMapGenerator();
3133
this.processedComponents = new ArrayList<>();
3234
this.mapGenerated = false;
@@ -86,8 +88,11 @@ private void processRecord(TypeElement recordElement) throws IOException {
8688
}
8789
}
8890

89-
JavaFile javaFile = generator.generateComponentClass(recordElement, fields);
90-
javaFile.writeTo(filer);
91+
JavaFile javaComponentFile = this.componentGenerator.generate(recordElement, fields);
92+
javaComponentFile.writeTo(this.filer);
93+
94+
JavaFile javaComponentViewFile = this.componentViewGenerator.generate(recordElement, fields);
95+
javaComponentViewFile.writeTo(this.filer);
9196
}
9297

9398
private List<VariableElement> extractRecordComponents(TypeElement recordElement) {
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package com.github.elebras1.flecs.processor;
2+
3+
import com.palantir.javapoet.*;
4+
5+
import javax.lang.model.element.*;
6+
import java.lang.foreign.MemorySegment;
7+
import java.util.List;
8+
9+
import static com.github.elebras1.flecs.processor.ComponentAbstractGenerator.LAYOUT_FIELD_CLASS;
10+
11+
public class ComponentViewAbstractGenerator extends AbstractGenerator {
12+
public JavaFile generate(TypeElement recordElement, List<VariableElement> fields) {
13+
String packageName = this.getPackageName(recordElement);
14+
String recordName = recordElement.getSimpleName().toString();
15+
String componentViewClassName = recordName + "ComponentView";
16+
17+
String componentReference = packageName + "." + recordName + "Component";
18+
19+
TypeSpec componentClass = TypeSpec.classBuilder(componentViewClassName)
20+
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
21+
.addField(this.createField())
22+
.addMethod(this.createSetterMethod())
23+
.addMethods(this.createMethods(fields, componentReference))
24+
.build();
25+
26+
return JavaFile.builder(packageName, componentClass).addFileComment("Generated by ComponentViewGenerator").indent(" ").build();
27+
}
28+
29+
private FieldSpec createField() {
30+
return FieldSpec.builder(MemorySegment.class, "memorySegment", Modifier.PRIVATE).build();
31+
}
32+
33+
private MethodSpec createSetterMethod() {
34+
return MethodSpec.methodBuilder("setMemorySegment")
35+
.addModifiers(Modifier.PUBLIC)
36+
.addParameter(MemorySegment.class, "memorySegment")
37+
.addJavadoc("Internal API - Do not use.\n")
38+
.addStatement("this.memorySegment = memorySegment")
39+
.build();
40+
}
41+
42+
private Iterable<MethodSpec> createMethods(List<VariableElement> fields, String componentReference) {
43+
return fields.stream().map(field -> this.createMethod(field, componentReference)).toList();
44+
}
45+
46+
private MethodSpec createMethod(VariableElement field, String componentReference) {
47+
MethodSpec.Builder method = MethodSpec.methodBuilder(field.getSimpleName().toString()).addModifiers(Modifier.PUBLIC).returns(TypeName.get(field.asType()));
48+
49+
String fieldName = field.getSimpleName().toString();
50+
String offsetName = "OFFSET_" + fieldName.toUpperCase();
51+
String typeName = field.asType().toString();
52+
String getterMethod = this.getGetterMethod(typeName);
53+
if ("java.lang.String".equals(typeName)) {
54+
int size = this.getStringSize(field);
55+
method.addStatement("$T $L = $L.$L(memorySegment, $L.$L, $L)", String.class, fieldName, LAYOUT_FIELD_CLASS, getterMethod, componentReference, offsetName, size);
56+
} else if (typeName.endsWith("[]")) {
57+
int length = this.getArrayLength(field);
58+
method.addStatement("$L $L = $L.$L(memorySegment, $L.$L, $L)", typeName, fieldName, LAYOUT_FIELD_CLASS, getterMethod, componentReference, offsetName, length);
59+
} else {
60+
method.addStatement("$L $L = $L.$L(memorySegment, $L.$L)", typeName, fieldName, LAYOUT_FIELD_CLASS, getterMethod, componentReference, offsetName);
61+
}
62+
63+
method.addStatement("return $L", fieldName);
64+
65+
return method.build();
66+
}
67+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package com.github.elebras1.flecs.processor;
2+
3+
import com.palantir.javapoet.JavaFile;
4+
5+
import javax.lang.model.element.TypeElement;
6+
import javax.lang.model.element.VariableElement;
7+
import java.util.List;
8+
9+
public interface Generator {
10+
11+
JavaFile generate(TypeElement recordElement, List<VariableElement> fields);
12+
}

0 commit comments

Comments
 (0)