55import org .variantsync .diffdetective .diff .DiffLineNumber ;
66import org .variantsync .diffdetective .diff .Lines ;
77import org .variantsync .diffdetective .util .Assert ;
8+ import org .variantsync .diffdetective .util .StringUtils ;
89import org .variantsync .diffdetective .util .fide .FixTrueFalse ;
910
10- import java .util .ArrayList ;
11- import java .util .Collection ;
12- import java .util .List ;
13- import java .util .Objects ;
11+ import java .util .*;
1412import java .util .function .Function ;
1513
1614import static org .variantsync .diffdetective .util .fide .FormulaUtils .negate ;
@@ -46,12 +44,14 @@ public class DiffNode {
4644 /**
4745 * We use a list for children to maintain order.
4846 */
49- private final List <DiffNode > allChildren ;
47+ private final List <DiffNode > childOrder ;
48+
49+ // These lists store the outgoing edges. These lists do not respect order.
5050 private List <DiffNode > beforeChildren ;
5151 private List <DiffNode > afterChildren ;
5252
5353 private DiffNode () {
54- this .allChildren = new ArrayList <>();
54+ this .childOrder = new ArrayList <>();
5555 this .beforeChildren = new ArrayList <>();
5656 this .afterChildren = new ArrayList <>();
5757 }
@@ -196,7 +196,7 @@ public boolean beforePathEqualsAfterPath() {
196196 * @return The number of unique child nodes.
197197 */
198198 public int getTotalNumberOfChildren () {
199- return allChildren .size ();
199+ return childOrder .size ();
200200 }
201201
202202 /**
@@ -292,10 +292,51 @@ public void drop() {
292292 }
293293 }
294294
295+ private void dropBeforeChild (final DiffNode child ) {
296+ assert child .beforeParent == this ;
297+ child .beforeParent = null ;
298+ }
299+
300+ private void dropAfterChild (final DiffNode child ) {
301+ assert child .afterParent == this ;
302+ child .afterParent = null ;
303+ }
304+
305+ public int indexOfChild (final DiffNode child ) {
306+ return childOrder .indexOf (child );
307+ }
308+
309+ public boolean insertChildAt (final DiffNode child , int index , Time time ) {
310+ return switch (time ) {
311+ case BEFORE -> insertBeforeChild (child , index );
312+ case AFTER -> insertAfterChild (child , index );
313+ };
314+ }
315+
316+ public boolean insertBeforeChild (final DiffNode child , int index ) {
317+ if (!child .isAdd ()) {
318+ addWithoutDuplicates (beforeChildren , child );
319+ insertWithoutDuplicates (childOrder , child , index );
320+ child .setBeforeParent (this );
321+ return true ;
322+ }
323+ return false ;
324+ }
325+
326+ public boolean insertAfterChild (final DiffNode child , int index ) {
327+ if (!child .isRem ()) {
328+ addWithoutDuplicates (afterChildren , child );
329+ insertWithoutDuplicates (childOrder , child , index );
330+ child .setAfterParent (this );
331+ return true ;
332+ }
333+ return false ;
334+ }
335+
295336 public boolean addBeforeChild (final DiffNode child ) {
296337 if (!child .isAdd ()) {
297- addToList (beforeChildren , child );
298- addToList ( allChildren , child );
338+ addWithoutDuplicates (beforeChildren , child );
339+ addWithoutDuplicates ( childOrder , child );
299340 child .setBeforeParent (this );
300341 return true ;
301342 }
@@ -304,8 +345,8 @@ public boolean addBeforeChild(final DiffNode child) {
304345
305346 public boolean addAfterChild (final DiffNode child ) {
306347 if (!child .isRem ()) {
307- addToList (afterChildren , child );
308- addToList ( allChildren , child );
348+ addWithoutDuplicates (afterChildren , child );
349+ addWithoutDuplicates ( childOrder , child );
309350 child .setAfterParent (this );
310351 return true ;
311352 }
@@ -324,15 +365,21 @@ public void addAfterChildren(final Collection<DiffNode> afterChildren) {
324365 }
325366 }
326367
327- private static void addToList (final List <DiffNode > childrenlist , final DiffNode child ) {
368+ private static void addWithoutDuplicates (final List <DiffNode > childrenlist , final DiffNode child ) {
328369 if (!childrenlist .contains (child )) {
329370 childrenlist .add (child );
330371 }
331372 }
332373
374+ private static void insertWithoutDuplicates (final List <DiffNode > childrenlist , final DiffNode child , int index ) {
375+ if (!childrenlist .contains (child )) {
376+ childrenlist .add (index , child );
377+ }
378+ }
379+
333380 public boolean removeBeforeChild (final DiffNode child ) {
334381 if (this .beforeChildren .remove (child )) {
335- removeFromCache (this .afterChildren , this .allChildren , child );
382+ removeFromCache (this .afterChildren , this .childOrder , child );
336383 dropBeforeChild (child );
337384 return true ;
338385 }
@@ -341,7 +388,7 @@ public boolean removeBeforeChild(final DiffNode child) {
341388
342389 public boolean removeAfterChild (final DiffNode child ) {
343390 if (this .afterChildren .remove (child )) {
344- removeFromCache (this .beforeChildren , this .allChildren , child );
391+ removeFromCache (this .beforeChildren , this .childOrder , child );
345392 dropAfterChild (child );
346393 return true ;
347394 }
@@ -357,7 +404,7 @@ public void removeChildren(final Collection<DiffNode> childrenToRemove) {
357404
358405 public List <DiffNode > removeBeforeChildren () {
359406 for (final DiffNode child : beforeChildren ) {
360- removeFromCache (this .afterChildren , this .allChildren , child );
407+ removeFromCache (this .afterChildren , this .childOrder , child );
361408 dropBeforeChild (child );
362409 }
363410
@@ -368,7 +415,7 @@ public List<DiffNode> removeBeforeChildren() {
368415
369416 public List <DiffNode > removeAfterChildren () {
370417 for (final DiffNode child : afterChildren ) {
371- removeFromCache (this .beforeChildren , this .allChildren , child );
418+ removeFromCache (this .beforeChildren , this .childOrder , child );
372419 dropAfterChild (child );
373420 }
374421
@@ -377,16 +424,6 @@ public List<DiffNode> removeAfterChildren() {
377424 return orphans ;
378425 }
379426
380- private void dropBeforeChild (final DiffNode child ) {
381- assert child .beforeParent == this ;
382- child .beforeParent = null ;
383- }
384-
385- private void dropAfterChild (final DiffNode child ) {
386- assert child .afterParent == this ;
387- child .afterParent = null ;
388- }
389-
390427 private static void removeFromCache (
391428 final List <DiffNode > childrenTypeB ,
392429 final List <DiffNode > allChildren ,
@@ -440,8 +477,24 @@ public Node getDirectFeatureMapping() {
440477 return featureMapping ;
441478 }
442479
443- public Collection <DiffNode > getAllChildren () {
444- return allChildren ;
480+ public List <DiffNode > getChildOrder () {
481+ return Collections .unmodifiableList (childOrder );
482+ }
483+
484+ public List <DiffNode > getAllChildren () {
485+ return getChildOrder ();
486+ }
487+
488+ public List <DiffNode > getBeforeChildren () {
489+ return Collections .unmodifiableList (beforeChildren );
490+ }
491+
492+ public List <DiffNode > getAfterChildren () {
493+ return Collections .unmodifiableList (afterChildren );
494+ }
495+
496+ public List <DiffNode > getChildren (Time time ) {
497+ return time .match (getBeforeChildren (), getAfterChildren ());
445498 }
446499
447500 public void setIsMultilineMacro (boolean isMultilineMacro ) {
@@ -506,6 +559,13 @@ public Node getAfterFeatureMapping() {
506559 return getFeatureMapping (DiffNode ::getAfterParent );
507560 }
508561
562+ public Node getFeatureMapping (Time time ) {
563+ return time .match (
564+ this ::getBeforeFeatureMapping ,
565+ this ::getAfterFeatureMapping
566+ );
567+ }
568+
509569 private List <Node > getPresenceCondition (Function <DiffNode , DiffNode > parentOf ) {
510570 final DiffNode parent = parentOf .apply (this );
511571
@@ -556,6 +616,17 @@ public Node getAfterPresenceCondition() {
556616 }
557617 }
558618
619+ public Node getPresenceCondition (Time time ) {
620+ return time .match (
621+ this ::getBeforePresenceCondition ,
622+ this ::getAfterPresenceCondition
623+ );
624+ }
625+
626+ public boolean isLeaf () {
627+ return childOrder .isEmpty ();
628+ }
629+
559630 public boolean isRem () {
560631 return this .diffType .equals (DiffType .REM );
561632 }
@@ -631,13 +702,13 @@ public void assertConsistency() {
631702 // check consistency of children lists and edges
632703 for (final DiffNode bc : beforeChildren ) {
633704 Assert .assertTrue (bc .beforeParent == this , () -> "Before child " + bc + " of " + this + " has another parent " + bc .beforeParent + "!" );
634- Assert .assertTrue (allChildren .contains (bc ), () -> "Before child " + bc + " of " + this + " is not in the list of all children!" );
705+ Assert .assertTrue (childOrder .contains (bc ), () -> "Before child " + bc + " of " + this + " is not in the list of all children!" );
635706 }
636707 for (final DiffNode ac : afterChildren ) {
637708 Assert .assertTrue (ac .afterParent == this , () -> "After child " + ac + " of " + this + " has another parent " + ac .afterParent + "!" );
638- Assert .assertTrue (allChildren .contains (ac ), () -> "After child " + ac + " of " + this + " is not in the list of all children!" );
709+ Assert .assertTrue (childOrder .contains (ac ), () -> "After child " + ac + " of " + this + " is not in the list of all children!" );
639710 }
640- for (final DiffNode c : allChildren ) {
711+ for (final DiffNode c : childOrder ) {
641712 Assert .assertTrue (beforeChildren .contains (c ) || afterChildren .contains (c ), () -> "Child " + c + " of " + this + " is neither a before not an after child!" );
642713 }
643714
@@ -666,6 +737,36 @@ public void assertSemanticConsistency() {
666737 }
667738 }
668739
740+ public static String toTextDiffLine (final DiffType diffType , final String text ) {
741+ return diffType .symbol + text .replaceAll (StringUtils .LINEBREAK_REGEX , StringUtils .LINEBREAK + diffType .symbol );
742+ }
743+
744+ public String toTextDiffLine () {
745+ return toTextDiffLine (diffType , this .getLabel ());
746+ }
747+
748+ public String toTextDiff () {
749+ final StringBuilder diff = new StringBuilder ();
750+
751+ if (!this .isRoot ()) {
752+ diff
753+ .append (this .toTextDiffLine ())
754+ .append (StringUtils .LINEBREAK );
755+ }
756+
757+ for (final DiffNode child : childOrder ) {
758+ diff .append (child .toTextDiff ());
759+ }
760+
761+ if (isMacro ()) {
762+ diff
763+ .append (toTextDiffLine (this .diffType , CodeType .ENDIF .asMacroText ()))
764+ .append (StringUtils .LINEBREAK );
765+ }
766+
767+ return diff .toString ();
768+ }
769+
669770 @ Override
670771 public String toString () {
671772 String s ;
0 commit comments