Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.BigIntegerNode;
import com.fasterxml.jackson.databind.node.BooleanNode;
import com.fasterxml.jackson.databind.node.DecimalNode;
import com.fasterxml.jackson.databind.node.DoubleNode;
import com.fasterxml.jackson.databind.node.FloatNode;
import com.fasterxml.jackson.databind.node.IntNode;
Expand All @@ -43,6 +45,8 @@
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Iterator;
import java.util.Map;
import java.util.Spliterator;
Expand Down Expand Up @@ -689,6 +693,12 @@ public JsonNode createNumeric(@NotNull final Number value) {
if (value instanceof Byte) {
return ShortNode.valueOf(value.byteValue());
}
if (value instanceof BigDecimal bd) {
return DecimalNode.valueOf(bd);
}
if (value instanceof BigInteger bi) {
return BigIntegerNode.valueOf(bi);
}
return DoubleNode.valueOf(value.doubleValue());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -935,6 +935,12 @@ public DataResult<JsonNode> mergeToList(@NotNull final JsonNode list,
return DataResult.error("Not an array: " + list);
}
final ArrayNode result = list.isNull() ? this.nodeFactory.arrayNode() : ((ArrayNode) list).deepCopy();
// TOML requires homogeneous arrays — validate element type consistency
if (!result.isEmpty() && result.get(0).getNodeType() != value.getNodeType()) {
return DataResult.error(
"TOML requires homogeneous arrays: expected "
+ result.get(0).getNodeType() + " but got " + value.getNodeType());
}
result.add(value);
return DataResult.success(result);
}
Expand Down Expand Up @@ -1089,6 +1095,10 @@ public DataResult<JsonNode> mergeToMap(@NotNull final JsonNode map,
if (!key.isTextual()) {
return DataResult.error("Key is not a string: " + key);
}
// TOML does not support null values
if (value.isNull()) {
return DataResult.error("TOML does not support null values for key: " + key.asText());
}
final ObjectNode result = map.isNull() ? this.nodeFactory.objectNode() : ((ObjectNode) map).deepCopy();
result.set(key.asText(), value);
return DataResult.success(result);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -828,7 +828,7 @@ public Object createMap(@NotNull final Stream<Pair<Object, Object>> entries) {
return; // Skip entries with null keys
}
final String key = keyObj.toString();
map.put(key, valueObj);
map.put(key, valueObj == null ? YamlNull.INSTANCE : valueObj);
});
return map;
}
Expand Down Expand Up @@ -1254,9 +1254,21 @@ public <U> Object convertTo(@NotNull final DynamicOps<U> sourceOps,
* @return a deep copy of the value, or the value itself if it is immutable;
* {@code null} if the input is {@code null}
*/
private static final int MAX_DEEP_COPY_DEPTH = 512;

@Nullable
@SuppressWarnings("unchecked")
private Object deepCopy(@Nullable final Object value) {
return deepCopy(value, 0);
}

@Nullable
@SuppressWarnings("unchecked")
private Object deepCopy(@Nullable final Object value, final int depth) {
if (depth > MAX_DEEP_COPY_DEPTH) {
throw new IllegalStateException(
"YAML structure too deeply nested (depth > " + MAX_DEEP_COPY_DEPTH + ")");
}
if (value == null) {
return null;
}
Expand All @@ -1267,15 +1279,15 @@ private Object deepCopy(@Nullable final Object value) {
final Map<String, Object> original = (Map<String, Object>) value;
final Map<String, Object> copy = new LinkedHashMap<>();
for (final Map.Entry<String, Object> entry : original.entrySet()) {
copy.put(entry.getKey(), deepCopy(entry.getValue()));
copy.put(entry.getKey(), deepCopy(entry.getValue(), depth + 1));
}
return copy;
}
if (value instanceof List) {
final List<Object> original = (List<Object>) value;
final List<Object> copy = new ArrayList<>();
for (final Object element : original) {
copy.add(deepCopy(element));
copy.add(deepCopy(element, depth + 1));
}
return copy;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -959,7 +959,7 @@ void createMapHandlesNullValues() {

@SuppressWarnings("unchecked")
final Map<String, Object> resultMap = (Map<String, Object>) map;
assertThat(resultMap.get("key")).isNull();
assertThat(resultMap.get("key")).isSameAs(SnakeYamlOps.NULL);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ private MigrationReportImpl(final BuilderImpl builder) {
this.fromVersion = builder.fromVersion;
this.toVersion = builder.toVersion;
this.startTime = builder.startTime;
this.endTime = Instant.now();
this.endTime = builder.endTime;
this.fixExecutions = List.copyOf(builder.fixExecutions);
this.touchedTypes = Set.copyOf(builder.touchedTypes);
this.warnings = List.copyOf(builder.warnings);
Expand Down Expand Up @@ -164,6 +164,7 @@ public static final class BuilderImpl implements MigrationReport.Builder {
private DataVersion fromVersion;
private DataVersion toVersion;
private Instant startTime;
private Instant endTime;
private final List<FixExecution> fixExecutions = new ArrayList<>();
private final Set<TypeReference> touchedTypes = new LinkedHashSet<>();
private final List<String> warnings = new ArrayList<>();
Expand Down Expand Up @@ -259,14 +260,22 @@ public Builder endFix(
this.fixExecutions.add(execution);

// Reset current fix tracking
this.resetFixState();

return this;
}

/**
* Resets fix-tracking state. Called after endFix() or on error paths
* to prevent stale state from corrupting subsequent fix records.
*/
private void resetFixState() {
this.currentFixName = null;
this.currentFixFromVersion = null;
this.currentFixToVersion = null;
this.currentFixStartTime = null;
this.currentRuleApplications.clear();
this.currentFixBeforeSnapshot = null;

return this;
}

@Override
Expand Down Expand Up @@ -301,6 +310,7 @@ public MigrationReport build() {
);
}

this.endTime = Instant.now();
return new MigrationReportImpl(this);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ public boolean hasFixesInRange(
*
* <p>This method is idempotent - calling it multiple times has no effect after the first call.</p>
*/
public void freeze() {
public synchronized void freeze() {
if (!this.frozen) {
// Create an immutable deep copy
final Map<TypeReference, NavigableMap<DataVersion, List<DataFix<?>>>> immutableCopy = new HashMap<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -187,8 +187,16 @@ public <T> Dynamic<T> update(
diagCtx.reportBuilder().endFix(fix, duration, afterSnapshot);
}
} catch (final FixException e) {
if (diagCtx != null) {
final Duration duration = Duration.between(fixStart, Instant.now());
diagCtx.reportBuilder().endFix(fix, duration, null);
}
throw e; // Re-throw FixException as-is
} catch (final Exception e) {
if (diagCtx != null) {
final Duration duration = Duration.between(fixStart, Instant.now());
diagCtx.reportBuilder().endFix(fix, duration, null);
}
throw new FixException(
"Fix '" + fix.name() + "' failed: " + e.getMessage(),
fix.name(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import de.splatgames.aether.datafixers.api.TypeReference;
import de.splatgames.aether.datafixers.api.diagnostic.DiagnosticContext;
import de.splatgames.aether.datafixers.api.dynamic.Dynamic;
import de.splatgames.aether.datafixers.api.exception.FixException;
import de.splatgames.aether.datafixers.api.fix.DataFix;
import de.splatgames.aether.datafixers.api.fix.DataFixerContext;
import de.splatgames.aether.datafixers.api.rewrite.TypeRewriteRule;
Expand Down Expand Up @@ -170,6 +171,13 @@ protected SchemaDataFix(
final Typed<?> typedIn = new Typed<>((Type<Object>) logical, input);
final Typed<?> typedOut = rule.apply(typedIn);

if (!(typedOut.value() instanceof Dynamic)) {
throw new FixException(
"Rule produced non-Dynamic output for type '" + type.getId()
+ "' in fix '" + this.name() + "': " + typedOut.value().getClass().getName()
);
}

@SuppressWarnings("unchecked")
final Dynamic<Object> result = (Dynamic<Object>) typedOut.value();

Expand Down
Loading