Skip to content

Commit f114ce9

Browse files
committed
refactor: refactor the time dependent fields in DiffNode
This pattern should be more understandable than the previous array based implementation. Furthermore, it is now much easier to add new time dependent fields.
1 parent 3a778b5 commit f114ce9

1 file changed

Lines changed: 69 additions & 53 deletions

File tree

  • src/main/java/org/variantsync/diffdetective/variation/diff

src/main/java/org/variantsync/diffdetective/variation/diff/DiffNode.java

Lines changed: 69 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
import org.variantsync.diffdetective.variation.tree.VariationNode;
1414
import org.variantsync.functjonal.Cast;
1515

16-
import java.lang.reflect.Array;
1716
import java.util.*;
1817
import java.util.function.Function;
1918
import java.util.stream.Stream;
@@ -52,31 +51,51 @@ public class DiffNode<L extends Label> implements HasNodeType {
5251
private Node featureMapping;
5352

5453
/**
55-
* The parents {@link DiffNode} before and after the edit.
56-
* This array has to be indexed by {@code Time.ordinal()}
57-
*
58-
* Invariant: Iff {@code getParent(time) != null} then
59-
* {@code getParent(time).getChildOrder(time).contains(this)}.
60-
*/
61-
private DiffNode<L>[] parents = Cast.unchecked(Array.newInstance(DiffNode.class, 2));
54+
* Bundles all the state that may be different before and after an edit.
55+
* @see at
56+
*/
57+
private static class TimeDependentState<L extends Label> {
58+
/**
59+
* The parents {@link DiffNode} before and after the edit.
60+
* This array has to be indexed by {@code Time.ordinal()}
61+
*
62+
* Invariant: Iff {@code getParent(time) != null} then
63+
* {@code getParent(time).getChildOrder(time).contains(this)}.
64+
*/
65+
public DiffNode<L> parent;
66+
67+
/**
68+
* The children before and after the edit.
69+
* This array has to be indexed by {@code Time.ordinal()}
70+
*
71+
* Invariant: Iff {@code getChildOrder(time).contains(child)} then
72+
* {@code child.getParent(time) == this}.
73+
*/
74+
public List<DiffNode<L>> children;
75+
76+
/**
77+
* Cache for before and after projections.
78+
* It stores the projection node at each time so that only one instance of {@link Projection}
79+
* per {@link Time} is ever created. This array has to be indexed by {@code Time.ordinal()}
80+
*
81+
* <p>This field is required to allow identity tests of {@link Projection}s with {@code ==}.
82+
*/
83+
public Projection<L> projection;
84+
85+
public TimeDependentState() {
86+
parent = null;
87+
children = null;
88+
children = new ArrayList<>();
89+
projection = null;
90+
}
91+
}
6292

63-
/**
64-
* The children before and after the edit.
65-
* This array has to be indexed by {@code Time.ordinal()}
66-
*
67-
* Invariant: Iff {@code getChildOrder(time).contains(child)} then
68-
* {@code child.getParent(time) == this}.
69-
*/
70-
private final List<DiffNode<L>>[] children = Cast.unchecked(Array.newInstance(List.class, 2));
93+
private final TimeDependentState<L> stateBefore = new TimeDependentState<L>();
94+
private final TimeDependentState<L> stateAfter = new TimeDependentState<L>();
7195

72-
/**
73-
* Cache for before and after projections.
74-
* It stores the projection node at each time so that only one instance of {@link Projection}
75-
* per {@link Time} is ever created. This array has to be indexed by {@code Time.ordinal()}
76-
*
77-
* <p>This field is required to allow identity tests of {@link Projection}s with {@code ==}.
78-
*/
79-
private Projection<L>[] projections = Cast.unchecked(Array.newInstance(Projection.class, 2));
96+
private TimeDependentState<L> at(Time time) {
97+
return time.match(stateBefore, stateAfter);
98+
}
8099

81100
/**
82101
* Creates a DiffNode with the given parameters.
@@ -104,9 +123,6 @@ public DiffNode(DiffType diffType, NodeType nodeType,
104123
public DiffNode(DiffType diffType,
105124
DiffLineNumber fromLines, DiffLineNumber toLines,
106125
Node featureMapping, VariationLabel<L> label) {
107-
children[BEFORE.ordinal()] = new ArrayList<>();
108-
children[AFTER.ordinal()] = new ArrayList<>();
109-
110126
this.diffType = diffType;
111127
this.label = label;
112128
this.from = fromLines;
@@ -267,7 +283,7 @@ public void drop(Time time) {
267283
* Returns -1 if the given node is not a child of this node.
268284
*/
269285
public int indexOfChild(final DiffNode<L> child, Time time) {
270-
return children[time.ordinal()].indexOf(child);
286+
return at(time).children.indexOf(child);
271287
}
272288

273289
/**
@@ -278,8 +294,8 @@ public void insertChild(final DiffNode<L> child, int index, Time time) {
278294
Assert.assertFalse(isChild(child, time), () ->
279295
"Given child " + child + " already has a " + time + " parent (" + child.getParent(time) + ")!");
280296

281-
children[time.ordinal()].add(index, child);
282-
child.parents[time.ordinal()] = this;
297+
at(time).children.add(index, child);
298+
child.at(time).parent = this;
283299
}
284300

285301
/**
@@ -291,8 +307,8 @@ public void addChild(final DiffNode<L> child, Time time) {
291307
Assert.assertFalse(isChild(child, time), () ->
292308
"Given child " + child + " already has a " + time + " parent (" + child.getParent(time) + ")!");
293309

294-
children[time.ordinal()].add(child);
295-
child.parents[time.ordinal()] = this;
310+
at(time).children.add(child);
311+
child.at(time).parent = this;
296312
}
297313

298314
/**
@@ -315,8 +331,8 @@ public void addChildren(final Collection<DiffNode<L>> children, Time time) {
315331
public void removeChild(final DiffNode<L> child, Time time) {
316332
Assert.assertTrue(isChild(child, time));
317333

318-
child.parents[time.ordinal()] = null;
319-
children[time.ordinal()].remove(child);
334+
child.at(time).parent = null;
335+
at(time).children.remove(child);
320336
}
321337

322338
/**
@@ -341,12 +357,12 @@ public void removeChildren(final Collection<DiffNode<L>> childrenToRemove) {
341357
* @return All removed children.
342358
*/
343359
public List<DiffNode<L>> removeChildren(Time time) {
344-
for (var child : children[time.ordinal()]) {
345-
child.parents[time.ordinal()] = null;
360+
for (var child : at(time).children) {
361+
child.at(time).parent = null;
346362
}
347363

348-
final List<DiffNode<L>> orphans = children[time.ordinal()];
349-
children[time.ordinal()] = new ArrayList<>();
364+
final List<DiffNode<L>> orphans = at(time).children;
365+
at(time).children = new ArrayList<>();
350366
return orphans;
351367
}
352368

@@ -387,10 +403,10 @@ public DiffNode<L> split(Time time) {
387403
getParent(time).replaceChild(this, other, time);
388404

389405
// Preserve the projection by changing its `backingNode` to `other`.
390-
if (this.projections[time.ordinal()] != null) {
391-
other.projections[time.ordinal()] = this.projections[time.ordinal()];
392-
this.projections[time.ordinal()] = null;
393-
other.projections[time.ordinal()].backingNode = other;
406+
if (this.at(time).projection != null) {
407+
other.at(time).projection = this.at(time).projection;
408+
this.at(time).projection = null;
409+
other.at(time).projection.backingNode = other;
394410
}
395411

396412
return other;
@@ -409,11 +425,11 @@ public void replaceChild(DiffNode<L> oldChild, DiffNode<L> newChild, Time time)
409425
Assert.assertNull(newChild.getParent(time));
410426
Assert.assertTrue(newChild.getDiffType().existsAtTime(time));
411427

412-
for (ListIterator<DiffNode<L>> it = children[time.ordinal()].listIterator(); it.hasNext(); ) {
428+
for (ListIterator<DiffNode<L>> it = at(time).children.listIterator(); it.hasNext(); ) {
413429
if (it.next() == oldChild) {
414430
it.set(newChild);
415-
newChild.parents[time.ordinal()] = oldChild.parents[time.ordinal()];
416-
oldChild.parents[time.ordinal()] = null;
431+
newChild.at(time).parent = oldChild.at(time).parent;
432+
oldChild.at(time).parent = null;
417433
break;
418434
}
419435
}
@@ -423,7 +439,7 @@ public void replaceChild(DiffNode<L> oldChild, DiffNode<L> newChild, Time time)
423439
* Returns the parent of this node before or after the edit.
424440
*/
425441
public DiffNode<L> getParent(Time time) {
426-
return parents[time.ordinal()];
442+
return at(time).parent;
427443
}
428444

429445
/**
@@ -503,7 +519,7 @@ public void setFormula(Node featureMapping) {
503519
* Returns the order of the children at {@code time}.
504520
*/
505521
public List<DiffNode<L>> getChildOrder(Time time) {
506-
return Collections.unmodifiableList(children[time.ordinal()]);
522+
return Collections.unmodifiableList(at(time).children);
507523
}
508524

509525
/**
@@ -513,8 +529,8 @@ public List<DiffNode<L>> getChildOrder(Time time) {
513529
*/
514530
public Stream<DiffNode<L>> getAllChildrenStream() {
515531
return Stream.concat(
516-
children[BEFORE.ordinal()].stream(),
517-
children[AFTER.ordinal()].stream().filter(child -> child.getParent(BEFORE) != this)
532+
at(BEFORE).children.stream(),
533+
at(AFTER).children.stream().filter(child -> child.getParent(BEFORE) != this)
518534
);
519535
};
520536

@@ -578,7 +594,7 @@ public boolean isChild(DiffNode<L> child, Time time) {
578594
* Returns true iff this node has no children.
579595
*/
580596
public boolean isLeaf() {
581-
return children[BEFORE.ordinal()].isEmpty() && children[AFTER.ordinal()].isEmpty();
597+
return at(BEFORE).children.isEmpty() && at(AFTER).children.isEmpty();
582598
}
583599

584600
/**
@@ -750,11 +766,11 @@ public void assertConsistency() {
750766
public Projection<L> projection(Time time) {
751767
Assert.assertTrue(getDiffType().existsAtTime(time));
752768

753-
if (projections[time.ordinal()] == null) {
754-
projections[time.ordinal()] = new Projection<>(this, time);
769+
if (at(time).projection == null) {
770+
at(time).projection = new Projection<>(this, time);
755771
}
756772

757-
return projections[time.ordinal()];
773+
return at(time).projection;
758774
}
759775

760776
/**

0 commit comments

Comments
 (0)