String url = DeviceMappingConstants.getBuoyTenderInvitationUrl(serverUrlWithoutTrailingSlash, leaderboardName,
eventId, NonGwtUrlHelper.INSTANCE);
diff --git a/java/com.sap.sailing.domain.shared.android/META-INF/MANIFEST.MF b/java/com.sap.sailing.domain.shared.android/META-INF/MANIFEST.MF
index 5e841b9aafc..79b133999a3 100644
--- a/java/com.sap.sailing.domain.shared.android/META-INF/MANIFEST.MF
+++ b/java/com.sap.sailing.domain.shared.android/META-INF/MANIFEST.MF
@@ -6,9 +6,9 @@ Bundle-Version: 1.0.0.qualifier
Bundle-Vendor: SAP
Bundle-RequiredExecutionEnvironment: JavaSE-1.7
Require-Bundle: com.sap.sailing.domain.common,
- com.sap.sse.datamining.shared,
com.sap.sse.common,
- com.sap.sse.shared.android
+ com.sap.sse.shared.android,
+ com.sap.sse.datamining.annotations;bundle-version="1.0.0"
Export-Package: com.sap.sailing.domain.abstractlog,
com.sap.sailing.domain.abstractlog.impl,
com.sap.sailing.domain.abstractlog.race,
diff --git a/java/com.sap.sailing.domain.shared.android/build.gradle b/java/com.sap.sailing.domain.shared.android/build.gradle
index b75064f8383..2e51d9cecaf 100644
--- a/java/com.sap.sailing.domain.shared.android/build.gradle
+++ b/java/com.sap.sailing.domain.shared.android/build.gradle
@@ -10,6 +10,6 @@ dependencies {
/* local dependencies */
compile project(":java:com.sap.sailing.domain.common")
compile project(":java:com.sap.sse.common")
- compile project(":java:com.sap.sse.datamining.shared")
+ compile project(":java:com.sap.sse.datamining.annotations")
compile project(":java:com.sap.sse.shared.android")
}
diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/Revokable.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/Revokable.java
index a4643c274a3..abdad5cd632 100644
--- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/Revokable.java
+++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/Revokable.java
@@ -11,7 +11,7 @@ import com.sap.sailing.domain.abstractlog.race.RaceLogEvent;
* the event-iterators of the {@code RaceLog} ({@link RaceLog#getUnrevokedEvents()},
* {@link RaceLog#getUnrevokedEventsDescending()}).
*
- * The event {@code r} can only successufully revoke {@code e}, if {@code r}'s priority is higher
+ * The event {@code r} can only successfully revoke {@code e}, if {@code r}'s priority is higher
* (author prio, timepoint), and if {@code e} is of an event type that implements {@code Revokable}.
* @author Fredrik Teschke
*
diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/impl/AbstractLogImpl.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/impl/AbstractLogImpl.java
index 21684189a64..4436a2045bb 100644
--- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/impl/AbstractLogImpl.java
+++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/impl/AbstractLogImpl.java
@@ -123,8 +123,11 @@ extends TrackImpl implements AbstractLog {
try {
checkIfSuccessfullyRevokes(revokeEvent);
lockForWrite();
- revokedEventIds.add(revokeEvent.getRevokedEventId());
- unlockAfterWrite();
+ try {
+ revokedEventIds.add(revokeEvent.getRevokedEventId());
+ } finally {
+ unlockAfterWrite();
+ }
} catch (NotRevokableException e) {
logger.log(Level.WARNING, e.getMessage());
}
@@ -133,9 +136,12 @@ extends TrackImpl implements AbstractLog {
private void checkIfSuccessfullyRevokes(RevokeEvent> revokeEvent) throws NotRevokableException {
lockForRead();
- EventT revokedEvent = getEventById(revokeEvent.getRevokedEventId());
- unlockAfterRead();
-
+ EventT revokedEvent;
+ try {
+ revokedEvent = getEventById(revokeEvent.getRevokedEventId());
+ } finally {
+ unlockAfterRead();
+ }
if (revokedEvent == null) {
// it can happen that the event that has been revoked is not yet loaded - as we assume
// that race log events never get removed we can safely continue and assume that
diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/impl/LogEventComparator.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/impl/LogEventComparator.java
index 11ddcea67cb..b85f09054ac 100644
--- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/impl/LogEventComparator.java
+++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/impl/LogEventComparator.java
@@ -8,6 +8,7 @@ import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor;
import com.sap.sailing.domain.abstractlog.race.RaceLogEvent;
import com.sap.sailing.domain.tracking.impl.TimedComparator;
import com.sap.sse.common.Timed;
+import com.sap.sse.common.Util;
/**
* Comparator sorting by pass, then by {@link AbstractLogEventAuthor}, then by {@link RaceLogEvent#getCreatedAt()}
@@ -43,7 +44,7 @@ public class LogEventComparator implements Comparator, Serializable {
if (result != 0) return result;
//compare logical timepoints
- result = e1.getLogicalTimePoint().compareTo(e2.getLogicalTimePoint());
+ result = Util.compareToWithNull(e1.getLogicalTimePoint(), e2.getLogicalTimePoint(), /* nullIsLess */ false);
if (result != 0) return result;
//compare ids
diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/SimpleRaceLogIdentifier.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/SimpleRaceLogIdentifier.java
index 4e5d65e9f28..5dc573afaa4 100644
--- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/SimpleRaceLogIdentifier.java
+++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/SimpleRaceLogIdentifier.java
@@ -12,6 +12,4 @@ public interface SimpleRaceLogIdentifier extends Serializable {
String getFleetName();
Triple getIdentifier();
-
- String getDeprecatedIdentifier();
}
\ No newline at end of file
diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/analyzing/impl/LastFlagsFinder.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/analyzing/impl/LastFlagsFinder.java
index 0d25623da77..eaaa81fb4d9 100644
--- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/analyzing/impl/LastFlagsFinder.java
+++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/analyzing/impl/LastFlagsFinder.java
@@ -10,6 +10,7 @@ import com.sap.sailing.domain.abstractlog.race.RaceLog;
import com.sap.sailing.domain.abstractlog.race.RaceLogEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogFlagEvent;
import com.sap.sse.common.TimePoint;
+import com.sap.sse.common.Util;
/**
* Analysis returns the most recent {@link RaceLogFlagEvent}s.
@@ -42,7 +43,7 @@ public class LastFlagsFinder extends RaceLogAnalyzer> {
List result = new ArrayList();
TimePoint logicalTime = flagEvent.getLogicalTimePoint();
- while (flagEvent.getLogicalTimePoint().equals(logicalTime)) {
+ while (Util.compareToWithNull(flagEvent.getLogicalTimePoint(), logicalTime, /* nullIsLess */ false) == 0) {
result.add(flagEvent);
flagEvent = getNextFlagEvent(iterator);
if (flagEvent == null) {
@@ -84,7 +85,7 @@ public class LastFlagsFinder extends RaceLogAnalyzer> {
Collections.sort(sortedEvents, new Comparator() {
@Override
public int compare(RaceLogFlagEvent left, RaceLogFlagEvent right) {
- int result = right.getLogicalTimePoint().compareTo(left.getLogicalTimePoint());
+ int result = Util.compareToWithNull(right.getLogicalTimePoint(), left.getLogicalTimePoint(), /* nullIsLess */ false);
if (result == 0) {
result = Boolean.valueOf(right.isDisplayed()).compareTo(left.isDisplayed());
if (result == 0) {
diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/impl/SimpleRaceLogIdentifierImpl.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/impl/SimpleRaceLogIdentifierImpl.java
index a5bcdd12c36..366ec5bd51c 100644
--- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/impl/SimpleRaceLogIdentifierImpl.java
+++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/impl/SimpleRaceLogIdentifierImpl.java
@@ -41,15 +41,6 @@ public class SimpleRaceLogIdentifierImpl implements SimpleRaceLogIdentifier {
regattaLikeParentName, raceColumnName, fleetName);
}
- @Override
- public String getDeprecatedIdentifier() {
- return String.format("%s.%s.%s",
- regattaLikeParentName,
- raceColumnName,
- fleetName);
- }
-
-
@Override
public int hashCode() {
final int prime = 31;
diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/tracking/analyzing/impl/DefinedMarkFinder.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/tracking/analyzing/impl/RaceLogDefinedMarkFinder.java
similarity index 78%
rename from java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/tracking/analyzing/impl/DefinedMarkFinder.java
rename to java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/tracking/analyzing/impl/RaceLogDefinedMarkFinder.java
index 15de65022db..23859c29e34 100644
--- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/tracking/analyzing/impl/DefinedMarkFinder.java
+++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/tracking/analyzing/impl/RaceLogDefinedMarkFinder.java
@@ -5,7 +5,6 @@ import java.util.Collection;
import java.util.List;
import com.sap.sailing.domain.abstractlog.AbstractLog;
-import com.sap.sailing.domain.abstractlog.LogAnalyzer;
import com.sap.sailing.domain.abstractlog.MultiLogAnalyzer.AnalyzerFactory;
import com.sap.sailing.domain.abstractlog.race.RaceLog;
import com.sap.sailing.domain.abstractlog.race.RaceLogEvent;
@@ -13,15 +12,15 @@ import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogAnalyzer;
import com.sap.sailing.domain.abstractlog.race.tracking.RaceLogDefineMarkEvent;
import com.sap.sailing.domain.base.Mark;
-public class DefinedMarkFinder extends RaceLogAnalyzer> {
- public DefinedMarkFinder(RaceLog raceLog) {
+public class RaceLogDefinedMarkFinder extends RaceLogAnalyzer> {
+ public RaceLogDefinedMarkFinder(RaceLog raceLog) {
super(raceLog);
}
public static class Factory implements AnalyzerFactory> {
@Override
- public LogAnalyzer> createAnalyzer(AbstractLog, ?> log) {
- return new DefinedMarkFinder((RaceLog) log);
+ public RaceLogAnalyzer> createAnalyzer(AbstractLog, ?> log) {
+ return new RaceLogDefinedMarkFinder((RaceLog) log);
}
}
diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/tracking/impl/RaceLogRegisterCompetitorEventImpl.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/tracking/impl/RaceLogRegisterCompetitorEventImpl.java
index fb3791e2963..eb20dd7f79a 100644
--- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/tracking/impl/RaceLogRegisterCompetitorEventImpl.java
+++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/tracking/impl/RaceLogRegisterCompetitorEventImpl.java
@@ -1,6 +1,7 @@
package com.sap.sailing.domain.abstractlog.race.tracking.impl;
import java.io.Serializable;
+import java.util.Collections;
import java.util.List;
import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor;
@@ -35,7 +36,7 @@ public class RaceLogRegisterCompetitorEventImpl extends BaseRegisterCompetitorEv
@Override
public List getInvolvedBoats() {
- return raceLogEventData.getInvolvedBoats();
+ return Collections.singletonList(getCompetitor());
}
@Override
diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/shared/events/DeviceMappingEvent.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/shared/events/DeviceMappingEvent.java
index 2a31b71140e..ed69b2cb35d 100644
--- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/shared/events/DeviceMappingEvent.java
+++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/shared/events/DeviceMappingEvent.java
@@ -2,7 +2,7 @@ package com.sap.sailing.domain.abstractlog.shared.events;
import com.sap.sailing.domain.abstractlog.AbstractLogEvent;
import com.sap.sailing.domain.abstractlog.Revokable;
-import com.sap.sailing.domain.abstractlog.race.tracking.analyzing.impl.DefinedMarkFinder;
+import com.sap.sailing.domain.abstractlog.race.tracking.analyzing.impl.RaceLogDefinedMarkFinder;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Mark;
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
@@ -19,7 +19,7 @@ import com.sap.sse.common.WithID;
* If one end of the time range enclosed by {@link #getFrom()} and {@link #getTo()} is open, this can be closed
* by a {@link CloseOpenEndedDeviceMappingEvent}.
*
- * @see DefinedMarkFinder for rules by which {@link DeviceMapping}s are derived from these events.
+ * @see RaceLogDefinedMarkFinder for rules by which {@link DeviceMapping}s are derived from these events.
* @author Fredrik Teschke
*/
public interface DeviceMappingEvent extends AbstractLogEvent, Revokable {
diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/Boat.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/Boat.java
index 6220229b39c..0746e5e74da 100644
--- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/Boat.java
+++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/Boat.java
@@ -2,7 +2,7 @@ package com.sap.sailing.domain.base;
import com.sap.sse.common.Color;
import com.sap.sse.common.Named;
-import com.sap.sse.datamining.shared.annotations.Dimension;
+import com.sap.sse.datamining.annotations.Dimension;
public interface Boat extends Named {
BoatClass getBoatClass();
diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/Competitor.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/Competitor.java
index a0052733151..74a7f176fdd 100644
--- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/Competitor.java
+++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/Competitor.java
@@ -8,7 +8,7 @@ import com.sap.sse.common.Duration;
import com.sap.sse.common.IsManagedByCache;
import com.sap.sse.common.Named;
import com.sap.sse.common.WithID;
-import com.sap.sse.datamining.shared.annotations.Connector;
+import com.sap.sse.datamining.annotations.Connector;
public interface Competitor extends Named, WithID, IsManagedByCache {
@Connector(messageKey="Team", ordinal=9)
diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/Nationality.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/Nationality.java
index d675e7a25a1..63e55d8e851 100644
--- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/Nationality.java
+++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/Nationality.java
@@ -3,7 +3,7 @@ package com.sap.sailing.domain.base;
import com.sap.sse.common.CountryCode;
import com.sap.sse.common.IsManagedByCache;
import com.sap.sse.common.Named;
-import com.sap.sse.datamining.shared.annotations.Dimension;
+import com.sap.sse.datamining.annotations.Dimension;
public interface Nationality extends Named, IsManagedByCache {
@Dimension(messageKey="Acronym")
diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/WithNationality.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/WithNationality.java
index 33234b9a7c0..e638dde0eef 100644
--- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/WithNationality.java
+++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/WithNationality.java
@@ -1,6 +1,6 @@
package com.sap.sailing.domain.base;
-import com.sap.sse.datamining.shared.annotations.Connector;
+import com.sap.sse.datamining.annotations.Connector;
public interface WithNationality {
@Connector(messageKey="Nationality", ordinal=8)
diff --git a/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/CompetitorWithID.java b/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/CompetitorWithID.java
index 1b0d9499523..717d4f38716 100644
--- a/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/CompetitorWithID.java
+++ b/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/CompetitorWithID.java
@@ -41,15 +41,15 @@ public class CompetitorWithID implements Competitor {
return id;
}
- @Override
- public String toString() {
- return "CompetitorWithID [boatID=" + boatID + ", threeLetterIOCCode="
- + threeLetterIOCCode + ", name=" + name + ", id=" + id + "]";
- }
+ @Override
+ public String toString() {
+ return "CompetitorWithID [boatID=" + boatID + ", threeLetterIOCCode=" + threeLetterIOCCode + ", name=" + name
+ + ", id=" + id + "]";
+ }
- @Override
- public List getCrew() {
- return crew;
- }
+ @Override
+ public List getCrew() {
+ return crew;
+ }
}
diff --git a/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/DomainFactoryImpl.java b/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/DomainFactoryImpl.java
index cb41a8738dc..6227fb48c01 100755
--- a/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/DomainFactoryImpl.java
+++ b/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/DomainFactoryImpl.java
@@ -249,12 +249,12 @@ public class DomainFactoryImpl implements DomainFactory {
private Iterable createCompetitorList(StartList startList, String raceId, BoatClass boatClass) {
List result = new ArrayList();
for (com.sap.sailing.domain.swisstimingadapter.Competitor swissTimingCompetitor : startList.getCompetitors()) {
- Competitor domainCompetitor;
- if(swissTimingCompetitor.getID() != null) {
+ Competitor domainCompetitor;
+ if (swissTimingCompetitor.getID() != null) {
domainCompetitor = createCompetitorWithID(swissTimingCompetitor, boatClass);
- } else {
+ } else {
domainCompetitor = createCompetitorWithoutID(swissTimingCompetitor, raceId, boatClass);
- }
+ }
result.add(domainCompetitor);
}
return result;
diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/common/test/NaturalComparatorTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/common/test/NaturalComparatorTest.java
index 3e23aae89d9..b73f46c6058 100644
--- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/common/test/NaturalComparatorTest.java
+++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/common/test/NaturalComparatorTest.java
@@ -10,7 +10,7 @@ import java.util.List;
import org.junit.Before;
import org.junit.Test;
-import com.sap.sailing.domain.common.impl.NaturalComparator;
+import com.sap.sse.common.util.NaturalComparator;
public class NaturalComparatorTest {
diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/CompetitorProviderCacheInvalidationTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/CompetitorProviderCacheInvalidationTest.java
new file mode 100644
index 00000000000..2d261296651
--- /dev/null
+++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/CompetitorProviderCacheInvalidationTest.java
@@ -0,0 +1,348 @@
+package com.sap.sailing.domain.test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.sap.sailing.domain.abstractlog.impl.LogEventAuthorImpl;
+import com.sap.sailing.domain.abstractlog.race.RaceLog;
+import com.sap.sailing.domain.abstractlog.race.RaceLogEventFactory;
+import com.sap.sailing.domain.abstractlog.race.tracking.RaceLogRegisterCompetitorEvent;
+import com.sap.sailing.domain.abstractlog.regatta.RegattaLog;
+import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogRegisterCompetitorEvent;
+import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogRegisterCompetitorEventImpl;
+import com.sap.sailing.domain.base.Competitor;
+import com.sap.sailing.domain.base.impl.BoatClassImpl;
+import com.sap.sailing.domain.base.impl.CourseAreaImpl;
+import com.sap.sailing.domain.base.impl.FleetImpl;
+import com.sap.sailing.domain.base.impl.RegattaImpl;
+import com.sap.sailing.domain.base.impl.SeriesImpl;
+import com.sap.sailing.domain.common.BoatClassMasterdata;
+import com.sap.sailing.domain.common.LeaderboardNameConstants;
+import com.sap.sailing.domain.common.abstractlog.NotRevokableException;
+import com.sap.sailing.domain.leaderboard.impl.CompetitorProviderFromRaceColumnsAndRegattaLike;
+import com.sap.sailing.domain.leaderboard.impl.FlexibleLeaderboardImpl;
+import com.sap.sailing.domain.leaderboard.impl.LowPoint;
+import com.sap.sailing.domain.leaderboard.impl.RegattaLeaderboardImpl;
+import com.sap.sailing.domain.leaderboard.impl.ThresholdBasedResultDiscardingRuleImpl;
+import com.sap.sailing.domain.racelog.impl.EmptyRaceLogStore;
+import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore;
+import com.sap.sailing.domain.test.mock.MockedTrackedRaceWithStartTimeAndRanks;
+import com.sap.sailing.domain.tracking.TrackedRace;
+import com.sap.sse.common.Util;
+import com.sap.sse.common.impl.MillisecondsTimePoint;
+
+/**
+ * Tests the behavior of the class {@link CompetitorProviderFromRaceColumnsAndRegattaLike}, paying particular
+ * attention to its caching and cache invalidation logic. When competitors are added to regatta logs or race logs
+ * or when a tracked race is attached to a column or a column is added or removed, its caches need to be
+ * invalidated and re-calculated accordingly.
+ *
+ * @author Axel Uhl (d043530)
+ *
+ */
+public class CompetitorProviderCacheInvalidationTest extends AbstractLeaderboardTest {
+ private CompetitorProviderFromRaceColumnsAndRegattaLike competitorProviderFlexibleLeaderboard;
+ private CompetitorProviderFromRaceColumnsAndRegattaLike competitorProviderRegattaLeaderboard;
+ private FlexibleLeaderboardImpl flexibleLeaderboard;
+ private RegattaLeaderboardImpl regattaLeaderboard;
+ private RegattaImpl regatta;
+ final int NUMBER_OF_COMP_LISTS = 4;
+ @SuppressWarnings("unchecked")
+ private List[] compLists = (List[]) new List>[NUMBER_OF_COMP_LISTS];
+
+ @Before
+ public void setUp() {
+ final CourseAreaImpl courseArea = new CourseAreaImpl(
+ "Test Course Area", UUID.randomUUID());
+ flexibleLeaderboard = new FlexibleLeaderboardImpl("Test Flexible Leaderboard",
+ new ThresholdBasedResultDiscardingRuleImpl(new int[0]), new LowPoint(), courseArea);
+ competitorProviderFlexibleLeaderboard = new CompetitorProviderFromRaceColumnsAndRegattaLike(flexibleLeaderboard);
+ regatta = new RegattaImpl(EmptyRaceLogStore.INSTANCE,
+ EmptyRegattaLogStore.INSTANCE, "Test Regatta", new BoatClassImpl("49er", BoatClassMasterdata._49ER), /* startDate */
+ null, /* endDate */null, /* trackedRegattaRegistry */null, new LowPoint(), UUID.randomUUID(),
+ courseArea);
+ regattaLeaderboard = new RegattaLeaderboardImpl(regatta, new ThresholdBasedResultDiscardingRuleImpl(new int[0]));
+ regatta.addSeries(new SeriesImpl("Test Series", /* isMedal */false, Arrays.asList(new FleetImpl("Yellow"),
+ new FleetImpl("Blue")), Arrays.asList("R1", "R2", "R3"), /* trackedRegattaRegistry */null));
+ for (int l = 0; l < NUMBER_OF_COMP_LISTS; l++) {
+ compLists[l] = new ArrayList();
+ for (int i = 0; i < 10; i++) {
+ compLists[l].add(createCompetitor("" + l + "/" + i));
+ }
+ }
+ competitorProviderRegattaLeaderboard = new CompetitorProviderFromRaceColumnsAndRegattaLike(regattaLeaderboard);
+ }
+
+ @Test
+ public void testSimpleCompetitorListOnOneRaceInFlexibleLeaderboard() {
+ TrackedRace trackedRace = new MockedTrackedRaceWithStartTimeAndRanks(MillisecondsTimePoint.now(), compLists[0]);
+ flexibleLeaderboard.addRace(trackedRace, "R1", /* medalRace */ false);
+ Set expected = new HashSet<>(compLists[0]);
+ Set actual = new HashSet<>();
+ Util.addAll(competitorProviderFlexibleLeaderboard.getAllCompetitors(), actual);
+ assertEquals(expected, actual);
+ Set actualForRace = new HashSet<>();
+ Util.addAll(competitorProviderFlexibleLeaderboard.getAllCompetitors(
+ flexibleLeaderboard.getRaceColumnByName("R1"),
+ flexibleLeaderboard.getFleet(LeaderboardNameConstants.DEFAULT_FLEET_NAME)), actualForRace);
+ assertEquals(expected, actualForRace);
+ }
+
+ @Test
+ public void testSimpleCompetitorListOnOneRaceLogInFlexibleLeaderboard() {
+ flexibleLeaderboard.addRaceColumn("R1", /* medalRace */ false);
+ RaceLog raceLog = flexibleLeaderboard.getRacelog("R1", LeaderboardNameConstants.DEFAULT_FLEET_NAME);
+ final int passId = 1;
+ final LogEventAuthorImpl author = new LogEventAuthorImpl("Me", 0);
+ final Map competitorOnRaceLogRegistrationEvents = new HashMap<>();
+ for (Competitor c : compLists[0]) {
+ final RaceLogRegisterCompetitorEvent registerCompetitorEvent = RaceLogEventFactory.INSTANCE.createRegisterCompetitorEvent(MillisecondsTimePoint.now(), author, passId, c);
+ raceLog.add(registerCompetitorEvent);
+ competitorOnRaceLogRegistrationEvents.put(c, registerCompetitorEvent);
+ }
+ Set expected = new HashSet<>(compLists[0]);
+ Set actual = new HashSet<>();
+ Util.addAll(competitorProviderFlexibleLeaderboard.getAllCompetitors(), actual);
+ assertEquals(expected, actual);
+ Set actualForRace = new HashSet<>();
+ Util.addAll(competitorProviderFlexibleLeaderboard.getAllCompetitors(
+ flexibleLeaderboard.getRaceColumnByName("R1"),
+ flexibleLeaderboard.getFleet(LeaderboardNameConstants.DEFAULT_FLEET_NAME)), actualForRace);
+ assertEquals(expected, actualForRace);
+ // we're now revoking a competitor registration on the race log and assert
+ // that the competitor is gone from the list:
+ final Competitor competitorToRevokeFromRaceLog = compLists[0].get(2);
+ raceLog.add(RaceLogEventFactory.INSTANCE.createRevokeEvent(author, passId, competitorOnRaceLogRegistrationEvents.get(competitorToRevokeFromRaceLog), "Test revoking"));
+ actualForRace = new HashSet<>(); // try another time; cache should of course yield an equal result (although
+ // we're not asserting here that the result actually comes from the cache)
+ Util.addAll(competitorProviderFlexibleLeaderboard.getAllCompetitors(
+ flexibleLeaderboard.getRaceColumnByName("R1"),
+ flexibleLeaderboard.getFleet(LeaderboardNameConstants.DEFAULT_FLEET_NAME)), actualForRace);
+ expected.remove(competitorToRevokeFromRaceLog);
+ assertEquals(expected.size(), actualForRace.size());
+ assertEquals(expected, actualForRace);
+ flexibleLeaderboard.removeRaceColumn("R1"); // test that changes to the race columns invalidate the cache
+ flexibleLeaderboard.addRaceColumn("R1", /* medalRace */ false);
+ actualForRace = new HashSet<>(); // try another time; cache should of course yield an equal result (although
+ // we're not asserting here that the result actually comes from the cache)
+ Util.addAll(competitorProviderFlexibleLeaderboard.getAllCompetitors(
+ flexibleLeaderboard.getRaceColumnByName("R1"),
+ flexibleLeaderboard.getFleet(LeaderboardNameConstants.DEFAULT_FLEET_NAME)), actualForRace);
+ assertTrue(actualForRace.isEmpty());
+ }
+
+ @Test
+ public void testSimpleCompetitorListOnRegattaLogInFlexibleLeaderboard() {
+ flexibleLeaderboard.addRaceColumn("R1", /* medalRace */ false);
+ RegattaLog regattaLog = flexibleLeaderboard.getRegattaLog();
+ for (Competitor c : compLists[0]) {
+ regattaLog.add(new RegattaLogRegisterCompetitorEventImpl(MillisecondsTimePoint.now(), new LogEventAuthorImpl("Me", 0), MillisecondsTimePoint.now(), UUID.randomUUID(), c));
+ }
+ Set expected = new HashSet<>(compLists[0]);
+ Set actual = new HashSet<>();
+ Util.addAll(competitorProviderFlexibleLeaderboard.getAllCompetitors(), actual);
+ assertEquals(expected, actual);
+ Set actualForRace = new HashSet<>();
+ Util.addAll(competitorProviderFlexibleLeaderboard.getAllCompetitors(
+ flexibleLeaderboard.getRaceColumnByName("R1"),
+ flexibleLeaderboard.getFleet(LeaderboardNameConstants.DEFAULT_FLEET_NAME)), actualForRace);
+ assertEquals(expected, actualForRace);
+ }
+
+ @Test
+ public void testSimpleCompetitorListOnOneRaceLogAndRegattaLogInFlexibleLeaderboard() {
+ flexibleLeaderboard.addRaceColumn("R1", /* medalRace */ false);
+ RegattaLog regattaLog = flexibleLeaderboard.getRegattaLog();
+ for (Competitor c : compLists[0]) {
+ regattaLog.add(new RegattaLogRegisterCompetitorEventImpl(MillisecondsTimePoint.now(), new LogEventAuthorImpl("Me", 0), MillisecondsTimePoint.now(), UUID.randomUUID(), c));
+ }
+ RaceLog raceLog = flexibleLeaderboard.getRacelog("R1", LeaderboardNameConstants.DEFAULT_FLEET_NAME);
+ for (Competitor c : compLists[1]) {
+ raceLog.add(RaceLogEventFactory.INSTANCE.createRegisterCompetitorEvent(MillisecondsTimePoint.now(), new LogEventAuthorImpl("Me", 0), 1, c));
+ }
+ Set expected = new HashSet<>(compLists[0]);
+ expected.addAll(compLists[1]);
+ Set actual = new HashSet<>();
+ Util.addAll(competitorProviderFlexibleLeaderboard.getAllCompetitors(), actual);
+ assertEquals(expected, actual);
+ Set actualForRace = new HashSet<>();
+ Util.addAll(competitorProviderFlexibleLeaderboard.getAllCompetitors(
+ flexibleLeaderboard.getRaceColumnByName("R1"),
+ flexibleLeaderboard.getFleet(LeaderboardNameConstants.DEFAULT_FLEET_NAME)), actualForRace);
+ assertEquals(expected, actualForRace);
+ }
+
+ @Test
+ public void testTwoCompetitorListsOnTwoRacesInFlexibleLeaderboard() {
+ TrackedRace trackedRace1 = new MockedTrackedRaceWithStartTimeAndRanks(MillisecondsTimePoint.now(), compLists[0]);
+ flexibleLeaderboard.addRace(trackedRace1, "R1", /* medalRace */ false);
+ TrackedRace trackedRace2 = new MockedTrackedRaceWithStartTimeAndRanks(MillisecondsTimePoint.now(), compLists[1]);
+ flexibleLeaderboard.addRace(trackedRace2, "R2", /* medalRace */ false);
+ Set expected = new HashSet<>(compLists[0]);
+ expected.addAll(compLists[1]);
+ Set actual = new HashSet<>();
+ Util.addAll(competitorProviderFlexibleLeaderboard.getAllCompetitors(), actual);
+ assertEquals(expected, actual);
+ Set actualForRace1 = new HashSet<>();
+ Util.addAll(competitorProviderFlexibleLeaderboard.getAllCompetitors(
+ flexibleLeaderboard.getRaceColumnByName("R1"),
+ flexibleLeaderboard.getFleet(LeaderboardNameConstants.DEFAULT_FLEET_NAME)), actualForRace1);
+ Set actualForRace2 = new HashSet<>();
+ Util.addAll(competitorProviderFlexibleLeaderboard.getAllCompetitors(
+ flexibleLeaderboard.getRaceColumnByName("R2"),
+ flexibleLeaderboard.getFleet(LeaderboardNameConstants.DEFAULT_FLEET_NAME)), actualForRace2);
+ assertEquals(new HashSet<>(compLists[0]), actualForRace1);
+ assertEquals(new HashSet<>(compLists[1]), actualForRace2);
+ }
+
+ @Test
+ public void testSimpleCompetitorListOnOneRaceInRegattaLeaderboard() {
+ TrackedRace trackedRace = new MockedTrackedRaceWithStartTimeAndRanks(MillisecondsTimePoint.now(), compLists[0]);
+ regattaLeaderboard.getRaceColumnByName("R1").setTrackedRace(regattaLeaderboard.getRaceColumnByName("R1").getFleetByName("Yellow"), trackedRace);
+ Set expected = new HashSet<>(compLists[0]);
+ Set actual = new HashSet<>();
+ Util.addAll(competitorProviderRegattaLeaderboard.getAllCompetitors(), actual);
+ assertEquals(expected, actual);
+ Set actualForRaceYellow = new HashSet<>();
+ Util.addAll(competitorProviderRegattaLeaderboard.getAllCompetitors(
+ regattaLeaderboard.getRaceColumnByName("R1"), regattaLeaderboard.getFleet("Yellow")), actualForRaceYellow);
+ assertEquals(expected, actualForRaceYellow);
+ Set actualForRaceBlue = new HashSet<>();
+ Util.addAll(competitorProviderRegattaLeaderboard.getAllCompetitors(
+ regattaLeaderboard.getRaceColumnByName("R1"), regattaLeaderboard.getFleet("Blue")), actualForRaceBlue);
+ assertEquals(Collections.emptySet(), actualForRaceBlue);
+ }
+
+ @Test
+ public void testTwoCompetitorListsOnTwoRacesInRegattaLeaderboard() {
+ TrackedRace trackedRace1 = new MockedTrackedRaceWithStartTimeAndRanks(MillisecondsTimePoint.now(), compLists[0]);
+ regattaLeaderboard.getRaceColumnByName("R1").setTrackedRace(regattaLeaderboard.getRaceColumnByName("R1").getFleetByName("Yellow"), trackedRace1);
+ TrackedRace trackedRace2 = new MockedTrackedRaceWithStartTimeAndRanks(MillisecondsTimePoint.now(), compLists[1]);
+ regattaLeaderboard.getRaceColumnByName("R1").setTrackedRace(regattaLeaderboard.getRaceColumnByName("R1").getFleetByName("Blue"), trackedRace2);
+ Set expected = new HashSet<>(compLists[0]);
+ expected.addAll(compLists[1]);
+ Set actual = new HashSet<>();
+ Util.addAll(competitorProviderRegattaLeaderboard.getAllCompetitors(), actual);
+ assertEquals(expected, actual);
+ Set actualForRaceYellow = new HashSet<>();
+ Util.addAll(competitorProviderRegattaLeaderboard.getAllCompetitors(
+ regattaLeaderboard.getRaceColumnByName("R1"), regattaLeaderboard.getFleet("Yellow")), actualForRaceYellow);
+ assertEquals(new HashSet<>(compLists[0]), actualForRaceYellow);
+ Set actualForRaceBlue = new HashSet<>();
+ Util.addAll(competitorProviderRegattaLeaderboard.getAllCompetitors(
+ regattaLeaderboard.getRaceColumnByName("R1"), regattaLeaderboard.getFleet("Blue")), actualForRaceBlue);
+ assertEquals(new HashSet<>(compLists[1]), actualForRaceBlue);
+ }
+
+ @Test
+ public void testSimpleCompetitorListOnOneRaceLogInRegattaLeaderboard() {
+ RaceLog raceLog = regattaLeaderboard.getRacelog("R1", "Yellow");
+ for (Competitor c : compLists[0]) {
+ raceLog.add(RaceLogEventFactory.INSTANCE.createRegisterCompetitorEvent(MillisecondsTimePoint.now(), new LogEventAuthorImpl("Me", 0), 1, c));
+ }
+ Set expected = new HashSet<>(compLists[0]);
+ Set actual = new HashSet<>();
+ Util.addAll(competitorProviderRegattaLeaderboard.getAllCompetitors(), actual);
+ assertEquals(expected, actual);
+ Set actualForRaceYellow = new HashSet<>();
+ Util.addAll(competitorProviderRegattaLeaderboard.getAllCompetitors(
+ regattaLeaderboard.getRaceColumnByName("R1"), regattaLeaderboard.getFleet("Yellow")), actualForRaceYellow);
+ assertEquals(new HashSet<>(compLists[0]), actualForRaceYellow);
+ }
+
+ @Test
+ public void testSimpleCompetitorListOnRegattaLogInRegattaLeaderboard() throws NotRevokableException {
+ RegattaLog regattaLog = regattaLeaderboard.getRegatta().getRegattaLog();
+ final Map competitorOnRegattaLogRegistrationEvents = new HashMap<>();
+ final LogEventAuthorImpl author = new LogEventAuthorImpl("Me", 0);
+ for (Competitor c : compLists[0]) {
+ final RegattaLogRegisterCompetitorEventImpl registerCompetitorEvent = new RegattaLogRegisterCompetitorEventImpl(MillisecondsTimePoint.now(), author, MillisecondsTimePoint.now(), UUID.randomUUID(), c);
+ regattaLog.add(registerCompetitorEvent);
+ competitorOnRegattaLogRegistrationEvents.put(c, registerCompetitorEvent);
+ }
+ Set expected = new HashSet<>(compLists[0]);
+ Set actual = new HashSet<>();
+ Util.addAll(competitorProviderRegattaLeaderboard.getAllCompetitors(), actual);
+ assertEquals(expected, actual);
+ Set actualForRaceYellow = new HashSet<>();
+ Util.addAll(competitorProviderRegattaLeaderboard.getAllCompetitors(
+ regattaLeaderboard.getRaceColumnByName("R1"), regattaLeaderboard.getFleet("Yellow")), actualForRaceYellow);
+ assertEquals(new HashSet<>(compLists[0]), actualForRaceYellow);
+ Set actualForRaceBlue = new HashSet<>();
+ Util.addAll(competitorProviderRegattaLeaderboard.getAllCompetitors(
+ regattaLeaderboard.getRaceColumnByName("R1"), regattaLeaderboard.getFleet("Blue")), actualForRaceBlue);
+ assertEquals(new HashSet<>(compLists[0]), actualForRaceBlue);
+ // now revoke one registration again from the regatta log
+ final Competitor competitorToRevokeFromRegattaLog = compLists[0].get(2);
+ regattaLog.revokeEvent(author, competitorOnRegattaLogRegistrationEvents.get(competitorToRevokeFromRegattaLog));
+ actualForRaceYellow = new HashSet<>(); // try another time; cache should of course yield an equal result (although
+ // we're not asserting here that the result actually comes from the cache)
+ Util.addAll(competitorProviderRegattaLeaderboard.getAllCompetitors(
+ regattaLeaderboard.getRaceColumnByName("R1"), regattaLeaderboard.getFleet("Yellow")), actualForRaceYellow);
+ expected.remove(competitorToRevokeFromRegattaLog);
+ assertEquals(expected.size(), actualForRaceYellow.size());
+ assertEquals(expected, actualForRaceYellow);
+ }
+
+ @Test
+ public void testSimpleCompetitorListOnOneRaceLogAndRegattaLogInRegattaLeaderboard() {
+ final RegattaLog regattaLog = regattaLeaderboard.getRegatta().getRegattaLog();
+ final LogEventAuthorImpl author = new LogEventAuthorImpl("Me", 0);
+ for (Competitor c : compLists[0]) {
+ regattaLog.add(new RegattaLogRegisterCompetitorEventImpl(MillisecondsTimePoint.now(), author, MillisecondsTimePoint.now(), UUID.randomUUID(), c));
+ }
+ final RaceLog raceLog = regattaLeaderboard.getRacelog("R1", "Yellow");
+ final int passId = 1;
+ final Map competitorOnRaceLogRegistrationEvents = new HashMap<>();
+ for (Competitor c : compLists[passId]) {
+ final RaceLogRegisterCompetitorEvent registerCompetitorEvent = RaceLogEventFactory.INSTANCE.createRegisterCompetitorEvent(MillisecondsTimePoint.now(), author, passId, c);
+ raceLog.add(registerCompetitorEvent);
+ competitorOnRaceLogRegistrationEvents.put(c, registerCompetitorEvent);
+ }
+ regattaLeaderboard.setSuppressed(compLists[0].get(compLists[0].size()-passId), /* suppressed */ true);
+ Set expected = new HashSet<>(compLists[0]);
+ expected.addAll(compLists[passId]);
+ Set expectedWithoutSuppressed = new HashSet<>(expected);
+ expectedWithoutSuppressed.remove(compLists[0].get(compLists[0].size()-passId));
+ Set actual = new HashSet<>();
+ Util.addAll(competitorProviderRegattaLeaderboard.getAllCompetitors(), actual);
+ assertEquals(expected, actual);
+ Set actualWithoutSuppressed = new HashSet<>();
+ Util.addAll(regattaLeaderboard.getCompetitors(), actualWithoutSuppressed);
+ assertEquals(expectedWithoutSuppressed, actualWithoutSuppressed);
+ Set actualForRaceYellow = new HashSet<>();
+ Util.addAll(competitorProviderRegattaLeaderboard.getAllCompetitors(
+ regattaLeaderboard.getRaceColumnByName("R1"), regattaLeaderboard.getFleet("Yellow")), actualForRaceYellow);
+ assertEquals(expected, actualForRaceYellow);
+ actualForRaceYellow = new HashSet<>(); // try another time; cache should of course yield an equal result (although
+ // we're not asserting here that the result actually comes from the cache)
+ Util.addAll(competitorProviderRegattaLeaderboard.getAllCompetitors(
+ regattaLeaderboard.getRaceColumnByName("R1"), regattaLeaderboard.getFleet("Yellow")), actualForRaceYellow);
+ assertEquals(expected, actualForRaceYellow);
+ // But now it gets interesting: we're revoking a competitor registration on the race log and assert
+ // that the competitor is gone from the list:
+ final Competitor competitorToRevokeFromRaceLog = compLists[passId].get(2);
+ raceLog.add(RaceLogEventFactory.INSTANCE.createRevokeEvent(author, passId, competitorOnRaceLogRegistrationEvents.get(competitorToRevokeFromRaceLog), "Test revoking"));
+ actualForRaceYellow = new HashSet<>(); // try another time; cache should of course yield an equal result (although
+ // we're not asserting here that the result actually comes from the cache)
+ Util.addAll(competitorProviderRegattaLeaderboard.getAllCompetitors(
+ regattaLeaderboard.getRaceColumnByName("R1"), regattaLeaderboard.getFleet("Yellow")), actualForRaceYellow);
+ expected.remove(competitorToRevokeFromRaceLog);
+ assertEquals(expected.size(), actualForRaceYellow.size());
+ assertEquals(expected, actualForRaceYellow);
+ }
+
+}
diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LeaderboardScoringAndRankingTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LeaderboardScoringAndRankingTest.java
index b09baba3584..49a4594eb01 100644
--- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LeaderboardScoringAndRankingTest.java
+++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LeaderboardScoringAndRankingTest.java
@@ -2,6 +2,7 @@ package com.sap.sailing.domain.test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
@@ -69,6 +70,7 @@ import com.sap.sailing.domain.racelog.impl.EmptyRaceLogStore;
import com.sap.sailing.domain.ranking.OneDesignRankingMetric;
import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore;
import com.sap.sailing.domain.test.mock.MockedTrackedRaceWithStartTimeAndRanks;
+import com.sap.sailing.domain.test.mock.MockedTrackedRaceWithStartTimeAndZeroRanks;
import com.sap.sailing.domain.tracking.MarkPassing;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.TrackedRegattaRegistry;
@@ -293,10 +295,14 @@ public class LeaderboardScoringAndRankingTest extends AbstractLeaderboardTest {
}
// expect all results to be valid because a fleet will have its score counted even if not all fleets have raced;
// see discussion for bug 961, but more importantly later on bug 1023.
- assertTrue(leaderboard.getScoringScheme().isValidInTotalScore(leaderboard, q2Column, later));
+ for (final Competitor competitor : competitors) {
+ assertTrue(leaderboard.getScoringScheme().isValidInTotalScore(leaderboard, q2Column, competitor, later));
+ }
// now add a score correction for Q2/Blue to make it count:
leaderboard.getScoreCorrection().correctScore(competitors.get(9), q2Column, 42.);
- assertTrue(leaderboard.getScoringScheme().isValidInTotalScore(leaderboard, q2Column, later));
+ for (final Competitor competitor : competitors) {
+ assertTrue(leaderboard.getScoringScheme().isValidInTotalScore(leaderboard, q2Column, competitor, later));
+ }
// the new order in Q2 expected to be { (9, 3), ... } (we don't know about any competitor in q2Blue but #9
// therefore the new total points for #9 are
// { 9: 5+42=47 }
@@ -644,7 +650,7 @@ public class LeaderboardScoringAndRankingTest extends AbstractLeaderboardTest {
/* medal */ false, "testTieBreakWithTwoVersusOneWins",
DomainFactory.INSTANCE.getOrCreateBoatClass("49er", /* typicallyStartsUpwind */true), DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT));
Leaderboard leaderboard = createLeaderboard(regatta, /* discarding thresholds */ new int[0]);
- TimePoint later = createAndAttachTrackedRaces(series.get(1), "Default", f1, f2, f3);
+ TimePoint later = createAndAttachTrackedRaces(series.get(1), "Default", /* withScores */ true, f1, f2, f3);
List rankedCompetitors = leaderboard.getCompetitorsFromBestToWorst(later);
assertEquals(leaderboard.getTotalPoints(c[0], later), leaderboard.getTotalPoints(c[1], later), 0.000000001);
assertEquals(Arrays.asList(new Competitor[] { c[0], c[1], c[2] }), rankedCompetitors);
@@ -663,12 +669,382 @@ public class LeaderboardScoringAndRankingTest extends AbstractLeaderboardTest {
/* medal */ false, "testTieBreakWithTwoVersusOneSeconds",
DomainFactory.INSTANCE.getOrCreateBoatClass("49er", /* typicallyStartsUpwind */true), DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT));
Leaderboard leaderboard = createLeaderboard(regatta, /* discarding thresholds */ new int[0]);
- TimePoint later = createAndAttachTrackedRaces(series.get(1), "Default", f1, f2, f3, f4, f5, f6);
+ TimePoint later = createAndAttachTrackedRaces(series.get(1), "Default", /* withScores */ true, f1, f2, f3, f4, f5, f6);
List rankedCompetitors = leaderboard.getCompetitorsFromBestToWorst(later);
assertEquals(leaderboard.getTotalPoints(c[0], later), leaderboard.getTotalPoints(c[1], later), 0.000000001);
assertTrue(rankedCompetitors.indexOf(c[0]) == rankedCompetitors.indexOf(c[1])-1);
}
+ @Test
+ public void testBasicElminationScoringScheme() throws NoWindException {
+ Regatta regatta = createRegattaWithEliminations(1, new int[] { 8, 4, 2, 2 }, "testBasicElminationScoringScheme",
+ DomainFactory.INSTANCE.getOrCreateBoatClass("49er", /* typicallyStartsUpwind */true),
+ DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT_WITH_ELIMINATIONS_AND_ROUNDS_WINNER_GETS_07));
+ Leaderboard leaderboard = createLeaderboard(regatta, /* discarding thresholds */ new int[0]);
+ Competitor[] c = createCompetitors(64).toArray(new Competitor[64]);
+ // first round with 64 competitors, eight per heat:
+ Competitor[][] competitorsForHeatsInRound1 = new Competitor[8][];
+ TimePoint later = null;
+ for (int heat=0; heat<8; heat++) {
+ competitorsForHeatsInRound1[heat] = new Competitor[8];
+ for (int i=0; i<8; i++) {
+ competitorsForHeatsInRound1[heat][i] = c[8*heat+i];
+ }
+ later = createAndAttachTrackedRaces(series.get(0), "Heat "+(heat+1), /* withScores */ true, competitorsForHeatsInRound1[heat]);
+ }
+ // quarter-finals has promoted top four competitors of first round in heats with eight competitors each:
+ Competitor[][] competitorsForHeatsInQuarterFinals = new Competitor[4][];
+ for (int heat=0; heat<4; heat++) {
+ competitorsForHeatsInQuarterFinals[heat] = new Competitor[8];
+ for (int i=0; i<8; i++) {
+ competitorsForHeatsInQuarterFinals[heat][i] = c[8*(2*heat+(i/4))+(i%4)];
+ }
+ later = createAndAttachTrackedRaces(series.get(1), "Heat "+(heat+9), /* withScores */ true, competitorsForHeatsInQuarterFinals[heat]);
+ }
+ // semi-finals has promoted top four competitors of quarter finals which are the top four of each other first-round heat:
+ Competitor[][] competitorsForHeatsInSemiFinals = new Competitor[2][];
+ for (int heat=0; heat<2; heat++) {
+ competitorsForHeatsInSemiFinals[heat] = new Competitor[8];
+ for (int i=0; i<8; i++) {
+ competitorsForHeatsInSemiFinals[heat][i] = c[8*(4*heat+2*(i/4))+(i%4)];
+ }
+ later = createAndAttachTrackedRaces(series.get(2), "Heat "+(heat+13), /* withScores */ true, competitorsForHeatsInSemiFinals[heat]);
+ }
+ // finals has promoted top four competitors of semi finals which are the top four of first and fifth first-round heats
+ // for the final, and the top four of the first round's third and seventh heat
+ Competitor[][] competitorsForHeatsInFinals = new Competitor[2][];
+ for (int heat=0; heat<2; heat++) {
+ competitorsForHeatsInFinals[heat] = new Competitor[8];
+ for (int i=0; i<8; i++) {
+ competitorsForHeatsInFinals[heat][i] = c[8*(2*heat+4*(i/4))+(i%4)];
+ }
+ later = createAndAttachTrackedRaces(series.get(3), "Heat "+(heat+15), /* withScores */ true, competitorsForHeatsInFinals[heat]);
+ }
+ List rankedCompetitors = leaderboard.getCompetitorsFromBestToWorst(later);
+ assertSame(c[0], rankedCompetitors.get(0)); // should be the winner of the final round's Final heat and take the "crown" for the elimination
+ assertEquals(0.7, leaderboard.getTotalPoints(c[0], later), 0.000000001);
+ assertSame(c[1], rankedCompetitors.get(1)); // should be the winner of the final round's Final heat and take the "crown" for the elimination
+ assertEquals(2, leaderboard.getTotalPoints(c[1], later), 0.000000001);
+ // first four of second heat get promoted to quarter final but lose their heat
+ assertNull(leaderboard.getTotalPoints(c[ 8], series.get(0).getRaceColumns().iterator().next(), later));
+ assertNull(leaderboard.getTotalPoints(c[ 9], series.get(0).getRaceColumns().iterator().next(), later));
+ assertNull(leaderboard.getTotalPoints(c[10], series.get(0).getRaceColumns().iterator().next(), later));
+ assertNull(leaderboard.getTotalPoints(c[11], series.get(0).getRaceColumns().iterator().next(), later));
+ assertEquals(18.5, leaderboard.getTotalPoints(c[ 8], later), 0.000000001);
+ assertEquals(22.5, leaderboard.getTotalPoints(c[ 9], later), 0.000000001);
+ assertEquals(26.5, leaderboard.getTotalPoints(c[10], later), 0.000000001);
+ assertEquals(30.5, leaderboard.getTotalPoints(c[11], later), 0.000000001);
+ // last four competitors in last heat of first round don't get promoted and have null scores in all other rounds but the first
+ assertEquals(36.5, leaderboard.getTotalPoints(c[60], later), 0.000000001);
+ assertEquals(44.5, leaderboard.getTotalPoints(c[61], later), 0.000000001);
+ assertEquals(52.5, leaderboard.getTotalPoints(c[62], later), 0.000000001);
+ assertEquals(60.5, leaderboard.getTotalPoints(c[63], later), 0.000000001);
+ for (int i=1; i<=3; i++) {
+ for (int j=60; j<=63; j++) {
+ assertNull(leaderboard.getTotalPoints(c[j], series.get(i).getRaceColumns().iterator().next(), later));
+ }
+ }
+ }
+
+ @Test
+ public void testMultiElminationScoringScheme() throws NoWindException {
+ final int NUMBER_OF_ELIMINATIONS = 3;
+ Regatta regatta = createRegattaWithEliminations(NUMBER_OF_ELIMINATIONS, new int[] { 8, 4, 2, 2 }, "testBasicElminationScoringScheme",
+ DomainFactory.INSTANCE.getOrCreateBoatClass("49er", /* typicallyStartsUpwind */true),
+ DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT_WITH_ELIMINATIONS_AND_ROUNDS_WINNER_GETS_07));
+ // also test that no discards occur for three eliminations when discards start to apply only with four races completed
+ Leaderboard leaderboard = createLeaderboard(regatta, /* discarding thresholds */ new int[] { NUMBER_OF_ELIMINATIONS + 1 });
+ Competitor[] c = createCompetitors(64).toArray(new Competitor[64]);
+ TimePoint later = null;
+ for (int elimination=0; elimination=NUMBER_OF_ELIMINATIONS-1; numberOfEliminationsExpectedToScore--) {
+ assertEquals(numberOfEliminationsExpectedToScore*18.5, leaderboard.getTotalPoints(c[ 8], later), 0.000000001);
+ assertEquals(numberOfEliminationsExpectedToScore*22.5, leaderboard.getTotalPoints(c[ 9], later), 0.000000001);
+ assertEquals(numberOfEliminationsExpectedToScore*26.5, leaderboard.getTotalPoints(c[10], later), 0.000000001);
+ assertEquals(numberOfEliminationsExpectedToScore*30.5, leaderboard.getTotalPoints(c[11], later), 0.000000001);
+ // last four competitors in last heat of first round don't get promoted and have null scores in all other rounds but the first
+ assertEquals(numberOfEliminationsExpectedToScore*36.5, leaderboard.getTotalPoints(c[60], later), 0.000000001);
+ assertEquals(numberOfEliminationsExpectedToScore*44.5, leaderboard.getTotalPoints(c[61], later), 0.000000001);
+ assertEquals(numberOfEliminationsExpectedToScore*52.5, leaderboard.getTotalPoints(c[62], later), 0.000000001);
+ assertEquals(numberOfEliminationsExpectedToScore*60.5, leaderboard.getTotalPoints(c[63], later), 0.000000001);
+ List rankedCompetitors = leaderboard.getCompetitorsFromBestToWorst(later);
+ assertSame(c[0], rankedCompetitors.get(0)); // should be the winner of the final round's Final heat and take the "crown" for the elimination
+ assertEquals(numberOfEliminationsExpectedToScore*0.7, leaderboard.getTotalPoints(c[0], later), 0.000000001);
+ assertSame(c[1], rankedCompetitors.get(1)); // should be the winner of the final round's Final heat and take the "crown" for the elimination
+ assertEquals(numberOfEliminationsExpectedToScore*2, leaderboard.getTotalPoints(c[1], later), 0.000000001);
+ for (int elimination=0; elimination rankedCompetitors = leaderboard.getCompetitorsFromBestToWorst(later);
+ // Clarified with Juergen Bonne in an e-mail as of 18-09-2015T09:03:00Z that a final race's winner
+ // is scored with 0.7 only if the final race has actually been sailed. If the competitors are qualified
+ // for the final race but it's not sailed, average scores are to be assigned to all competitors qualified
+ // for the final race, but this average assumes 1.0 points for the first rank instead of the 0.7 assigned
+ // to the winner if the race is actually sailed. Similarly, if not even the semi-finals have been sailed,
+ // score averaging uses 1.0 for the first rank.
+ final double expectedPointsForFirstEightBoats = (1.0 + 2.0 + 3.0 + 4.0 + 5.0 + 6.0 + 7.0 + 8.0) / 8.0;
+ for (int i=0; i<8; i++) {
+ assertEquals(expectedPointsForFirstEightBoats, leaderboard.getTotalPoints(c[8*i], later), 0.0000001);
+ assertEquals("Competitor "+rankedCompetitors.get(i)+" not in list of best eight ",
+ 0, Arrays.asList(c).indexOf(rankedCompetitors.get(i))%8); // each first competitor in a heat ranks in the top 8
+ }
+ for (int i=1; i<=3; i++) {
+ for (Competitor comp : c) {
+ assertNull(leaderboard.getTotalPoints(comp, series.get(i).getRaceColumns().iterator().next(), later));
+ }
+ }
+ }
+
@Test
public void testScoringConsideringNotAllRaces() throws NoWindException {
// one discard at four races
@@ -710,7 +1086,7 @@ public class LeaderboardScoringAndRankingTest extends AbstractLeaderboardTest {
DomainFactory.INSTANCE.getOrCreateBoatClass("49er", /* typicallyStartsUpwind */true),
DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT));
Leaderboard leaderboard = createLeaderboard(regatta, /* discarding thresholds */ new int[] { 4 });
- TimePoint later = createAndAttachTrackedRaces(series.get(1), "Default", f1, f2, f3, f4, f5, f6);
+ TimePoint later = createAndAttachTrackedRaces(series.get(1), "Default", /* withScores */ true, f1, f2, f3, f4, f5, f6);
Map> rankedCompetitorsFromBestToWorstAfterEachRaceColumn =
leaderboard.getRankedCompetitorsFromBestToWorstAfterEachRaceColumn(later);
assertEquals(Arrays.asList(c[2], c[0], c[1], c[3]),
@@ -763,8 +1139,8 @@ public class LeaderboardScoringAndRankingTest extends AbstractLeaderboardTest {
/* medal */ true, "testTieBreakWithTwoVersusOneSeconds",
DomainFactory.INSTANCE.getOrCreateBoatClass("49er", /* typicallyStartsUpwind */true), DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT));
Leaderboard leaderboard = createLeaderboard(regatta, /* discarding thresholds */ new int[0]);
- TimePoint later = createAndAttachTrackedRaces(series.get(1), "Default", f1, f2);
- createAndAttachTrackedRaces(series.get(2), "Medal", m1);
+ TimePoint later = createAndAttachTrackedRaces(series.get(1), "Default", /* withScores */ true, f1, f2);
+ createAndAttachTrackedRaces(series.get(2), "Medal", /* withScores */ true, m1);
List rankedCompetitors = leaderboard.getCompetitorsFromBestToWorst(later);
// assert that both have equal score
assertEquals(leaderboard.getTotalPoints(c[0], later), leaderboard.getTotalPoints(c[1], later), 0.000000001);
@@ -786,7 +1162,7 @@ public class LeaderboardScoringAndRankingTest extends AbstractLeaderboardTest {
DomainFactory.INSTANCE.getOrCreateBoatClass("49er", /* typicallyStartsUpwind */true),
DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT));
Leaderboard leaderboard = createLeaderboard(regatta, /* discarding thresholds */ new int[0]);
- TimePoint later = createAndAttachTrackedRaces(series.get(1), "Default", f1, f2, f3, f4, f5, f6);
+ TimePoint later = createAndAttachTrackedRaces(series.get(1), "Default", /* withScores */ true, f1, f2, f3, f4, f5, f6);
List rankedCompetitors = leaderboard.getCompetitorsFromBestToWorst(later);
assertEquals(leaderboard.getTotalPoints(c[0], later), leaderboard.getTotalPoints(c[1], later), 0.000000001);
assertEquals(rankedCompetitors.indexOf(c[0]), rankedCompetitors.indexOf(c[1])-1);
@@ -806,7 +1182,7 @@ public class LeaderboardScoringAndRankingTest extends AbstractLeaderboardTest {
DomainFactory.INSTANCE.getOrCreateBoatClass("49er", /* typicallyStartsUpwind */true),
DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.HIGH_POINT));
Leaderboard leaderboard = createLeaderboard(regatta, /* discarding thresholds */ new int[0]);
- TimePoint later = createAndAttachTrackedRaces(series.get(1), "Default", f1, f2, f3, f4, f5, f6);
+ TimePoint later = createAndAttachTrackedRaces(series.get(1), "Default", /* withScores */ true, f1, f2, f3, f4, f5, f6);
List rankedCompetitors = leaderboard.getCompetitorsFromBestToWorst(later);
assertEquals(leaderboard.getTotalPoints(c[0], later), leaderboard.getTotalPoints(c[1], later), 0.000000001);
assertEquals(rankedCompetitors.indexOf(c[0]), rankedCompetitors.indexOf(c[1])-1);
@@ -1268,13 +1644,18 @@ public class LeaderboardScoringAndRankingTest extends AbstractLeaderboardTest {
assertEquals(0., leaderboard1.getTotalPoints(c[3], beforeStartOfR1), 0.00000001); // not even the R1 scores apply before the start time of R1
}
- private TimePoint createAndAttachTrackedRaces(Series theSeries, String fleetName, Competitor[]... competitorLists) {
+ private TimePoint createAndAttachTrackedRaces(Series theSeries, String fleetName, boolean withScores, Competitor[]... competitorLists) {
TimePoint now = MillisecondsTimePoint.now();
TimePoint later = new MillisecondsTimePoint(now.asMillis()+1000);
Iterator extends RaceColumn> columnIter = theSeries.getRaceColumns().iterator();
for (Competitor[] competitorList : competitorLists) {
RaceColumn raceColumn = columnIter.next();
- TrackedRace trackedRace = new MockedTrackedRaceWithStartTimeAndRanks(now, Arrays.asList(competitorList));
+ final TrackedRace trackedRace;
+ if (withScores) {
+ trackedRace = new MockedTrackedRaceWithStartTimeAndRanks(now, Arrays.asList(competitorList));
+ } else {
+ trackedRace = new MockedTrackedRaceWithStartTimeAndZeroRanks(now, Arrays.asList(competitorList));
+ }
raceColumn.setTrackedRace(raceColumn.getFleetByName(fleetName), trackedRace);
}
return later;
@@ -1363,6 +1744,52 @@ public class LeaderboardScoringAndRankingTest extends AbstractLeaderboardTest {
return regatta;
}
+ private Regatta createRegattaWithEliminations(final int numberOfEliminations, final int[] numbersOfHeatsPerRound,
+ final String regattaBaseName, BoatClass boatClass, ScoringScheme scoringScheme) {
+ series = new ArrayList();
+ // example for numbersOfHeatsPerRound: [8, 4, 2, 2]
+ for (int elimination=1; elimination<=numberOfEliminations; elimination++) {
+ int heatNumber = 1;
+ // create one elimination consisting of a number of rounds, each consisting of a number of heats
+ int roundNumber = 1;
+ for (int numberOfHeatsPerRound : numbersOfHeatsPerRound) {
+ final boolean isFinalRound = roundNumber == numbersOfHeatsPerRound.length;
+ // create one round as a series that has one fleet per heat
+ List fleetsInRound = new ArrayList();
+ for (int heatInRound=1; heatInRound<=numberOfHeatsPerRound; heatInRound++) {
+ final int ordering = numbersOfHeatsPerRound.length-roundNumber+1+
+ // in final round distinguish Final and Losers Final
+ (isFinalRound ? heatInRound-1 : 1);
+ fleetsInRound.add(new FleetImpl("Heat "+(heatNumber++), ordering));
+ }
+ List raceColumnNameForRound = new ArrayList();
+ raceColumnNameForRound.add("E"+elimination+"R"+roundNumber);
+ final String roundName;
+ if (numbersOfHeatsPerRound.length-roundNumber == 2) {
+ roundName = "Quarter-Final";
+ } else if (numbersOfHeatsPerRound.length-roundNumber == 1) {
+ roundName = "Semi-Final";
+ } else if (numbersOfHeatsPerRound.length-roundNumber == 0) {
+ roundName = "Final";
+ } else {
+ roundName = "Round "+roundNumber;
+ }
+ Series seriesForRound = new SeriesImpl("E"+elimination+" "+roundName, /* isMedal */ false, fleetsInRound,
+ raceColumnNameForRound, /* trackedRegattaRegistry */null);
+ if (isFinalRound) {
+ // last "Final" round; here, the fleets are contiguously scored
+ seriesForRound.setSplitFleetContiguousScoring(true);
+ }
+ series.add(seriesForRound);
+ roundNumber++;
+ }
+ }
+ Regatta regatta = new RegattaImpl(RegattaImpl.getDefaultName(regattaBaseName, boatClass.getName()), boatClass,
+ /*startDate*/ null, /*endDate*/ null, series, /* persistent */ false, scoringScheme,
+ /* ID */ "123", /* course area */ null, OneDesignRankingMetric::new);
+ return regatta;
+ }
+
/**
* See bug 1260. There must be a possibility to have ordered fleets that are scored such that the winner of the race of the best fleet
* gets the best score in that column; and the winner of the second-best fleet gets the n-th best score in the column with n being the
diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/WindSteppingTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/WindSteppingTest.java
index 55ff34a60d1..4d01be47c38 100644
--- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/WindSteppingTest.java
+++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/WindSteppingTest.java
@@ -24,7 +24,7 @@ public class WindSteppingTest {
@Test
public void testSteppingWithMaxDistance() {
double[] levels = {2.,4.};
- WindSpeedStepping stepping = new WindSpeedSteppingWithMaxDistance(levels, 1.0);
+ WindSpeedSteppingWithMaxDistance stepping = new WindSpeedSteppingWithMaxDistance(levels, 1.0);
Assert.assertEquals(0, stepping.getLevelIndexForValue(1));
Assert.assertEquals(1, stepping.getLevelIndexForValue(5));
Assert.assertEquals(-1, stepping.getLevelIndexForValue(5.01));
@@ -32,6 +32,8 @@ public class WindSteppingTest {
Assert.assertEquals(-1, stepping.getLevelIndexForValue(8));
Assert.assertEquals(2.0, stepping.getSteppedValueForValue(1.064), 0.005);
Assert.assertEquals(-1.0, stepping.getSteppedValueForValue(7.8365), 0.005);
+ Assert.assertEquals(1.55, stepping.getHistogramXValue(20, 1.5));
+ Assert.assertEquals(2.55, stepping.getHistogramXValue(20, 2.5));
}
}
diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRace.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRace.java
index 7eac79db332..6b4dabf7c9c 100755
--- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRace.java
+++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRace.java
@@ -50,6 +50,7 @@ import com.sap.sailing.domain.racelog.tracking.GPSFixStore;
import com.sap.sailing.domain.ranking.RankingMetric;
import com.sap.sailing.domain.ranking.RankingMetric.RankingInfo;
import com.sap.sailing.domain.ranking.RankingMetricConstructor;
+import com.sap.sailing.domain.regattalike.IsRegattaLike;
import com.sap.sailing.domain.regattalike.RegattaLikeIdentifier;
import com.sap.sailing.domain.regattalike.RegattaLikeListener;
import com.sap.sailing.domain.tracking.CourseDesignChangedListener;
@@ -472,6 +473,21 @@ public class MockedTrackedRace implements DynamicTrackedRace {
public RaceColumn getRaceColumnByName(String raceColumnName) {
return null;
}
+
+ @Override
+ public IsRegattaLike getRegattaLike() {
+ return null;
+ }
+
+ @Override
+ public RaceLog getRacelog(String raceColumnName, String fleetName) {
+ return null;
+ }
+
+ @Override
+ public Iterable extends RaceColumn> getRaceColumns() {
+ return null;
+ }
};
}
diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRaceWithStartTimeAndZeroRanks.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRaceWithStartTimeAndZeroRanks.java
new file mode 100644
index 00000000000..9576f2badc4
--- /dev/null
+++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRaceWithStartTimeAndZeroRanks.java
@@ -0,0 +1,40 @@
+package com.sap.sailing.domain.test.mock;
+
+import java.util.List;
+
+import com.sap.sailing.domain.base.Competitor;
+import com.sap.sailing.domain.base.Regatta;
+import com.sap.sailing.domain.common.NoWindException;
+import com.sap.sse.common.TimePoint;
+
+/**
+ * Returns 0 for {@link #getRank(Competitor)} and {@link #getRank(Competitor, TimePoint)} in all cases. Still, the competitor
+ * set participating in this race is maintained. This can be used, e.g., to mock a race that hasn't started yet and therefore
+ * the competitors have no rank yet.
+ *
+ * @author Axel Uhl (d043530)
+ *
+ */
+public class MockedTrackedRaceWithStartTimeAndZeroRanks extends MockedTrackedRaceWithStartTimeAndRanks {
+ private static final long serialVersionUID = 7726627832998127727L;
+
+ protected MockedTrackedRaceWithStartTimeAndZeroRanks(TimePoint startTime,
+ List competitors, Regatta regatta) {
+ super(startTime, competitors, regatta);
+ }
+
+ public MockedTrackedRaceWithStartTimeAndZeroRanks(TimePoint startTime, List competitors) {
+ super(startTime, competitors);
+ }
+
+ @Override
+ public int getRank(Competitor competitor) throws NoWindException {
+ return 0;
+ }
+
+ @Override
+ public int getRank(Competitor competitor, TimePoint timePoint) {
+ return 0;
+ }
+
+}
diff --git a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/DomainFactoryImpl.java b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/DomainFactoryImpl.java
index 016526588f6..58f961deb91 100755
--- a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/DomainFactoryImpl.java
+++ b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/DomainFactoryImpl.java
@@ -29,7 +29,6 @@ import com.sap.sailing.domain.base.ControlPoint;
import com.sap.sailing.domain.base.Course;
import com.sap.sailing.domain.base.Mark;
import com.sap.sailing.domain.base.Nationality;
-import com.sap.sailing.domain.base.Person;
import com.sap.sailing.domain.base.RaceDefinition;
import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.base.Sideline;
@@ -106,8 +105,6 @@ public class DomainFactoryImpl implements DomainFactory {
private final Map, DynamicPerson> personCache = new HashMap<>();
- private final Map teamCache = new HashMap<>();
-
/**
* Caches regattas by their name and their boat class's name
*/
@@ -263,7 +260,7 @@ public class DomainFactoryImpl implements DomainFactory {
nationality = null;
logger.log(Level.SEVERE, "Unknown nationality "+nationalityAsString+" for competitor "+name+"; leaving null", iae);
}
- DynamicTeam team = getOrCreateTeam(name, nationality, competitorId);
+ DynamicTeam team = createTeam(name, nationality, competitorId);
DynamicBoat boat = new BoatImpl(shortName, boatClass, shortName);
result = competitorStore.getOrCreateCompetitor(competitorId, name, null /* displayColor */,
null /* email */, null /* flagImag */, team, boat, (double) timeOnTimeFactor,
@@ -272,27 +269,15 @@ public class DomainFactoryImpl implements DomainFactory {
return result;
}
- /**
- * If a team called name already is known by this domain factory, it is returned. Otherwise, the team name
- * is split along "+" signs with one {@link Person} object created for each part. If an existing team is found, its
- * nationality will be updated to match nationality.
- */
- private DynamicTeam getOrCreateTeam(String name, Nationality nationality, UUID competitorId) {
- synchronized (teamCache) {
- DynamicTeam result = teamCache.get(competitorId);
- if (result == null) {
- String[] sailorNames = name.split("\\b*\\+\\b*");
- List sailors = new ArrayList();
- for (String sailorName : sailorNames) {
- sailors.add(getOrCreatePerson(sailorName.trim(), nationality, competitorId));
- }
- result = new TeamImpl(name, sailors, /* TODO coach not known */null);
- teamCache.put(competitorId, result);
- } else {
- result.setNationality(nationality);
- }
- return result;
+ private DynamicTeam createTeam(String name, Nationality nationality, UUID competitorId) {
+ DynamicTeam result;
+ String[] sailorNames = name.split("\\b*\\+\\b*");
+ List sailors = new ArrayList();
+ for (String sailorName : sailorNames) {
+ sailors.add(getOrCreatePerson(sailorName.trim(), nationality, competitorId));
}
+ result = new TeamImpl(name, sailors, /* TODO coach not known */null);
+ return result;
}
@Override
diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/RaceColumn.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/RaceColumn.java
index 64a34b1f1fc..73d88aa7405 100644
--- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/RaceColumn.java
+++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/RaceColumn.java
@@ -1,6 +1,7 @@
package com.sap.sailing.domain.base;
import com.sap.sailing.domain.abstractlog.race.RaceLog;
+import com.sap.sailing.domain.abstractlog.regatta.RegattaLog;
import com.sap.sailing.domain.common.RaceIdentifier;
import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.leaderboard.MetaLeaderboard;
@@ -227,4 +228,18 @@ public interface RaceColumn extends Named {
boolean hasSplitFleets();
RaceExecutionOrderProvider getRaceExecutionOrderProvider();
+
+ /**
+ * Provides the combined set of competitors from all {@link #getTrackedRace(Fleet) tracked races attached to this
+ * column} or, in case a fleet does not have a tracked race attached, the competitors registered through the
+ * respective {@link RaceLog} {@link #getRaceLog(Fleet) attached to this column for that fleet}. Note that this does
+ * not include competitors registered through any {@link RegattaLog} on the structure owning (directly or
+ * transitively) this column.
+ */
+ Iterable getAllCompetitors();
+
+ /**
+ * Same as {@link #getAllCompetitors()}, but restricted to the single race identified by the fleet parameter.
+ */
+ Iterable getAllCompetitors(Fleet fleet);
}
diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/Regatta.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/Regatta.java
index 6415cd5901f..49056157559 100755
--- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/Regatta.java
+++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/Regatta.java
@@ -4,6 +4,7 @@ import com.sap.sailing.domain.base.configuration.RegattaConfiguration;
import com.sap.sailing.domain.common.RankingMetrics;
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
import com.sap.sailing.domain.common.RegattaIdentifier;
+import com.sap.sailing.domain.leaderboard.HasRaceColumnsAndRegattaLike;
import com.sap.sailing.domain.leaderboard.ScoringScheme;
import com.sap.sailing.domain.ranking.RankingMetricConstructor;
import com.sap.sailing.domain.ranking.RankingMetricsFactory;
@@ -22,7 +23,7 @@ import com.sap.sse.common.WithID;
* @author Axel Uhl (d043530)
*
*/
-public interface Regatta extends Named, WithID, IsRegattaLike {
+public interface Regatta extends Named, WithID, IsRegattaLike, HasRaceColumnsAndRegattaLike {
ScoringScheme getScoringScheme();
/**
diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/AbstractRaceColumn.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/AbstractRaceColumn.java
index ae4152f9869..e03614bd77e 100755
--- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/AbstractRaceColumn.java
+++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/AbstractRaceColumn.java
@@ -3,11 +3,14 @@ package com.sap.sailing.domain.base.impl;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
+import java.util.Set;
import java.util.logging.Logger;
import com.sap.sailing.domain.abstractlog.race.RaceLog;
+import com.sap.sailing.domain.abstractlog.shared.analyzing.RegisteredCompetitorsAnalyzer;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.RaceColumn;
@@ -43,7 +46,7 @@ public abstract class AbstractRaceColumn extends SimpleAbstractRaceColumn implem
this.raceLogStore = raceLogStore;
this.regattaLikeParent = regattaLikeParent;
for (final Fleet fleet : getFleets()) {
- reloadRaceLog(fleet);
+ reloadRaceLog(fleet);
}
}
@@ -117,9 +120,9 @@ public abstract class AbstractRaceColumn extends SimpleAbstractRaceColumn implem
@Override
public Fleet getFleetOfCompetitor(Competitor competitor) {
- for (Map.Entry e : trackedRaces.entrySet()) {
- if (Util.contains(e.getValue().getRace().getCompetitors(), competitor)) {
- return e.getKey();
+ for (final Fleet fleet : getFleets()) {
+ if (Util.contains(getAllCompetitors(fleet), competitor)) {
+ return fleet;
}
}
return null;
@@ -206,4 +209,41 @@ public abstract class AbstractRaceColumn extends SimpleAbstractRaceColumn implem
public void setMasterDataExportOngoingThreadFlag(boolean flagValue) {
trackedRaces.setMasterDataExportOngoingThreadFlag(flagValue);
}
+
+
+ @Override
+ public Iterable getAllCompetitors() {
+ Set result = new HashSet<>();
+ for (Fleet fleet : getFleets()) {
+ TrackedRace trackedRace = getTrackedRace(fleet);
+ if (trackedRace != null) {
+ Util.addAll(trackedRace.getRace().getCompetitors(), result);
+ } else {
+ // if no tracked race is found, use competitors from race log; this assumes that if a tracked
+ // race exists, its competitors set takes precedence over what's in the race log. Usually,
+ // the tracked race will have the same competitors as those in the race log, or more because
+ // those from the regatta log are added to the tracked race as well.
+ Set viaRaceLog = new RegisteredCompetitorsAnalyzer<>(getRaceLog(fleet)).analyze();
+ result.addAll(viaRaceLog);
+ }
+ }
+ return result;
+ }
+
+ @Override
+ public Iterable getAllCompetitors(final Fleet fleet) {
+ final Iterable result;
+ TrackedRace trackedRace = getTrackedRace(fleet);
+ if (trackedRace != null) {
+ result = trackedRace.getRace().getCompetitors();
+ } else {
+ // if no tracked race is found, use competitors from race log; this assumes that if a tracked
+ // race exists, its competitors set takes precedence over what's in the race log. Usually,
+ // the tracked race will have the same competitors as those in the race log, or more because
+ // those from the regatta log are added to the tracked race as well.
+ Set viaRaceLog = new RegisteredCompetitorsAnalyzer<>(getRaceLog(fleet)).analyze();
+ result = viaRaceLog;
+ }
+ return result;
+ }
}
diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/DomainFactoryImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/DomainFactoryImpl.java
index 2f3a8e712a1..51bf63b2070 100755
--- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/DomainFactoryImpl.java
+++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/DomainFactoryImpl.java
@@ -50,6 +50,7 @@ import com.sap.sailing.domain.leaderboard.impl.HighPointWinnerGetsFive;
import com.sap.sailing.domain.leaderboard.impl.HighPointWinnerGetsSix;
import com.sap.sailing.domain.leaderboard.impl.LowPoint;
import com.sap.sailing.domain.leaderboard.impl.LowPointWinnerGetsZero;
+import com.sap.sailing.domain.leaderboard.impl.LowPointWithEliminationsAndRoundsWinnerGets07;
import com.sap.sailing.domain.tracking.GPSFixTrack;
import com.sap.sailing.domain.tracking.MarkPassing;
import com.sap.sailing.domain.tracking.TrackedRace;
@@ -113,6 +114,8 @@ public class DomainFactoryImpl extends SharedDomainFactoryImpl implements Domain
return new HighPointWinnerGetsEightAndInterpolation();
case HIGH_POINT_FIRST_GETS_TEN_OR_EIGHT:
return new HighPointFirstGets10Or8AndLastBreaksTie();
+ case LOW_POINT_WITH_ELIMINATIONS_AND_ROUNDS_WINNER_GETS_07:
+ return new LowPointWithEliminationsAndRoundsWinnerGets07();
}
throw new RuntimeException("Unknown scoring scheme type "+scoringSchemeType.name());
}
diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/RegattaImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/RegattaImpl.java
index 32e5547549e..cf30214a41b 100755
--- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/RegattaImpl.java
+++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/RegattaImpl.java
@@ -16,9 +16,9 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
+import com.sap.sailing.domain.abstractlog.race.RaceLog;
import com.sap.sailing.domain.abstractlog.race.RaceLogEvent;
import com.sap.sailing.domain.abstractlog.regatta.RegattaLog;
-import com.sap.sailing.domain.abstractlog.shared.analyzing.RegisteredCompetitorsAnalyzer;
import com.sap.sailing.domain.base.BoatClass;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.CourseArea;
@@ -40,6 +40,7 @@ import com.sap.sailing.domain.common.RegattaName;
import com.sap.sailing.domain.common.RegattaNameAndRaceName;
import com.sap.sailing.domain.leaderboard.ResultDiscardingRule;
import com.sap.sailing.domain.leaderboard.ScoringScheme;
+import com.sap.sailing.domain.leaderboard.impl.CompetitorProviderFromRaceColumnsAndRegattaLike;
import com.sap.sailing.domain.racelog.RaceLogIdentifier;
import com.sap.sailing.domain.racelog.RaceLogStore;
import com.sap.sailing.domain.racelog.impl.EmptyRaceLogStore;
@@ -111,6 +112,8 @@ public class RegattaImpl extends NamedImpl implements Regatta, RaceColumnListene
* Defaults to true. See {@link Regatta#useStartTimeInference()}.
*/
private boolean useStartTimeInference;
+
+ private transient CompetitorProviderFromRaceColumnsAndRegattaLike competitorsProvider;
/**
* Constructs a regatta with an empty {@link RaceLogStore}.
@@ -171,7 +174,6 @@ public class RegattaImpl extends NamedImpl implements Regatta, RaceColumnListene
* @param series
* all {@link Series} in this iterable will have their {@link Series#setRegatta(Regatta) regatta set} to
* this new regatta.
- * @param rankingMetricConstructor TODO
*/
public RegattaImpl(RaceLogStore raceLogStore, RegattaLogStore regattaLogStore,
String name, BoatClass boatClass, TimePoint startDate, TimePoint endDate, Iterable series, boolean persistent, ScoringScheme scoringScheme,
@@ -272,8 +274,8 @@ public class RegattaImpl extends NamedImpl implements Regatta, RaceColumnListene
/**
* {@link RaceColumnListeners} may not be de-serialized (yet) when the regatta
- * is de-serialized. Do avoid re-registering empty objects most probably leading
- * to null pointer exception one need to initialize all listeners after
+ * is de-serialized. To avoid re-registering empty objects most probably leading
+ * to null pointer exception one needs to initialize all listeners after
* all objects have been read.
*/
public void initializeSeriesAfterDeserialize() {
@@ -363,24 +365,15 @@ public class RegattaImpl extends NamedImpl implements Regatta, RaceColumnListene
@Override
public Iterable getAllCompetitors() {
Set result = new HashSet();
+ if (competitorsProvider == null) {
+ competitorsProvider = new CompetitorProviderFromRaceColumnsAndRegattaLike(this);
+ }
+ Util.addAll(competitorsProvider.getAllCompetitors(), result);
for (RaceDefinition race : getAllRaces()) {
for (Competitor c : race.getCompetitors()) {
result.add(c);
}
}
- for (Series series : getSeries()) {
- for (RaceColumn rc : series.getRaceColumns()) {
- for (Fleet fleet : rc.getFleets()) {
- TrackedRace trackedRace = rc.getTrackedRace(fleet);
- if (trackedRace != null) {
- Util.addAll(trackedRace.getRace().getCompetitors(), result);
- }
- }
- }
- }
- //consider {@link RegattaLog}
- Set viaLog = new RegisteredCompetitorsAnalyzer<>(regattaLikeHelper.getRegattaLog()).analyze();
- result.addAll(viaLog);
return result;
}
@@ -700,4 +693,37 @@ public class RegattaImpl extends NamedImpl implements Regatta, RaceColumnListene
public RaceColumn getRaceColumnByName(String raceColumnName) {
return regattaLikeHelper.getRaceColumnByName(raceColumnName);
}
+
+ @Override
+ public IsRegattaLike getRegattaLike() {
+ return this;
+ }
+
+ @Override
+ public RaceLog getRacelog(String raceColumnName, String fleetName) {
+ final RaceLog result;
+ final RaceColumn raceColumn = getRaceColumnByName(raceColumnName);
+ if (raceColumn == null) {
+ result = null;
+ } else {
+ final Fleet fleet = raceColumn.getFleetByName(fleetName);
+ if (fleet == null) {
+ result = null;
+ } else {
+ result = raceColumn.getRaceLog(fleet);
+ }
+ }
+ return result;
+ }
+
+ @Override
+ public Iterable extends RaceColumn> getRaceColumns() {
+ final List result = new ArrayList<>();
+ for (final Series series : getSeries()) {
+ for (final RaceColumnInSeries rc : series.getRaceColumns()) {
+ result.add(rc);
+ }
+ }
+ return result;
+ }
}
diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/HasRaceColumns.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/HasRaceColumns.java
new file mode 100644
index 00000000000..e66e4765e4f
--- /dev/null
+++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/HasRaceColumns.java
@@ -0,0 +1,12 @@
+package com.sap.sailing.domain.leaderboard;
+
+import com.sap.sailing.domain.base.RaceColumn;
+import com.sap.sailing.domain.base.RaceColumnListener;
+
+public interface HasRaceColumns {
+ Iterable extends RaceColumn> getRaceColumns();
+
+ void addRaceColumnListener(RaceColumnListener listener);
+
+ void removeRaceColumnListener(RaceColumnListener listener);
+}
diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/HasRaceColumnsAndRegattaLike.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/HasRaceColumnsAndRegattaLike.java
new file mode 100644
index 00000000000..0bc3be68e60
--- /dev/null
+++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/HasRaceColumnsAndRegattaLike.java
@@ -0,0 +1,7 @@
+package com.sap.sailing.domain.leaderboard;
+
+import com.sap.sailing.domain.regattalike.HasRegattaLike;
+
+public interface HasRaceColumnsAndRegattaLike extends HasRegattaLike, HasRaceColumns {
+
+}
diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/Leaderboard.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/Leaderboard.java
index e5f14034d65..b423bce0676 100644
--- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/Leaderboard.java
+++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/Leaderboard.java
@@ -44,7 +44,7 @@ import com.sap.sse.common.Util;
* @author Axel Uhl (d043530)
*
*/
-public interface Leaderboard extends LeaderboardBase {
+public interface Leaderboard extends LeaderboardBase, HasRaceColumns {
/**
* If the leaderboard is a "matrix" with the cells being defined by a competitor / race "coordinate,"
* then this interface defines the structure of the "cells."
@@ -93,6 +93,22 @@ public interface Leaderboard extends LeaderboardBase {
* @return all competitors in this leaderboard, including the suppressed ones.
*/
Iterable getAllCompetitors();
+
+ /**
+ * Retrieves all competitors expected to race in the fleet and column specified.
+ * When a {@link TrackedRace} is {@link RaceColumn#getTrackedRace(Fleet) attached} to the race
+ * column for the fleet specified, its competitor set is returned. Otherwise,
+ * the competitors are collected from any other information, such as a regatta log and/or the
+ * race log for the combination of race column and fleet or, in case of a meta-leaderboard,
+ * from the leaderboard represented by the race column.
+ */
+ Iterable getAllCompetitors(RaceColumn raceColumn, Fleet fleet);
+
+ /**
+ * Same as {@link #getAllCompetitors(RaceColumn, Fleet)} with competitors from {@link #getSuppressedCompetitors()}
+ * removed
+ */
+ Iterable getCompetitors(RaceColumn raceColumn, Fleet fleet);
/**
* Convenience method which returns the difference between {@link #getAllCompetitors()} and {@link #getCompetitors()}.
@@ -354,7 +370,7 @@ public interface Leaderboard extends LeaderboardBase {
* that column shall be considered for discarding and counts for determining the number of races so far. Also, if a
* tracked race is connected to the column and has started already, the column is to be considered for discarding
* unless the column has several unordered fleets and not all fleets have started their race yet (see
- * {@link ScoringScheme#isValidInTotalScore(Leaderboard, RaceColumn, TimePoint)}).
+ * {@link ScoringScheme#isValidInTotalScore(Leaderboard, RaceColumn, Competitor, TimePoint)}).
*/
boolean countRaceForComparisonWithDiscardingThresholds(Competitor competitor, RaceColumn raceColumn, TimePoint timePoint);
diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/LeaderboardCacheManager.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/LeaderboardCacheManager.java
index bdb5c767ba1..39636a492e6 100755
--- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/LeaderboardCacheManager.java
+++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/LeaderboardCacheManager.java
@@ -8,7 +8,6 @@ import java.util.Set;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;
-import com.sap.sailing.domain.abstractlog.race.RaceLogEvent;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.CompetitorChangeListener;
import com.sap.sailing.domain.base.Fleet;
@@ -16,9 +15,9 @@ import com.sap.sailing.domain.base.Nationality;
import com.sap.sailing.domain.base.RaceColumn;
import com.sap.sailing.domain.base.RaceColumnListener;
import com.sap.sailing.domain.base.WithNationality;
+import com.sap.sailing.domain.base.impl.RaceColumnListenerWithDefaultAction;
import com.sap.sailing.domain.common.MaxPointsReason;
import com.sap.sailing.domain.leaderboard.caching.LeaderboardCache;
-import com.sap.sailing.domain.racelog.RaceLogIdentifier;
import com.sap.sailing.domain.tracking.RaceChangeListener;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.impl.AbstractRaceChangeListener;
@@ -262,9 +261,14 @@ public class LeaderboardCacheManager {
leaderboard.getScoreCorrection().addScoreCorrectionListener(scoreCorrectionListener);
scoreCorrectionListeners.put(leaderboard, scoreCorrectionListener);
competitorChangeListeners.put(leaderboard, competitorChangeListener);
- final RaceColumnListener raceColumnListener = new RaceColumnListener() {
+ final RaceColumnListener raceColumnListener = new RaceColumnListenerWithDefaultAction() {
private static final long serialVersionUID = 8165124797028386317L;
+ @Override
+ public void defaultAction() {
+ removeFromCache(leaderboard);
+ }
+
@Override
public void trackedRaceLinked(RaceColumn raceColumn, Fleet fleet, TrackedRace trackedRace) {
removeFromCache(leaderboard);
@@ -278,70 +282,6 @@ public class LeaderboardCacheManager {
public boolean isTransient() {
return true;
}
-
- @Override
- public void trackedRaceUnlinked(RaceColumn raceColumn, Fleet fleet, TrackedRace trackedRace) {
- removeFromCache(leaderboard); // removes all listeners from invalidationListenersPerLeaderboard and from their TrackedRaces
- }
-
- @Override
- public void isMedalRaceChanged(RaceColumn raceColumn, boolean newIsMedalRace) {
- removeFromCache(leaderboard);
- }
-
- @Override
- public void isStartsWithZeroScoreChanged(RaceColumn raceColumn, boolean newIsStartsWithZeroScore) {
- removeFromCache(leaderboard);
- }
-
- @Override
- public void hasSplitFleetContiguousScoringChanged(RaceColumn raceColumn, boolean hasSplitFleetContiguousScoring) {
- removeFromCache(leaderboard);
- }
-
- @Override
- public void isFirstColumnIsNonDiscardableCarryForwardChanged(RaceColumn raceColumn,
- boolean firstColumnIsNonDiscardableCarryForward) {
- removeFromCache(leaderboard);
- }
-
- @Override
- public void raceColumnAddedToContainer(RaceColumn raceColumn) {
- removeFromCache(leaderboard);
- }
-
- @Override
- public void raceColumnRemovedFromContainer(RaceColumn raceColumn) {
- removeFromCache(leaderboard);
- }
-
- @Override
- public void raceColumnMoved(RaceColumn raceColumn, int newIndex) {
- removeFromCache(leaderboard);
- }
-
- @Override
- public void factorChanged(RaceColumn raceColumn, Double oldFactor, Double newFactor) {
- removeFromCache(leaderboard);
- }
-
- @Override
- public void resultDiscardingRuleChanged(ResultDiscardingRule oldDiscardingRule,
- ResultDiscardingRule newDiscardingRule) {
- removeFromCache(leaderboard);
- }
-
- @Override
- public void competitorDisplayNameChanged(Competitor competitor, String oldDisplayName,
- String displayName) {
- removeFromCache(leaderboard);
- }
-
- @Override
- public void raceLogEventAdded(RaceColumn raceColumn, RaceLogIdentifier raceLogIdentifier,
- RaceLogEvent event) {
- removeFromCache(leaderboard);
- }
};
leaderboard.addRaceColumnListener(raceColumnListener);
synchronized (raceColumnListeners) {
diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/ScoringScheme.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/ScoringScheme.java
index 3265f108ac5..9badb203b79 100755
--- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/ScoringScheme.java
+++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/ScoringScheme.java
@@ -50,10 +50,11 @@ public interface ScoringScheme extends Serializable {
* If the competitor has no {@link RaceColumn#getTrackedRace(Competitor) tracked race} in the column in which
* the competitor participated, null is returned, meaning the competitor has no score assigned for that
* race.
+ * @param timePoint TODO
*/
- Double getScoreForRank(RaceColumn raceColumn, Competitor competitor, int rank,
- Callable numberOfCompetitorsInRaceFetcher,
- NumberOfCompetitorsInLeaderboardFetcher numberOfCompetitorsInLeaderboardFetcher);
+ Double getScoreForRank(Leaderboard leaderboard, RaceColumn raceColumn, Competitor competitor,
+ int rank, Callable numberOfCompetitorsInRaceFetcher,
+ NumberOfCompetitorsInLeaderboardFetcher numberOfCompetitorsInLeaderboardFetcher, TimePoint timePoint);
/**
* If a competitor is disqualified, a penalty score is attributed by this scoring scheme. Some schemes require to
@@ -94,9 +95,11 @@ public interface ScoringScheme extends Serializable {
* Under certain circumstances, a scoring scheme may decide that the scores of a column are not (yet) to be used
* for the leaderboard's total scores. This may, e.g., be the case if a column is split into more than one fleet and
* those fleets are unordered. In that case, scores need to be available for all fleets before the column counts
- * for the total scores.
+ * for the total scores. Another example is a scoring scheme that defines elimination rounds and awards no points
+ * to a competitor in a round from which the competitor got promoted to the next round. Such promotion columns
+ * then have no scores and don't count in the number of races starting from where discards are applied.
*/
- boolean isValidInTotalScore(Leaderboard leaderboard, RaceColumn raceColumn, TimePoint at);
+ boolean isValidInTotalScore(Leaderboard leaderboard, RaceColumn raceColumn, Competitor competitor, TimePoint at);
/**
* Some scoring schemes are applied to {@link LeaderboardGroupMetaLeaderboard} instances. These instances of a
diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractLeaderboardImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractLeaderboardImpl.java
index 7cee58bf028..e5947108d43 100755
--- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractLeaderboardImpl.java
+++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractLeaderboardImpl.java
@@ -12,9 +12,9 @@ import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.RaceColumn;
import com.sap.sailing.domain.base.RaceColumnListener;
import com.sap.sailing.domain.common.MaxPointsReason;
+import com.sap.sailing.domain.leaderboard.HasRaceColumnsAndRegattaLike;
import com.sap.sailing.domain.leaderboard.ScoreCorrection;
import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule;
-import com.sap.sailing.domain.regattalike.HasRegattaLike;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sse.common.TimePoint;
@@ -28,14 +28,14 @@ import com.sap.sse.common.TimePoint;
* @author Axel Uhl (D043530)
*
*/
-public abstract class AbstractLeaderboardImpl extends AbstractSimpleLeaderboardImpl implements HasRegattaLike{
+public abstract class AbstractLeaderboardImpl extends AbstractSimpleLeaderboardImpl implements HasRaceColumnsAndRegattaLike {
private static final long serialVersionUID = -328091952760083438L;
-
+
/**
* Cache for the combined competitors of this leaderboard; taken from the {@link TrackedRace#getRace() races of the
* tracked races} associated with this leaderboard. Updated when the set of tracked races changes.
*/
- private transient Iterable allCompetitorsCache;
+ private transient CompetitorProviderFromRaceColumnsAndRegattaLike competitorsProvider;
/**
* @param scoreComparator the comparator to use to compare basic scores, such as net points
@@ -77,20 +77,21 @@ public abstract class AbstractLeaderboardImpl extends AbstractSimpleLeaderboardI
*/
@Override
public Iterable getAllCompetitors() {
- if (allCompetitorsCache == null) {
- Set result = new HashSet();
- synchronized (this) {
- for (TrackedRace r : getTrackedRaces()) {
- for (Competitor c : r.getRace().getCompetitors()) {
- result.add(c);
- }
- }
- }
- allCompetitorsCache = result;
- }
- return allCompetitorsCache;
+ return getOrCreateCompetitorsProvider().getAllCompetitors();
}
+ @Override
+ public Iterable getAllCompetitors(RaceColumn raceColumn, Fleet fleet) {
+ return getOrCreateCompetitorsProvider().getAllCompetitors(raceColumn, fleet);
+ }
+
+ private CompetitorProviderFromRaceColumnsAndRegattaLike getOrCreateCompetitorsProvider() {
+ if (competitorsProvider == null) {
+ competitorsProvider = new CompetitorProviderFromRaceColumnsAndRegattaLike(this);
+ }
+ return competitorsProvider;
+ }
+
@Override
public Competitor getCompetitorByIdAsString(String idAsString) {
for (Competitor competitor : getAllCompetitors()) {
@@ -144,18 +145,6 @@ public abstract class AbstractLeaderboardImpl extends AbstractSimpleLeaderboardI
return correctedRank;
}
- @Override
- public void trackedRaceLinked(RaceColumn raceColumn, Fleet fleet, TrackedRace trackedRace) {
- allCompetitorsCache = null;
- super.trackedRaceLinked(raceColumn, fleet, trackedRace);
- }
-
- @Override
- public void trackedRaceUnlinked(RaceColumn raceColumn, Fleet fleet, TrackedRace trackedRace) {
- allCompetitorsCache = null;
- super.trackedRaceUnlinked(raceColumn, fleet, trackedRace);
- }
-
// Note: no need to redefine isMedalRaceChanged because that doesn't affect the competitorsCache
@Override
@@ -182,4 +171,5 @@ public abstract class AbstractLeaderboardImpl extends AbstractSimpleLeaderboardI
Fleet fleet = raceColumn.getFleetByName(fleetName);
return raceColumn.getRaceLog(fleet);
}
+
}
diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractScoringSchemeImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractScoringSchemeImpl.java
index b3fe8b717ef..baa13882aa3 100755
--- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractScoringSchemeImpl.java
+++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractScoringSchemeImpl.java
@@ -77,7 +77,7 @@ public abstract class AbstractScoringSchemeImpl implements ScoringScheme {
* more races.
*/
@Override
- public boolean isValidInTotalScore(Leaderboard leaderboard, RaceColumn raceColumn, TimePoint at) {
+ public boolean isValidInTotalScore(Leaderboard leaderboard, RaceColumn raceColumn, Competitor competitor, TimePoint at) {
boolean result;
Iterable extends Fleet> fleets = raceColumn.getFleets();
if (Util.size(fleets) <= 1 || allFleetsOrdered(fleets)) {
diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractSimpleLeaderboardImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractSimpleLeaderboardImpl.java
index 974b95d4510..1f36ec0afee 100755
--- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractSimpleLeaderboardImpl.java
+++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractSimpleLeaderboardImpl.java
@@ -14,6 +14,7 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.NavigableSet;
+import java.util.NoSuchElementException;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.Callable;
@@ -532,7 +533,7 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
if (raceColumn.isStartsWithZeroScore()) {
needToResetScoreUponNextNonEmptyEntry = true;
}
- if (getScoringScheme().isValidInTotalScore(this, raceColumn, timePoint)) {
+ if (getScoringScheme().isValidInTotalScore(this, raceColumn, competitor, timePoint)) {
final Double totalPoints = getTotalPoints(competitor, raceColumn, timePoint, discardedRaceColumns);
if (totalPoints != null) {
if (needToResetScoreUponNextNonEmptyEntry) {
@@ -651,7 +652,7 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
@Override
public boolean countRaceForComparisonWithDiscardingThresholds(Competitor competitor, RaceColumn raceColumn, TimePoint timePoint) {
TrackedRace trackedRaceForCompetitorInColumn;
- return getScoringScheme().isValidInTotalScore(this, raceColumn, timePoint) &&
+ return getScoringScheme().isValidInTotalScore(this, raceColumn, competitor, timePoint) &&
(getScoreCorrection().isScoreCorrected(competitor, raceColumn, timePoint) ||
((trackedRaceForCompetitorInColumn=raceColumn.getTrackedRace(competitor)) != null &&
trackedRaceForCompetitorInColumn.hasStarted(timePoint)));
@@ -1096,31 +1097,69 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
@Override
public Iterable