Skip to content

Commit 80358d8

Browse files
authored
Merge pull request #254 from aether-framework/bugfix/129-131-139-142-143-144-159-168-169-178-mixed-core-fixes
Refactor migration handling for consistency and improved validation
2 parents 0e06805 + 624772a commit 80358d8

11 files changed

Lines changed: 92 additions & 30 deletions

File tree

aether-datafixers-api/src/main/java/de/splatgames/aether/datafixers/api/optic/Prism.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,8 @@ public S reverseGet(@NotNull final A value) {
270270
* @throws NullPointerException if {@code source} or {@code modifier} is {@code null}
271271
*/
272272
@NotNull
273+
// Safe cast: for monomorphic prisms (S == T), the source is returned unchanged
274+
// when getOption returns empty, preserving the original type.
273275
@SuppressWarnings("unchecked")
274276
default T modify(@NotNull final S source,
275277
@NotNull final Function<A, B> modifier) {
@@ -301,6 +303,8 @@ default T modify(@NotNull final S source,
301303
* @throws NullPointerException if {@code source} or {@code value} is {@code null}
302304
*/
303305
@NotNull
306+
// Safe cast: for monomorphic prisms (S == T), the source is returned unchanged
307+
// when getOption returns empty, preserving the original type.
304308
@SuppressWarnings("unchecked")
305309
default T set(@NotNull final S source, @NotNull final B value) {
306310
Preconditions.checkNotNull(source, "source must not be null");

aether-datafixers-codec/src/main/java/de/splatgames/aether/datafixers/codec/xml/jackson/JacksonXmlOps.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1556,6 +1556,23 @@ public <U> JsonNode convertTo(@NotNull final DynamicOps<U> sourceOps,
15561556
return empty();
15571557
}
15581558

1559+
/**
1560+
* Validates that the given node is suitable for XML serialization.
1561+
*
1562+
* <p>XML requires exactly one root element. This method checks that the given
1563+
* node is an object (map) node, which will become the root element when serialized.</p>
1564+
*
1565+
* @param node the node to validate, must not be {@code null}
1566+
* @throws IllegalArgumentException if the node is not suitable for XML serialization
1567+
*/
1568+
public static void validateForSerialization(@NotNull final JsonNode node) {
1569+
Preconditions.checkNotNull(node, "node must not be null");
1570+
if (!node.isObject()) {
1571+
throw new IllegalArgumentException(
1572+
"XML serialization requires an object node as root, but got: " + node.getNodeType());
1573+
}
1574+
}
1575+
15591576
/**
15601577
* Returns a string representation of this {@code DynamicOps} instance.
15611578
*

aether-datafixers-core/src/main/java/de/splatgames/aether/datafixers/core/AetherDataFixer.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,10 @@ public TaggedDynamic update(
181181
Preconditions.checkNotNull(input, "input must not be null");
182182
Preconditions.checkNotNull(fromVersion, "fromVersion must not be null");
183183
Preconditions.checkNotNull(toVersion, "toVersion must not be null");
184+
Preconditions.checkArgument(
185+
fromVersion.compareTo(toVersion) <= 0,
186+
"fromVersion (%s) must be <= toVersion (%s)", fromVersion, toVersion
187+
);
184188

185189
@SuppressWarnings("unchecked") final Dynamic<Object> dyn = (Dynamic<Object>) input.value();
186190

aether-datafixers-core/src/main/java/de/splatgames/aether/datafixers/core/bootstrap/DataFixerRuntimeFactory.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
import de.splatgames.aether.datafixers.core.fix.DataFixerBuilder;
3030
import de.splatgames.aether.datafixers.core.schema.SimpleSchemaRegistry;
3131
import org.jetbrains.annotations.NotNull;
32+
import org.slf4j.Logger;
33+
import org.slf4j.LoggerFactory;
3234

3335
/**
3436
* Factory for creating fully configured {@link AetherDataFixer} instances.
@@ -67,6 +69,8 @@
6769
*/
6870
public final class DataFixerRuntimeFactory {
6971

72+
private static final Logger LOG = LoggerFactory.getLogger(DataFixerRuntimeFactory.class);
73+
7074
/**
7175
* Creates a fully configured data fixer from a bootstrap.
7276
*
@@ -85,6 +89,13 @@ public AetherDataFixer create(
8589

8690
final SimpleSchemaRegistry schemas = new SimpleSchemaRegistry();
8791
bootstrap.registerSchemas(schemas);
92+
93+
if (schemas.stream().findAny().isEmpty()) {
94+
LOG.warn("Bootstrap registered no schemas — DataFixer may not function correctly");
95+
} else if (schemas.get(currentVersion) == null) {
96+
LOG.warn("No schema registered for currentVersion: {}", currentVersion);
97+
}
98+
8899
schemas.freeze();
89100

90101
final DataFixerBuilder builder = new DataFixerBuilder(currentVersion);

aether-datafixers-core/src/main/java/de/splatgames/aether/datafixers/core/codec/SimpleCodecRegistry.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ public boolean has(@NotNull final TypeReference ref) {
8787
}
8888

8989
@Override
90-
public void freeze() {
90+
public synchronized void freeze() {
9191
if (!this.frozen) {
9292
this.codecs = Map.copyOf(this.codecs);
9393
this.frozen = true;

aether-datafixers-core/src/main/java/de/splatgames/aether/datafixers/core/diagnostic/DiagnosticRuleWrapper.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -93,14 +93,15 @@ public Optional<Typed<?>> rewrite(
9393
return this.delegate.rewrite(type, input);
9494
}
9595

96-
final Instant start = Instant.now();
96+
final Instant timestamp = Instant.now();
97+
final long startNano = System.nanoTime();
9798
final Optional<Typed<?>> result = this.delegate.rewrite(type, input);
98-
final Duration duration = Duration.between(start, Instant.now());
99+
final Duration duration = Duration.ofNanos(System.nanoTime() - startNano);
99100

100101
final RuleApplication application = new RuleApplication(
101102
this.delegate.toString(),
102103
type.describe(),
103-
start,
104+
timestamp,
104105
duration,
105106
result.isPresent(),
106107
null

aether-datafixers-core/src/main/java/de/splatgames/aether/datafixers/core/schema/SimpleSchemaRegistry.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ public Schema latest() {
129129
}
130130

131131
@Override
132-
public void freeze() {
132+
public synchronized void freeze() {
133133
if (!this.frozen) {
134134
this.schemas = Collections.unmodifiableNavigableMap(new TreeMap<>(this.schemas));
135135
this.frozen = true;

aether-datafixers-core/src/main/java/de/splatgames/aether/datafixers/core/type/SimpleTypeRegistry.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ public Set<TypeReference> references() {
9393
}
9494

9595
@Override
96-
public void freeze() {
96+
public synchronized void freeze() {
9797
if (!this.frozen) {
9898
this.types = Map.copyOf(this.types);
9999
this.frozen = true;

aether-datafixers-schema-tools/src/main/java/de/splatgames/aether/datafixers/schematools/analysis/MigrationAnalyzer.java

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -381,10 +381,8 @@ private MigrationStep analyzeStep(
381381
).schemaDiff(diff)
382382
.affectedTypes(affectedTypes);
383383

384-
// Add first fix if present (simplified - in reality there might be multiple)
385-
if (!fixes.isEmpty()) {
386-
stepBuilder.fix(fixes.get(0));
387-
}
384+
// Add all applicable fixes for this migration step
385+
stepBuilder.fixes(fixes);
388386

389387
return stepBuilder.build();
390388
}

aether-datafixers-schema-tools/src/main/java/de/splatgames/aether/datafixers/schematools/analysis/MigrationStep.java

Lines changed: 44 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
import org.jetbrains.annotations.NotNull;
3232
import org.jetbrains.annotations.Nullable;
3333

34+
import java.util.ArrayList;
35+
import java.util.List;
3436
import java.util.Objects;
3537
import java.util.Optional;
3638
import java.util.Set;
@@ -82,9 +84,9 @@ public final class MigrationStep {
8284
private final DataVersion targetVersion;
8385

8486
/**
85-
* The DataFix applied in this step, or {@code null} if no fix applies.
87+
* The DataFixes applied in this step. Empty if no fixes apply.
8688
*/
87-
private final DataFix<?> fix;
89+
private final List<DataFix<?>> fixes;
8890

8991
/**
9092
* The schema diff between source and target versions, or {@code null} if not computed.
@@ -105,20 +107,20 @@ public final class MigrationStep {
105107
*
106108
* @param sourceVersion the source version, must not be {@code null}
107109
* @param targetVersion the target version, must not be {@code null}
108-
* @param fix the DataFix for this step, may be {@code null}
110+
* @param fixes the DataFixes for this step, must not be {@code null}
109111
* @param schemaDiff the schema diff, may be {@code null}
110112
* @param affectedTypes the affected types, must not be {@code null}
111113
*/
112114
private MigrationStep(
113115
@NotNull final DataVersion sourceVersion,
114116
@NotNull final DataVersion targetVersion,
115-
@Nullable final DataFix<?> fix,
117+
@NotNull final List<DataFix<?>> fixes,
116118
@Nullable final SchemaDiff schemaDiff,
117119
@NotNull final Set<TypeReference> affectedTypes
118120
) {
119121
this.sourceVersion = Preconditions.checkNotNull(sourceVersion, "sourceVersion must not be null");
120122
this.targetVersion = Preconditions.checkNotNull(targetVersion, "targetVersion must not be null");
121-
this.fix = fix;
123+
this.fixes = List.copyOf(Preconditions.checkNotNull(fixes, "fixes must not be null"));
122124
this.schemaDiff = schemaDiff;
123125
this.affectedTypes = Set.copyOf(Preconditions.checkNotNull(affectedTypes, "affectedTypes must not be null"));
124126
}
@@ -143,7 +145,7 @@ public static MigrationStep withFix(
143145
Preconditions.checkNotNull(targetVersion, "targetVersion must not be null");
144146
Preconditions.checkNotNull(fix, "fix must not be null");
145147
Preconditions.checkNotNull(affectedTypes, "affectedTypes must not be null");
146-
return new MigrationStep(sourceVersion, targetVersion, fix, null, affectedTypes);
148+
return new MigrationStep(sourceVersion, targetVersion, List.of(fix), null, affectedTypes);
147149
}
148150

149151
/**
@@ -165,7 +167,7 @@ public static MigrationStep withoutFix(
165167
Preconditions.checkNotNull(sourceVersion, "sourceVersion must not be null");
166168
Preconditions.checkNotNull(targetVersion, "targetVersion must not be null");
167169
Preconditions.checkNotNull(affectedTypes, "affectedTypes must not be null");
168-
return new MigrationStep(sourceVersion, targetVersion, null, schemaDiff, affectedTypes);
170+
return new MigrationStep(sourceVersion, targetVersion, List.of(), schemaDiff, affectedTypes);
169171
}
170172

171173
/**
@@ -212,7 +214,17 @@ public DataVersion targetVersion() {
212214
*/
213215
@NotNull
214216
public Optional<DataFix<?>> fix() {
215-
return Optional.ofNullable(this.fix);
217+
return this.fixes.isEmpty() ? Optional.empty() : Optional.of(this.fixes.get(0));
218+
}
219+
220+
/**
221+
* Returns all DataFixes applied in this step.
222+
*
223+
* @return an unmodifiable list of fixes, never {@code null}
224+
*/
225+
@NotNull
226+
public List<DataFix<?>> fixes() {
227+
return this.fixes;
216228
}
217229

218230
/**
@@ -241,7 +253,7 @@ public Set<TypeReference> affectedTypes() {
241253
* @return {@code true} if a fix is present
242254
*/
243255
public boolean hasFix() {
244-
return this.fix != null;
256+
return !this.fixes.isEmpty();
245257
}
246258

247259
/**
@@ -284,7 +296,7 @@ public boolean equals(final Object obj) {
284296
}
285297
return this.sourceVersion.equals(other.sourceVersion)
286298
&& this.targetVersion.equals(other.targetVersion)
287-
&& Objects.equals(this.fix, other.fix)
299+
&& Objects.equals(this.fixes, other.fixes)
288300
&& this.affectedTypes.equals(other.affectedTypes);
289301
}
290302

@@ -298,7 +310,7 @@ public boolean equals(final Object obj) {
298310
*/
299311
@Override
300312
public int hashCode() {
301-
return Objects.hash(this.sourceVersion, this.targetVersion, this.fix, this.affectedTypes);
313+
return Objects.hash(this.sourceVersion, this.targetVersion, this.fixes, this.affectedTypes);
302314
}
303315

304316
/**
@@ -315,8 +327,8 @@ public String toString() {
315327
final StringBuilder sb = new StringBuilder();
316328
sb.append("MigrationStep[");
317329
sb.append(this.sourceVersion.getVersion()).append(" -> ").append(this.targetVersion.getVersion());
318-
if (this.fix != null) {
319-
sb.append(", fix=").append(this.fix.getClass().getSimpleName());
330+
if (!this.fixes.isEmpty()) {
331+
sb.append(", fixes=").append(this.fixes.size());
320332
}
321333
sb.append(", affected=").append(this.affectedTypes.size()).append(" types");
322334
sb.append("]");
@@ -355,9 +367,9 @@ public static final class Builder {
355367
private final DataVersion targetVersion;
356368

357369
/**
358-
* The optional DataFix; defaults to {@code null}.
370+
* The list of DataFixes; defaults to empty.
359371
*/
360-
private DataFix<?> fix;
372+
private final List<DataFix<?>> fixes = new ArrayList<>();
361373

362374
/**
363375
* The optional schema diff; defaults to {@code null}.
@@ -392,7 +404,22 @@ private Builder(
392404
*/
393405
@NotNull
394406
public Builder fix(@Nullable final DataFix<?> fix) {
395-
this.fix = fix;
407+
if (fix != null) {
408+
this.fixes.add(fix);
409+
}
410+
return this;
411+
}
412+
413+
/**
414+
* Adds all fixes for this step.
415+
*
416+
* @param fixes the fixes to add, must not be {@code null}
417+
* @return this builder for chaining
418+
*/
419+
@NotNull
420+
public Builder fixes(@NotNull final List<DataFix<?>> fixes) {
421+
Preconditions.checkNotNull(fixes, "fixes must not be null");
422+
this.fixes.addAll(fixes);
396423
return this;
397424
}
398425

@@ -434,7 +461,7 @@ public MigrationStep build() {
434461
return new MigrationStep(
435462
this.sourceVersion,
436463
this.targetVersion,
437-
this.fix,
464+
this.fixes,
438465
this.schemaDiff,
439466
this.affectedTypes
440467
);

0 commit comments

Comments
 (0)