diff --git a/java/com.googlecode.java-diff-utils/src/difflib/Chunk.java b/java/com.googlecode.java-diff-utils/src/difflib/Chunk.java index dd06fa1f6b6..b87167f861f 100755 --- a/java/com.googlecode.java-diff-utils/src/difflib/Chunk.java +++ b/java/com.googlecode.java-diff-utils/src/difflib/Chunk.java @@ -34,7 +34,7 @@ import java.util.List; public class Chunk { private final int position; - private List lines; + private final List lines; /** * Creates a chunk and saves a copy of affected lines @@ -88,10 +88,6 @@ public class Chunk { return position; } - public void setLines(List lines) { - this.lines = lines; - } - /** * @return the affected lines */ diff --git a/java/com.googlecode.java-diff-utils/src/difflib/myers/MyersDiff.java b/java/com.googlecode.java-diff-utils/src/difflib/myers/MyersDiff.java index 1e6cb13ea62..6216d476553 100755 --- a/java/com.googlecode.java-diff-utils/src/difflib/myers/MyersDiff.java +++ b/java/com.googlecode.java-diff-utils/src/difflib/myers/MyersDiff.java @@ -123,7 +123,7 @@ public class MyersDiff implements DiffAlgorithm { } /** - * Computes the minimum diffpath that expresses de differences + * Computes the minimum diffpath that expresses the differences * between the original and revised sequences, according * to Gene Myers differencing algorithm. * @@ -155,7 +155,6 @@ public class MyersDiff implements DiffAlgorithm { final int kplus = kmiddle + 1; final int kminus = kmiddle - 1; PathNode prev = null; - int i; if ((k == -d) || (k != d && diagonal[kminus].i < diagonal[kplus].i)) { i = diagonal[kplus].i; @@ -164,13 +163,9 @@ public class MyersDiff implements DiffAlgorithm { i = diagonal[kminus].i + 1; prev = diagonal[kminus]; } - diagonal[kminus] = null; // no longer used - int j = i - k; - PathNode node = new DiffNode(i, j, prev); - // orig and rev are zero-based // but the algorithm is one-based // that's why there's no +1 when indexing the sequences @@ -178,17 +173,15 @@ public class MyersDiff implements DiffAlgorithm { i++; j++; } - if (i > node.i) + if (i > node.i) { node = new Snake(i, j, node); - + } diagonal[kmiddle] = node; - if (i >= N && j >= M) { return diagonal[kmiddle]; } } diagonal[middle + d - 1] = null; - } // According to Myers, this cannot happen throw new DifferentiationFailedException("could not find a diff path"); diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/NauticSide.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/NauticSide.java deleted file mode 100644 index de2b1899eb3..00000000000 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/NauticSide.java +++ /dev/null @@ -1,3 +0,0 @@ -package com.sap.sailing.domain.common; - -public enum NauticSide { PORT, STARBOARD }; diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/NauticalSide.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/NauticalSide.java new file mode 100644 index 00000000000..33bbe388f08 --- /dev/null +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/NauticalSide.java @@ -0,0 +1,3 @@ +package com.sap.sailing.domain.common; + +public enum NauticalSide { PORT, STARBOARD }; diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/Waypoint.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/Waypoint.java index 24127965d89..e85cdd668ac 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/Waypoint.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/Waypoint.java @@ -1,7 +1,7 @@ package com.sap.sailing.domain.base; import com.sap.sailing.domain.common.Named; -import com.sap.sailing.domain.common.NauticSide; +import com.sap.sailing.domain.common.NauticalSide; /** * Waypoints constitute {@link Course}s and demarcate their {@link Leg}s. A waypoint's position is defined by a @@ -20,5 +20,5 @@ public interface Waypoint extends Named, WithID, IsManagedByDomainFactory { * Return the passing side for the waypoint. Null is allowed e.g. when the waypoint is a gate. * @return */ - NauticSide getPassingSide(); + NauticalSide getPassingSide(); } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/CourseImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/CourseImpl.java index de623ed67e8..e55419c3613 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/CourseImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/CourseImpl.java @@ -115,6 +115,7 @@ public class CourseImpl extends NamedImpl implements Course { public void addWaypoint(int zeroBasedPosition, Waypoint waypointToAdd) { LockUtil.lockForWrite(lock); try { + assert !waypoints.contains(waypointToAdd); // no duplicate waypoints allowed logger.info("Adding waypoint " + waypointToAdd + " to course '" + getName() + "'"); waypoints.add(zeroBasedPosition, waypointToAdd); Map updatesToWaypointIndexes = new HashMap(); @@ -412,7 +413,6 @@ public class CourseImpl extends NamedImpl implements Course { if (!patch.isEmpty()) { logger.info("applying course update " + patch + " to course " + this); CourseAsWaypointList courseAsWaypointList = new CourseAsWaypointList(this); - // FIXME issue: if the patch contains "forward moves" it may first insert the additional duplicates at the end, then remove them at the beginning, leading to duplicate waypoints in the course which is not supported patch.applyToInPlace(courseAsWaypointList); } } finally { diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/WaypointImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/WaypointImpl.java index 8e3b797120d..fe1257f8055 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/WaypointImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/WaypointImpl.java @@ -4,20 +4,20 @@ import com.sap.sailing.domain.base.Mark; import com.sap.sailing.domain.base.ControlPoint; import com.sap.sailing.domain.base.DomainFactory; import com.sap.sailing.domain.base.Waypoint; -import com.sap.sailing.domain.common.NauticSide; +import com.sap.sailing.domain.common.NauticalSide; public class WaypointImpl implements Waypoint { private static final long serialVersionUID = 1600863368078653897L; private final ControlPoint controlPoint; private static int idCounter = 1; private final int id; - private final NauticSide passingSide; + private final NauticalSide passingSide; public WaypointImpl(ControlPoint controlPoint) { this(controlPoint, null); } - public WaypointImpl(ControlPoint controlPoint, NauticSide passingSide) { + public WaypointImpl(ControlPoint controlPoint, NauticalSide passingSide) { this.controlPoint = controlPoint; this.passingSide = passingSide; id = idCounter++; @@ -54,7 +54,7 @@ public class WaypointImpl implements Waypoint { } @Override - public NauticSide getPassingSide() { + public NauticalSide getPassingSide() { return passingSide; } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/util/CourseAsWaypointList.java b/java/com.sap.sailing.domain/src/com/sap/sailing/util/CourseAsWaypointList.java index b3c31b3308c..1e44d908404 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/util/CourseAsWaypointList.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/util/CourseAsWaypointList.java @@ -1,9 +1,14 @@ package com.sap.sailing.util; import java.util.AbstractList; +import java.util.HashMap; +import java.util.Map; +import com.sap.sailing.domain.base.ControlPoint; import com.sap.sailing.domain.base.Course; import com.sap.sailing.domain.base.Waypoint; +import com.sap.sailing.domain.base.impl.MarkImpl; +import com.sap.sailing.domain.base.impl.WaypointImpl; import com.sap.sailing.domain.common.impl.Util; import difflib.Patch; @@ -12,6 +17,13 @@ import difflib.Patch; * Wraps a {@link Course} as a {@link List} based on the course's {@link Course#addWaypoint(int, Waypoint)} * and {@link Course#removeWaypoint(int)} methods. This is convenient in case a {@link Patch} is to be * {@link Patch#applyTo(java.util.List) applied} to a {@link Course}. + *

+ * + * Executions of all {@link #add(int, Waypoint)} operations that would lead to duplicate waypoint occurrences are + * replaced by an insertion or a proxy waypoint. The insertion of the waypoint passed to {@link #add(int, Waypoint)} is + * delayed until its occurrence is removed. This is necessary because moving of waypoints may be represented as a + * sequence of insert/remove (instead of remove/insert) which would lead to duplicate {@link Waypoint} occurrences in + * the course which is not permitted. * * @author Axel Uhl (D043530) * @@ -19,14 +31,31 @@ import difflib.Patch; public class CourseAsWaypointList extends AbstractList { private final Course course; + /** + * Keys are the waypoints to be inserted, values are the proxy waypoints used as placeholder instead of the + * key waypoints. The placeholder waypoint will be replaced by the key waypoint when {@link #remove(int)} removes + * the key waypoint. + */ + private final Map delayedInserts; + + private static final ControlPoint dummyControlPointForPlaceholderWaypoints = new MarkImpl("Dummy mark for placeholder waypoints"); + public CourseAsWaypointList(Course course) { super(); this.course = course; + this.delayedInserts = new HashMap(); } @Override public void add(int index, Waypoint element) { - course.addWaypoint(index, element); + if (course.getIndexOfWaypoint(element) == -1) { + course.addWaypoint(index, element); // no duplicate would be produced, immediate insert is safe + } else { + // a duplicate would result from inserting the waypoint; insert a placeholder waypoint and remember it + Waypoint placeholder = new WaypointImpl(dummyControlPointForPlaceholderWaypoints); + delayedInserts.put(element, placeholder); + course.addWaypoint(index, placeholder); + } } @Override @@ -42,6 +71,13 @@ public class CourseAsWaypointList extends AbstractList { public Waypoint remove(int index) { Waypoint toRemove = get(index); course.removeWaypoint(index); + Waypoint placeholder = delayedInserts.remove(toRemove); + if (placeholder != null) { + // now it's safe to replace the placeholder by the real waypoint that was passed to add(...) + int placeholderIndex = course.getIndexOfWaypoint(placeholder); + course.removeWaypoint(placeholderIndex); + course.addWaypoint(placeholderIndex, toRemove); + } return toRemove; }