Skip to content

Commit 5779050

Browse files
committed
deconstructed object is now backed by a map instead of a list
1 parent ce292a1 commit 5779050

File tree

5 files changed

+119
-93
lines changed

5 files changed

+119
-93
lines changed

foundry-core/src/main/java/org/machinemc/foundry/model/ClassModel.java

Lines changed: 7 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ public class ClassModel {
2929

3030
private final ModelAttribute[] attributes;
3131
private final ConstructionMethod constructionMethod;
32-
private final Constructor<?> constructor;
3332

3433
/**
3534
* Creates a class model for the specified type.
@@ -39,6 +38,10 @@ public class ClassModel {
3938
* @throws IllegalStateException if the class does not satisfy the requirements
4039
*/
4140
public static ClassModel of(Class<?> type) {
41+
if (type.isEnum() || type.isInterface() || type.isAnnotation() || Modifier.isAbstract(type.getModifiers()))
42+
throw new UnsupportedOperationException("Can not automatically resolve class model for '"
43+
+ type.getName() + "'");
44+
4245
if (type.isRecord())
4346
return ofRecord(type);
4447

@@ -58,7 +61,7 @@ public static ClassModel of(Class<?> type) {
5861
Constructor<?> noArgs = noArgsConstructor(type);
5962
Preconditions.checkState(noArgs != null, "Class '" + type.getName() + "' is missing "
6063
+ "no arguments constructor");
61-
return new ClassModel(attributes, ConstructionMethod.NO_ARGS_CONSTRUCTOR, noArgs);
64+
return new ClassModel(attributes, ConstructionMethod.NO_ARGS_CONSTRUCTOR);
6265
}
6366

6467
private static ClassModel ofRecord(Class<?> type) {
@@ -67,21 +70,12 @@ private static ClassModel ofRecord(Class<?> type) {
6770
.toArray(ModelAttribute[]::new);
6871
Constructor<?> constructor = allArgsConstructor(type, attributes);
6972
Preconditions.checkNotNull(constructor); // is always present on records
70-
return new ClassModel(attributes, ConstructionMethod.ALL_ARGS_CONSTRUCTOR, constructor);
73+
return new ClassModel(attributes, ConstructionMethod.ALL_ARGS_CONSTRUCTOR);
7174
}
7275

73-
protected ClassModel(ModelAttribute[] attributes, ConstructionMethod constructionMethod,
74-
Constructor<?> constructor) {
76+
protected ClassModel(ModelAttribute[] attributes, ConstructionMethod constructionMethod) {
7577
this.attributes = attributes;
7678
this.constructionMethod = constructionMethod;
77-
this.constructor = constructor;
78-
}
79-
80-
/**
81-
* @return the source class of this model
82-
*/
83-
public Class<?> getSource() {
84-
return constructor.getDeclaringClass();
8579
}
8680

8781
/**
@@ -103,13 +97,6 @@ public ConstructionMethod getConstructionMethod() {
10397
return constructionMethod;
10498
}
10599

106-
/**
107-
* @return constructor used by this model
108-
*/
109-
public Constructor<?> getConstructor() {
110-
return constructor;
111-
}
112-
113100
/**
114101
* Which construction method is used when creating new instance of the class.
115102
*/

foundry-core/src/main/java/org/machinemc/foundry/model/DeconstructedObject.java

Lines changed: 65 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,10 @@
22

33
import org.jetbrains.annotations.NotNull;
44
import org.jetbrains.annotations.Unmodifiable;
5+
import org.machinemc.foundry.DataHandler;
56

67
import java.lang.reflect.AnnotatedType;
7-
import java.util.Collections;
8-
import java.util.Iterator;
9-
import java.util.List;
10-
import java.util.function.Function;
8+
import java.util.*;
119

1210
/**
1311
* Represents an object that has been deconstructed.
@@ -16,16 +14,16 @@
1614
public final class DeconstructedObject implements Iterable<DeconstructedObject.Field> {
1715

1816
/**
19-
* Creates a function that deconstructs an instance of the specified type into a
17+
* Creates a data handler that deconstructs an instance of the specified type into a
2018
* {@link DeconstructedObject}.
2119
* <p>
22-
* The returned function is thread-safe and optimized for repeated use.
20+
* The returned data handler is thread-safe and optimized for repeated use.
2321
*
2422
* @param type type the object to deconstruct
2523
* @param <T> type of the object
26-
* @return a function that converts an instance of {@link T} into a deconstructed object
24+
* @return a data handler that converts an instance of {@link T} into a deconstructed object
2725
*/
28-
public static <T> Function<T, DeconstructedObject> createDeconstructor(Class<T> type) {
26+
public static <T> DataHandler<T, DeconstructedObject> createDeconstructor(Class<T> type) {
2927
ClassModel classModel = ClassModel.of(type);
3028
ObjectFactory<T> objectFactory = ObjectFactory.create(type, classModel);
3129
FieldsExtractor fieldsExtractor = FieldsExtractor.of(classModel);
@@ -37,42 +35,62 @@ public static <T> Function<T, DeconstructedObject> createDeconstructor(Class<T>
3735
}
3836

3937
/**
40-
* Creates a function that reconstructs an instance of the specified type from a
38+
* Creates a data handler that reconstructs an instance of the specified type from a
4139
* {@link DeconstructedObject}.
4240
* <p>
43-
* The returned function expects the input {@link DeconstructedObject} to contain fields
41+
* The returned data handler expects the input {@link DeconstructedObject} to contain fields
4442
* compatible with the target class schema.
4543
* <p>
46-
* The returned function is thread-safe and optimized for repeated use.
44+
* The returned data handler is thread-safe and optimized for repeated use.
4745
*
4846
* @param type type the object to reconstruct
4947
* @param <T> type of the object
50-
* @return a function that converts deconstructed object into an instance of {@link T}
48+
* @return a data handler that converts deconstructed object into an instance of {@link T}
5149
*/
52-
public static <T> Function<DeconstructedObject, T> createConstructor(Class<T> type) {
50+
public static <T> DataHandler<DeconstructedObject, T> createConstructor(Class<T> type) {
5351
ClassModel classModel = ClassModel.of(type);
5452
ObjectFactory<T> objectFactory = ObjectFactory.create(type, classModel);
5553
FieldsInjector fieldsInjector = FieldsInjector.of(classModel);
5654
return deconstructed -> {
5755
ModelDataContainer container = objectFactory.newContainer();
58-
fieldsInjector.write(deconstructed.fields, container);
56+
fieldsInjector.write(deconstructed.asList(), container);
5957
return objectFactory.read(container);
6058
};
6159
}
6260

63-
private final List<Field> fields;
61+
private final @Unmodifiable Map<String, Field> fields;
6462

65-
DeconstructedObject(List<Field> fields) {
66-
this.fields = fields;
63+
DeconstructedObject(Map<String, Field> fields) {
64+
this.fields = Collections.unmodifiableMap(fields);
65+
}
66+
67+
/**
68+
* Returns field with given name of empty if none exists.
69+
*
70+
* @param name name of the field
71+
* @return field with given name
72+
*/
73+
public Optional<Field> getField(String name) {
74+
return Optional.ofNullable(fields.get(name));
75+
}
76+
77+
/**
78+
* Returns an unmodifiable map of the fields in this deconstructed object, mapped
79+
* by their names.
80+
*
81+
* @return map of fields
82+
*/
83+
public @Unmodifiable Map<String, Field> asMap() {
84+
return fields;
6785
}
6886

6987
/**
7088
* Returns an unmodifiable list of the fields in this deconstructed object.
7189
*
72-
* @return the list of fields
90+
* @return list of fields
7391
*/
7492
public @Unmodifiable List<Field> asList() {
75-
return Collections.unmodifiableList(fields);
93+
return List.copyOf(fields.values());
7694
}
7795

7896
/**
@@ -84,7 +102,7 @@ public int size() {
84102

85103
@Override
86104
public @NotNull Iterator<Field> iterator() {
87-
return fields.iterator();
105+
return fields.values().iterator();
88106
}
89107

90108
/**
@@ -109,62 +127,89 @@ public sealed interface Field {
109127

110128
}
111129

130+
/**
131+
* Primitive boolean field.
132+
*/
112133
public record BoolField(String name, AnnotatedType annotatedType, boolean value) implements Field {
113134
@Override
114135
public Class<?> type() {
115136
return boolean.class;
116137
}
117138
}
118139

140+
/**
141+
* Primitive char field.
142+
*/
119143
public record CharField(String name, AnnotatedType annotatedType, char value) implements Field {
120144
@Override
121145
public Class<?> type() {
122146
return char.class;
123147
}
124148
}
125149

150+
/**
151+
* Primitive byte field.
152+
*/
126153
public record ByteField(String name, AnnotatedType annotatedType, byte value) implements Field {
127154
@Override
128155
public Class<?> type() {
129156
return byte.class;
130157
}
131158
}
132159

160+
/**
161+
* Primitive short field.
162+
*/
133163
public record ShortField(String name, AnnotatedType annotatedType, short value) implements Field {
134164
@Override
135165
public Class<?> type() {
136166
return short.class;
137167
}
138168
}
139169

170+
/**
171+
* Primitive int field.
172+
*/
140173
public record IntField(String name, AnnotatedType annotatedType, int value) implements Field {
141174
@Override
142175
public Class<?> type() {
143176
return int.class;
144177
}
145178
}
146179

180+
/**
181+
* Primitive long field.
182+
*/
147183
public record LongField(String name, AnnotatedType annotatedType, long value) implements Field {
148184
@Override
149185
public Class<?> type() {
150186
return long.class;
151187
}
152188
}
153189

190+
/**
191+
* Primitive float field.
192+
*/
154193
public record FloatField(String name, AnnotatedType annotatedType, float value) implements Field {
155194
@Override
156195
public Class<?> type() {
157196
return float.class;
158197
}
159198
}
160199

200+
/**
201+
* Primitive double field.
202+
*/
161203
public record DoubleField(String name, AnnotatedType annotatedType, double value) implements Field {
162204
@Override
163205
public Class<?> type() {
164206
return double.class;
165207
}
166208
}
167209

210+
/**
211+
* Primitive object field.
212+
*/
168213
public record ObjectField(String name, Class<?> type, AnnotatedType annotatedType, Object value) implements Field {
169214
}
170215

foundry-core/src/main/java/org/machinemc/foundry/model/FieldsExtractor.java

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
package org.machinemc.foundry.model;
22

3+
import com.google.common.collect.ImmutableMap;
4+
import com.google.common.collect.Maps;
35
import org.jetbrains.annotations.ApiStatus;
46

57
import java.lang.reflect.AnnotatedType;
6-
import java.util.ArrayList;
7-
import java.util.List;
8+
import java.util.Map;
89
import java.util.function.Function;
910

1011
/**
@@ -69,14 +70,16 @@ static FieldsExtractor of(ClassModel model) {
6970
* Reads fields from a container.
7071
*
7172
* @param dataContainer container to read
72-
* @return list of fields
73+
* @return map of fields
7374
*/
74-
List<DeconstructedObject.Field> read(ModelDataContainer dataContainer) {
75-
List<DeconstructedObject.Field> fields = new ArrayList<>(fieldReaders.length);
76-
for (var reader : fieldReaders) {
77-
fields.add(reader.apply(dataContainer));
75+
Map<String, DeconstructedObject.Field> read(ModelDataContainer dataContainer) {
76+
//noinspection unchecked
77+
Map.Entry<String, DeconstructedObject.Field>[] fields = new Map.Entry[fieldReaders.length];
78+
for (int i = 0; i < fieldReaders.length; i++) {
79+
var field = fieldReaders[i].apply(dataContainer);
80+
fields[i] = Maps.immutableEntry(field.name(), field);
7881
}
79-
return fields;
82+
return ImmutableMap.ofEntries(fields);
8083
}
8184

8285
}

foundry-core/src/main/java/org/machinemc/foundry/model/ObjectFactory.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package org.machinemc.foundry.model;
22

3-
import com.google.common.base.Preconditions;
43
import org.jetbrains.annotations.ApiStatus;
54
import org.objectweb.asm.ClassWriter;
65
import org.objectweb.asm.MethodVisitor;
@@ -51,7 +50,6 @@ public static <T> ObjectFactory<T> create(Class<T> type) {
5150
* @param <T> object type
5251
*/
5352
public static <T> ObjectFactory<T> create(Class<T> type, ClassModel classModel) {
54-
Preconditions.checkState(type.equals(classModel.getSource()), "Unexpected class model");
5553
//noinspection unchecked
5654
return (ObjectFactory<T>) CACHE.computeIfAbsent(type, _ -> load(type, classModel));
5755
}

0 commit comments

Comments
 (0)