From df8bc7d428a5f07ee48d5fc5495b2757556a7a61 Mon Sep 17 00:00:00 2001 From: Benjamin Moosherr Date: Thu, 30 Oct 2025 11:57:50 +0100 Subject: [PATCH 1/4] refactor: reuse `VariationNode.assertConsistency` in `DiffNode` --- .../variation/diff/DiffNode.java | 19 +++---------------- .../variation/diff/Projection.java | 5 +++++ 2 files changed, 8 insertions(+), 16 deletions(-) diff --git a/src/main/java/org/variantsync/diffdetective/variation/diff/DiffNode.java b/src/main/java/org/variantsync/diffdetective/variation/diff/DiffNode.java index f0f05f13c..5e27f75d6 100644 --- a/src/main/java/org/variantsync/diffdetective/variation/diff/DiffNode.java +++ b/src/main/java/org/variantsync/diffdetective/variation/diff/DiffNode.java @@ -737,6 +737,9 @@ public static DiffNode fromID(int id, String label) { * @throws AssertionError when an inconsistency is detected. */ public void assertConsistency() { + // check that the projections are valid (i.e., node type specific consistency checks) + diffType.forAllTimesOfExistence(time -> this.projection(time).assertConsistency()); + // check consistency of children lists and edges for (final DiffNode c : getAllChildren()) { Assert.assertTrue(isChild(c), () -> "Child " + c + " of " + this + " is neither a before nor an after child!"); @@ -770,22 +773,6 @@ public void assertConsistency() { Assert.assertTrue(pb.isNon()); } } - - // Else and Elif nodes have an If or Elif as parent. - if (this.isElse() || this.isElif()) { - Time.forAll(time -> { - if (getParent(time) != null) { - Assert.assertTrue(getParent(time).isIf() || getParent(time).isElif(), time + " parent " + getParent(time) + " of " + this + " is neither IF nor ELIF!"); - } - }); - } - - // Only if and elif nodes have a formula - if (this.isIf() || this.isElif()) { - Assert.assertTrue(this.getFormula() != null, "If or elif without feature mapping!"); - } else { - Assert.assertTrue(this.getFormula() == null, "Node with type " + getNodeType() + " has a non null feature mapping"); - } } /** diff --git a/src/main/java/org/variantsync/diffdetective/variation/diff/Projection.java b/src/main/java/org/variantsync/diffdetective/variation/diff/Projection.java index 91352292d..760de0d4c 100644 --- a/src/main/java/org/variantsync/diffdetective/variation/diff/Projection.java +++ b/src/main/java/org/variantsync/diffdetective/variation/diff/Projection.java @@ -130,4 +130,9 @@ public Node getFormula() { public int getID() { return getBackingNode().getID(); } + + @Override + public String toString() { + return String.format("Projection(%s, %s)", time, getBackingNode()); + } }; From da903780bc6d4930f7f387a1abfb67e575e2016a Mon Sep 17 00:00:00 2001 From: Benjamin Moosherr Date: Thu, 30 Oct 2025 11:58:27 +0100 Subject: [PATCH 2/4] feat: assert that there is at most one ELIF/ELSE --- .../diffdetective/variation/tree/VariationNode.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/org/variantsync/diffdetective/variation/tree/VariationNode.java b/src/main/java/org/variantsync/diffdetective/variation/tree/VariationNode.java index c3e8d1f39..21f80f049 100644 --- a/src/main/java/org/variantsync/diffdetective/variation/tree/VariationNode.java +++ b/src/main/java/org/variantsync/diffdetective/variation/tree/VariationNode.java @@ -537,6 +537,12 @@ public void assertConsistency() { "The root has to have the feature mapping 'true'"); } + // check that there is at most one ELIF/ELSE + Assert.assertTrue( + getChildren().stream().filter(c -> c.isElif() || c.isElse()).count() <= 1, + "There is more than one ELIF/ELSE node." + ); + // check consistency of children lists and edges for (var child : getChildren()) { Assert.assertTrue( From 282258d1799936b9783fe62913c2a0c0af6eb495 Mon Sep 17 00:00:00 2001 From: Benjamin Moosherr Date: Thu, 30 Oct 2025 11:59:09 +0100 Subject: [PATCH 3/4] feat: assert that the root `DiffNode` is unchanged --- .../variantsync/diffdetective/variation/diff/DiffNode.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/org/variantsync/diffdetective/variation/diff/DiffNode.java b/src/main/java/org/variantsync/diffdetective/variation/diff/DiffNode.java index 5e27f75d6..20e7d60cf 100644 --- a/src/main/java/org/variantsync/diffdetective/variation/diff/DiffNode.java +++ b/src/main/java/org/variantsync/diffdetective/variation/diff/DiffNode.java @@ -763,6 +763,10 @@ public void assertConsistency() { if (pb != null && pa == null) { Assert.assertTrue(isRem()); } + // the root was not edited + if (pb == null && pa == null) { + Assert.assertTrue(isNon()); + } // a node with exactly two parents was not edited if (pb != null && pa != null) { Assert.assertTrue(isNon()); From 9f43cc9bab13da7f8329ce5e5e10671d5524f090 Mon Sep 17 00:00:00 2001 From: Benjamin Moosherr Date: Thu, 30 Oct 2025 12:05:22 +0100 Subject: [PATCH 4/4] feat: `VariationTree.assertConsistency` --- .../variation/diff/DiffNode.java | 1 + .../variation/tree/VariationNode.java | 42 ++++++++++--------- .../variation/tree/VariationTree.java | 12 ++++++ 3 files changed, 35 insertions(+), 20 deletions(-) diff --git a/src/main/java/org/variantsync/diffdetective/variation/diff/DiffNode.java b/src/main/java/org/variantsync/diffdetective/variation/diff/DiffNode.java index 20e7d60cf..8ce5bfb8e 100644 --- a/src/main/java/org/variantsync/diffdetective/variation/diff/DiffNode.java +++ b/src/main/java/org/variantsync/diffdetective/variation/diff/DiffNode.java @@ -733,6 +733,7 @@ public static DiffNode fromID(int id, String label) { * Checks that the VariationDiff is in a valid state. * In particular, this method checks that all edges are well-formed (e.g., edges can be inconsistent because edges are double-linked). * This method also checks that a node with exactly one parent was edited, and that a node with exactly two parents was not edited. + * To check all children recursively, use {@link VariationDiff#assertConsistency}. * @see Assert#assertTrue * @throws AssertionError when an inconsistency is detected. */ diff --git a/src/main/java/org/variantsync/diffdetective/variation/tree/VariationNode.java b/src/main/java/org/variantsync/diffdetective/variation/tree/VariationNode.java index 21f80f049..cbe8f6b4b 100644 --- a/src/main/java/org/variantsync/diffdetective/variation/tree/VariationNode.java +++ b/src/main/java/org/variantsync/diffdetective/variation/tree/VariationNode.java @@ -487,26 +487,28 @@ public VariationTreeNode toVariationTree(final Map - *
  • if-chains are nested correctly, - *
  • the root is an {@link NodeType#IF} with the feature mapping {@code "true"}, - *
  • the feature mapping is {@code null} iff {@code isConditionalAnnotation} is {@code false} - * and - *
  • all edges are well-formed (e.g., edges can be inconsistent because edges are - * double-linked). - * - * - *

    Some invariants are not checked. These include - *

      - *
    • There should be no cycles and - *
    • {@link getID} should be unique in the whole variation tree. - *
    - * - * @see Assert#assertTrue - * @throws AssertionError when an inconsistency is detected. - */ + * Checks that this node satisfies some easy to check invariants. + * In particular, this method checks that + *
      + *
    • if-chains are nested correctly, + *
    • the root is an {@link NodeType#IF} with the feature mapping {@code "true"}, + *
    • the feature mapping is {@code null} iff {@code isConditionalAnnotation} is {@code false} + * and + *
    • all edges are well-formed (e.g., edges can be inconsistent because edges are + * double-linked). + *
    + * + *

    Some invariants are not checked. These include + *

      + *
    • There should be no cycles, + *
    • {@link getID} should be unique in the whole variation tree, and + *
    • children are not checked recursively. + *
    + * Use {@link VariationTree#assertConsistency} to check all children recursively. + * + * @see Assert#assertTrue + * @throws AssertionError when an inconsistency is detected. + */ public void assertConsistency() { // ELSE and ELIF nodes have an IF or ELIF as parent. if (isElse() || isElif()) { diff --git a/src/main/java/org/variantsync/diffdetective/variation/tree/VariationTree.java b/src/main/java/org/variantsync/diffdetective/variation/tree/VariationTree.java index 722744ed7..9920b3e6a 100644 --- a/src/main/java/org/variantsync/diffdetective/variation/tree/VariationTree.java +++ b/src/main/java/org/variantsync/diffdetective/variation/tree/VariationTree.java @@ -5,6 +5,7 @@ import org.variantsync.diffdetective.util.Assert; import org.variantsync.diffdetective.variation.DiffLinesLabel; import org.variantsync.diffdetective.variation.Label; +import org.variantsync.diffdetective.variation.NodeType; // For Javadoc import org.variantsync.diffdetective.variation.diff.DiffNode; import org.variantsync.diffdetective.variation.diff.VariationDiff; import org.variantsync.diffdetective.variation.diff.Projection; @@ -213,6 +214,17 @@ public String unparse() { return result.toString(); } + /** + * Checks whether this {@link VariationTree} is consistent. + * Throws an error if this {@link VariationTree} is inconsistent (e.g., if there are multiple + * {@link NodeType#ELSE} nodes). Has no side-effects otherwise. + * + * @see VariationNode#assertConsistency + */ + public void assertConsistency() { + forAllPreorder(VariationTreeNode::assertConsistency); + } + @Override public String toString() { return "variation tree from " + source;