Merge remote-tracking branch 'refs/remotes/external/master' into jens--media-replication

Conflicts:
	java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java
This commit is contained in:
Jens Rommel committed 2013-06-10 12:28:33 +02:00
commit 541290a7d5
105 files changed
+1572 -617

No files matched your search

+24
View File
@@ -342,6 +342,12 @@ if [[ "$@" == "install" ]] || [[ "$@" == "all" ]]; then
rm -rf $ACDIR/org.eclipse.* rm -rf $ACDIR/org.eclipse.*
rm -rf $ACDIR/configuration/org.eclipse.* rm -rf $ACDIR/configuration/org.eclipse.*
if [ ! -f "$ACDIR/env.sh" ]; then
cp -v $PROJECT_HOME/java/target/env.sh $ACDIR/
cp -v $PROJECT_HOME/java/target/start $ACDIR/
cp -v $PROJECT_HOME/java/target/stop $ACDIR/
fi
if [[ $HAS_OVERWRITTEN_TARGET -eq 0 ]] && [ ! -f $ACDIR/no-overwrite ]; then if [[ $HAS_OVERWRITTEN_TARGET -eq 0 ]] && [ ! -f $ACDIR/no-overwrite ]; then
cp -v $p2PluginRepository/configuration/config.ini configuration/ cp -v $p2PluginRepository/configuration/config.ini configuration/
@@ -351,6 +357,7 @@ if [[ "$@" == "install" ]] || [[ "$@" == "all" ]]; then
cp -v $PROJECT_HOME/java/target/configuration/monitoring.properties configuration/ cp -v $PROJECT_HOME/java/target/configuration/monitoring.properties configuration/
cp -v $PROJECT_HOME/configuration/mongodb.cfg $ACDIR/ cp -v $PROJECT_HOME/configuration/mongodb.cfg $ACDIR/
cp -v $PROJECT_HOME/java/target/env.sh $ACDIR/
cp -v $PROJECT_HOME/java/target/start $ACDIR/ cp -v $PROJECT_HOME/java/target/start $ACDIR/
cp -v $PROJECT_HOME/java/target/stop $ACDIR/ cp -v $PROJECT_HOME/java/target/stop $ACDIR/
cp -v $PROJECT_HOME/java/target/udpmirror $ACDIR/ cp -v $PROJECT_HOME/java/target/udpmirror $ACDIR/
@@ -368,6 +375,23 @@ if [[ "$@" == "install" ]] || [[ "$@" == "all" ]]; then
cp -v $PROJECT_HOME/configuration/buildAndUpdateProduct.sh $ACDIR/ cp -v $PROJECT_HOME/configuration/buildAndUpdateProduct.sh $ACDIR/
echo $VERSION_INFO > $ACDIR/configuration/jetty/version.txt echo $VERSION_INFO > $ACDIR/configuration/jetty/version.txt
# make sure to save the information from env.sh
. $ACDIR/env.sh
sed -i "s/mongo.port=.*$/mongo.port=$MONGODB_PORT/g" $ACDIR/configuration/config.ini
sed -i "s/expedition.udp.port=.*$/expedition.udp.port=$EXPEDITION_PORT/g" $ACDIR/configuration/config.ini
sed -i "s/replication.exchangeName=.*$/replication.exchangeName=$REPLICATION_CHANNEL/g" $ACDIR/configuration/config.ini
echo "I have updated the configuration with the following data:"
echo "SERVER_NAME: $SERVER_NAME"
echo "SERVER_PORT: $SERVER_PORT"
echo "MEMORY: $MEMORY"
echo "TELNET_PORT: $TELNET_PORT"
echo "MONGODB_PORT: $MONGODB_PORT"
echo "EXPEDITION_PORT: $EXPEDITION_PORT"
echo "REPLICATION_CHANNEL: $REPLICATION_CHANNEL"
echo ""
echo "Installation complete. You may now start the server using ./start" echo "Installation complete. You may now start the server using ./start"
fi fi
+22 -7
View File
@@ -1,13 +1,28 @@
import socket import socket, sys
UDP_IP = "195.227.44.85" if len(sys.argv) <= 2:
print "Please provide ip and mode (TCP, UDP)"
sys.exit(1)
UDP_IP = sys.argv[1]
UDP_PORT = 2013 UDP_PORT = 2013
MODE = sys.argv[2]
MESSAGE = "Hello, World!" MESSAGE = "Hello, World!"
print "UDP target IP:", UDP_IP print "Target IP:", UDP_IP
print "UDP target port:", UDP_PORT print "Target port:", UDP_PORT
print "message:", MESSAGE print "Message:", MESSAGE
print "Mode: ", MODE
sock = socket.socket(socket.AF_INET, # Internet if MODE == 'UDP':
sock = socket.socket(socket.AF_INET, # Internet
socket.SOCK_DGRAM) # UDP socket.SOCK_DGRAM) # UDP
sock.sendto(MESSAGE, (UDP_IP, UDP_PORT)) sock.sendto(MESSAGE, (UDP_IP, UDP_PORT))
else:
sock = socket.socket(socket.AF_INET, # Internet
socket.SOCK_STREAM) # TCP
sock.connect((UDP_IP, UDP_PORT))
sock.send(MESSAGE)
data = sock.recv(1024)
sock.close()
+29 -7
View File
@@ -1,13 +1,35 @@
import socket import socket
import sys
UDP_IP = "195.227.44.85" if len(sys.argv) <= 2:
print "Please provide an IP address and mode"
sys.exit(1)
UDP_IP = sys.argv[1]
MODE = sys.argv[2]
UDP_PORT = 2013 UDP_PORT = 2013
sock = socket.socket(socket.AF_INET, # Internet print "Listening on %s port %s (%s)" % (UDP_IP, UDP_PORT, MODE)
if MODE == 'UDP':
sock = socket.socket(socket.AF_INET, # Internet
socket.SOCK_DGRAM) # UDP socket.SOCK_DGRAM) # UDP
sock.bind((UDP_IP, UDP_PORT)) sock.bind((UDP_IP, UDP_PORT))
while True:
data, addr = sock.recvfrom(1024) # buffer size is 1024 bytes
print "received message:", data
else:
sock = socket.socket(socket.AF_INET, # Internet
socket.SOCK_STREAM) # TCP
sock.bind((UDP_IP, UDP_PORT))
sock.listen(1)
conn, addr = sock.accept()
while True:
data = conn.recv(1024)
if not data:
break
conn.send(data) # echo
print "received message:", data
conn.close()
print "Listening on 195.227.44.85 port %s" % UDP_PORT
while True:
data, addr = sock.recvfrom(1024) # buffer size is 1024 bytes
print "received message:", data
+1
View File
@@ -12,6 +12,7 @@ Like businesses, sailors need the latest information to make strategic decisions
* [[General Project Information|wiki/general-information]] * [[General Project Information|wiki/general-information]]
* Production Environment * Production Environment
* [[PIRONET (Hosting Provider, Emergency Procedures)|wiki/prionet-related-information]] * [[PIRONET (Hosting Provider, Emergency Procedures)|wiki/prionet-related-information]]
* [[Amazon EC2|wiki/amazon-ec2]]
* [[Production Environment|wiki/production-environment]] * [[Production Environment|wiki/production-environment]]
* [[TracTrac Lifecycle for Event Managers|wiki/tractrac-lifecycle]] * [[TracTrac Lifecycle for Event Managers|wiki/tractrac-lifecycle]]
* Information for Developers * Information for Developers
@@ -87,20 +87,6 @@ public abstract class AbstractLeaderboardDTO implements Serializable {
return false; return false;
} }
public Double getTotalPoints(LeaderboardRowDTO object) {
Double totalPoints = object.carriedPoints == null ? null : object.carriedPoints;
for (Map.Entry<String, LeaderboardEntryDTO> e : object.fieldsByRaceColumnName.entrySet()) {
if (e.getValue().totalPoints != null && getOrCreateRaceColumn(e.getKey()).isValidInTotalScore()) {
if (totalPoints == null) {
totalPoints = e.getValue().totalPoints;
} else {
totalPoints += e.getValue().totalPoints;
}
}
}
return totalPoints;
}
public Double getNetPoints(CompetitorDTO competitor, String nameOfLastRaceSoFar) { public Double getNetPoints(CompetitorDTO competitor, String nameOfLastRaceSoFar) {
Double netPoints = null; Double netPoints = null;
LeaderboardRowDTO row = rows.get(competitor); LeaderboardRowDTO row = rows.get(competitor);
@@ -15,6 +15,7 @@ public class LeaderboardRowDTO implements Serializable {
public CompetitorDTO competitor; public CompetitorDTO competitor;
public Map<String, LeaderboardEntryDTO> fieldsByRaceColumnName; public Map<String, LeaderboardEntryDTO> fieldsByRaceColumnName;
public Double carriedPoints; public Double carriedPoints;
public Double totalPoints;
public Double totalTimeSailedInSeconds; public Double totalTimeSailedInSeconds;
public Double totalTimeSailedDownwindInSeconds; public Double totalTimeSailedDownwindInSeconds;
public Double maximumSpeedOverGroundInKnots; public Double maximumSpeedOverGroundInKnots;
@@ -30,6 +31,22 @@ public class LeaderboardRowDTO implements Serializable {
result = prime * result + ((carriedPoints == null) ? 0 : carriedPoints.hashCode()); result = prime * result + ((carriedPoints == null) ? 0 : carriedPoints.hashCode());
result = prime * result + ((competitor == null) ? 0 : competitor.hashCode()); result = prime * result + ((competitor == null) ? 0 : competitor.hashCode());
result = prime * result + ((fieldsByRaceColumnName == null) ? 0 : fieldsByRaceColumnName.hashCode()); result = prime * result + ((fieldsByRaceColumnName == null) ? 0 : fieldsByRaceColumnName.hashCode());
result = prime * result
+ ((maximumSpeedOverGroundInKnots == null) ? 0 : maximumSpeedOverGroundInKnots.hashCode());
result = prime * result
+ ((totalDistanceTraveledInMeters == null) ? 0 : totalDistanceTraveledInMeters.hashCode());
result = prime * result + ((totalPoints == null) ? 0 : totalPoints.hashCode());
result = prime * result
+ ((totalTimeSailedDownwindInSeconds == null) ? 0 : totalTimeSailedDownwindInSeconds.hashCode());
result = prime * result + ((totalTimeSailedInSeconds == null) ? 0 : totalTimeSailedInSeconds.hashCode());
result = prime * result
+ ((totalTimeSailedReachingInSeconds == null) ? 0 : totalTimeSailedReachingInSeconds.hashCode());
result = prime * result
+ ((totalTimeSailedUpwindInSeconds == null) ? 0 : totalTimeSailedUpwindInSeconds.hashCode());
result = prime
* result
+ ((whenMaximumSpeedOverGroundWasAchieved == null) ? 0 : whenMaximumSpeedOverGroundWasAchieved
.hashCode());
return result; return result;
} }
@@ -57,6 +74,46 @@ public class LeaderboardRowDTO implements Serializable {
return false; return false;
} else if (!fieldsByRaceColumnName.equals(other.fieldsByRaceColumnName)) } else if (!fieldsByRaceColumnName.equals(other.fieldsByRaceColumnName))
return false; return false;
if (maximumSpeedOverGroundInKnots == null) {
if (other.maximumSpeedOverGroundInKnots != null)
return false;
} else if (!maximumSpeedOverGroundInKnots.equals(other.maximumSpeedOverGroundInKnots))
return false;
if (totalDistanceTraveledInMeters == null) {
if (other.totalDistanceTraveledInMeters != null)
return false;
} else if (!totalDistanceTraveledInMeters.equals(other.totalDistanceTraveledInMeters))
return false;
if (totalPoints == null) {
if (other.totalPoints != null)
return false;
} else if (!totalPoints.equals(other.totalPoints))
return false;
if (totalTimeSailedDownwindInSeconds == null) {
if (other.totalTimeSailedDownwindInSeconds != null)
return false;
} else if (!totalTimeSailedDownwindInSeconds.equals(other.totalTimeSailedDownwindInSeconds))
return false;
if (totalTimeSailedInSeconds == null) {
if (other.totalTimeSailedInSeconds != null)
return false;
} else if (!totalTimeSailedInSeconds.equals(other.totalTimeSailedInSeconds))
return false;
if (totalTimeSailedReachingInSeconds == null) {
if (other.totalTimeSailedReachingInSeconds != null)
return false;
} else if (!totalTimeSailedReachingInSeconds.equals(other.totalTimeSailedReachingInSeconds))
return false;
if (totalTimeSailedUpwindInSeconds == null) {
if (other.totalTimeSailedUpwindInSeconds != null)
return false;
} else if (!totalTimeSailedUpwindInSeconds.equals(other.totalTimeSailedUpwindInSeconds))
return false;
if (whenMaximumSpeedOverGroundWasAchieved == null) {
if (other.whenMaximumSpeedOverGroundWasAchieved != null)
return false;
} else if (!whenMaximumSpeedOverGroundWasAchieved.equals(other.whenMaximumSpeedOverGroundWasAchieved))
return false;
return true; return true;
} }
} }
@@ -16,7 +16,7 @@ import com.sap.sailing.domain.base.RaceColumnListener;
import com.sap.sailing.domain.common.MaxPointsReason; import com.sap.sailing.domain.common.MaxPointsReason;
import com.sap.sailing.domain.leaderboard.DelayedLeaderboardCorrections; import com.sap.sailing.domain.leaderboard.DelayedLeaderboardCorrections;
import com.sap.sailing.domain.leaderboard.Leaderboard; import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule; import com.sap.sailing.domain.leaderboard.ResultDiscardingRule;
import com.sap.sailing.domain.racelog.RaceLogEvent; import com.sap.sailing.domain.racelog.RaceLogEvent;
import com.sap.sailing.domain.racelog.RaceLogIdentifier; import com.sap.sailing.domain.racelog.RaceLogIdentifier;
import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.domain.tracking.TrackedRace;
@@ -375,6 +375,10 @@ public class DelayedLeaderboardCorrectionsImpl implements DelayedLeaderboardCorr
public void isMedalRaceChanged(RaceColumn raceColumn, boolean newIsMedalRace) { public void isMedalRaceChanged(RaceColumn raceColumn, boolean newIsMedalRace) {
} }
@Override
public void isStartsWithZeroScoreChanged(RaceColumn raceColumn, boolean newIsStartsWithZeroScore) {
}
@Override @Override
public boolean canAddRaceColumnToContainer(RaceColumn raceColumn) { public boolean canAddRaceColumnToContainer(RaceColumn raceColumn) {
return true; return true;
@@ -401,7 +405,7 @@ public class DelayedLeaderboardCorrectionsImpl implements DelayedLeaderboardCorr
} }
@Override @Override
public void resultDiscardingRuleChanged(ThresholdBasedResultDiscardingRule oldDiscardingRule, ThresholdBasedResultDiscardingRule newDiscardingRule) { public void resultDiscardingRuleChanged(ResultDiscardingRule oldDiscardingRule, ResultDiscardingRule newDiscardingRule) {
} }
@Override @Override
@@ -83,7 +83,7 @@ import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule;
import com.sap.sailing.domain.leaderboard.impl.FlexibleLeaderboardImpl; import com.sap.sailing.domain.leaderboard.impl.FlexibleLeaderboardImpl;
import com.sap.sailing.domain.leaderboard.impl.LeaderboardGroupImpl; import com.sap.sailing.domain.leaderboard.impl.LeaderboardGroupImpl;
import com.sap.sailing.domain.leaderboard.impl.RegattaLeaderboardImpl; import com.sap.sailing.domain.leaderboard.impl.RegattaLeaderboardImpl;
import com.sap.sailing.domain.leaderboard.impl.ResultDiscardingRuleImpl; import com.sap.sailing.domain.leaderboard.impl.ThresholdBasedResultDiscardingRuleImpl;
import com.sap.sailing.domain.leaderboard.impl.ScoreCorrectionImpl; import com.sap.sailing.domain.leaderboard.impl.ScoreCorrectionImpl;
import com.sap.sailing.domain.leaderboard.meta.LeaderboardGroupMetaLeaderboard; import com.sap.sailing.domain.leaderboard.meta.LeaderboardGroupMetaLeaderboard;
import com.sap.sailing.domain.persistence.DomainObjectFactory; import com.sap.sailing.domain.persistence.DomainObjectFactory;
@@ -238,7 +238,7 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
} }
if (result == null) { if (result == null) {
SettableScoreCorrection scoreCorrection = new ScoreCorrectionImpl(); SettableScoreCorrection scoreCorrection = new ScoreCorrectionImpl();
ThresholdBasedResultDiscardingRule resultDiscardingRule = loadResultDiscardingRule(dbLeaderboard); ThresholdBasedResultDiscardingRule resultDiscardingRule = loadResultDiscardingRule(dbLeaderboard, FieldNames.LEADERBOARD_DISCARDING_THRESHOLDS);
String regattaName = (String) dbLeaderboard.get(FieldNames.REGATTA_NAME.name()); String regattaName = (String) dbLeaderboard.get(FieldNames.REGATTA_NAME.name());
if (groupForMetaLeaderboard != null) { if (groupForMetaLeaderboard != null) {
result = new LeaderboardGroupMetaLeaderboard(groupForMetaLeaderboard, loadScoringScheme(dbLeaderboard), resultDiscardingRule); result = new LeaderboardGroupMetaLeaderboard(groupForMetaLeaderboard, loadScoringScheme(dbLeaderboard), resultDiscardingRule);
@@ -314,17 +314,16 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
} }
/** /**
* @param dbLeaderboard expects to find a field named {@link FieldNames#LEADERBOARD_DISCARDING_THRESHOLDS} * @param dbObject expects to find a field identified by <code>field</code> which holds a {@link BasicDBList}
*/ */
private ThresholdBasedResultDiscardingRule loadResultDiscardingRule(DBObject dbLeaderboard) { private ThresholdBasedResultDiscardingRule loadResultDiscardingRule(DBObject dbObject, FieldNames field) {
BasicDBList dbDiscardIndexResultsStartingWithHowManyRaces = (BasicDBList) dbLeaderboard BasicDBList dbDiscardIndexResultsStartingWithHowManyRaces = (BasicDBList) dbObject.get(field.name());
.get(FieldNames.LEADERBOARD_DISCARDING_THRESHOLDS.name());
int[] discardIndexResultsStartingWithHowManyRaces = new int[dbDiscardIndexResultsStartingWithHowManyRaces.size()]; int[] discardIndexResultsStartingWithHowManyRaces = new int[dbDiscardIndexResultsStartingWithHowManyRaces.size()];
int i = 0; int i = 0;
for (Object discardingThresholdAsObject : dbDiscardIndexResultsStartingWithHowManyRaces) { for (Object discardingThresholdAsObject : dbDiscardIndexResultsStartingWithHowManyRaces) {
discardIndexResultsStartingWithHowManyRaces[i++] = (Integer) discardingThresholdAsObject; discardIndexResultsStartingWithHowManyRaces[i++] = (Integer) discardingThresholdAsObject;
} }
ThresholdBasedResultDiscardingRule resultDiscardingRule = new ResultDiscardingRuleImpl( ThresholdBasedResultDiscardingRule resultDiscardingRule = new ThresholdBasedResultDiscardingRuleImpl(
discardIndexResultsStartingWithHowManyRaces); discardIndexResultsStartingWithHowManyRaces);
return resultDiscardingRule; return resultDiscardingRule;
} }
@@ -907,16 +906,19 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
private Series loadSeries(DBObject dbSeries, TrackedRegattaRegistry trackedRegattaRegistry) { private Series loadSeries(DBObject dbSeries, TrackedRegattaRegistry trackedRegattaRegistry) {
String name = (String) dbSeries.get(FieldNames.SERIES_NAME.name()); String name = (String) dbSeries.get(FieldNames.SERIES_NAME.name());
boolean isMedal = (Boolean) dbSeries.get(FieldNames.SERIES_IS_MEDAL.name()); boolean isMedal = (Boolean) dbSeries.get(FieldNames.SERIES_IS_MEDAL.name());
Boolean startsWithZeroScore = (Boolean) dbSeries.get(FieldNames.SERIES_STARTS_WITH_ZERO_SCORE.name());
final BasicDBList dbFleets = (BasicDBList) dbSeries.get(FieldNames.SERIES_FLEETS.name()); final BasicDBList dbFleets = (BasicDBList) dbSeries.get(FieldNames.SERIES_FLEETS.name());
Map<String, Fleet> fleetsByName = loadFleets(dbFleets); Map<String, Fleet> fleetsByName = loadFleets(dbFleets);
BasicDBList dbRaceColumns = (BasicDBList) dbSeries.get(FieldNames.SERIES_RACE_COLUMNS.name()); BasicDBList dbRaceColumns = (BasicDBList) dbSeries.get(FieldNames.SERIES_RACE_COLUMNS.name());
Iterable<String> raceColumnNames = loadRaceColumnNames(dbRaceColumns, fleetsByName); Iterable<String> raceColumnNames = loadRaceColumnNames(dbRaceColumns, fleetsByName);
Series series = new SeriesImpl( Series series = new SeriesImpl(name, isMedal, fleetsByName.values(), raceColumnNames, trackedRegattaRegistry);
name, if (dbSeries.get(FieldNames.SERIES_DISCARDING_THRESHOLDS.name()) != null) {
isMedal, ThresholdBasedResultDiscardingRule resultDiscardingRule = loadResultDiscardingRule(dbSeries, FieldNames.SERIES_DISCARDING_THRESHOLDS);
fleetsByName.values(), series.setResultDiscardingRule(resultDiscardingRule);
raceColumnNames, }
trackedRegattaRegistry); if (startsWithZeroScore != null) {
series.setStartsWithZeroScore(startsWithZeroScore);
}
loadRaceColumnRaceLinks(dbRaceColumns, series); loadRaceColumnRaceLinks(dbRaceColumns, series);
return series; return series;
} }
@@ -997,7 +999,7 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
@Override @Override
public RaceLog loadRaceLog(RaceLogIdentifier identifier) { public RaceLog loadRaceLog(RaceLogIdentifier identifier) {
RaceLog result = new RaceLogImpl(RaceLogImpl.class.getSimpleName()); RaceLog result = new RaceLogImpl(RaceLogImpl.class.getSimpleName(), identifier.getIdentifier());
try { try {
BasicDBObject query = new BasicDBObject(); BasicDBObject query = new BasicDBObject();
query.put(FieldNames.RACE_LOG_IDENTIFIER.name(), MongoUtils.escapeDollarAndDot(identifier.getIdentifier().toString())); query.put(FieldNames.RACE_LOG_IDENTIFIER.name(), MongoUtils.escapeDollarAndDot(identifier.getIdentifier().toString()));
@@ -20,8 +20,10 @@ public enum FieldNames {
REGATTA_SERIES, REGATTA_SERIES,
SERIES_NAME, SERIES_NAME,
SERIES_IS_MEDAL, SERIES_IS_MEDAL,
SERIES_STARTS_WITH_ZERO_SCORE,
SERIES_FLEETS, SERIES_FLEETS,
SERIES_RACE_COLUMNS, SERIES_RACE_COLUMNS,
SERIES_DISCARDING_THRESHOLDS,
FLEET_NAME, FLEET_NAME,
FLEET_ORDERING, FLEET_ORDERING,
FLEET_COLOR, FLEET_COLOR,
@@ -41,6 +41,7 @@ import com.sap.sailing.domain.leaderboard.FlexibleLeaderboard;
import com.sap.sailing.domain.leaderboard.Leaderboard; import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.leaderboard.LeaderboardGroup; import com.sap.sailing.domain.leaderboard.LeaderboardGroup;
import com.sap.sailing.domain.leaderboard.RegattaLeaderboard; import com.sap.sailing.domain.leaderboard.RegattaLeaderboard;
import com.sap.sailing.domain.leaderboard.ResultDiscardingRule;
import com.sap.sailing.domain.leaderboard.SettableScoreCorrection; import com.sap.sailing.domain.leaderboard.SettableScoreCorrection;
import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule; import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule;
import com.sap.sailing.domain.persistence.MongoObjectFactory; import com.sap.sailing.domain.persistence.MongoObjectFactory;
@@ -237,8 +238,8 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
BasicDBObject dbScoreCorrections = new BasicDBObject(); BasicDBObject dbScoreCorrections = new BasicDBObject();
storeScoreCorrections(leaderboard, dbScoreCorrections); storeScoreCorrections(leaderboard, dbScoreCorrections);
dbLeaderboard.put(FieldNames.LEADERBOARD_SCORE_CORRECTIONS.name(), dbScoreCorrections); dbLeaderboard.put(FieldNames.LEADERBOARD_SCORE_CORRECTIONS.name(), dbScoreCorrections);
final ThresholdBasedResultDiscardingRule resultDiscardingRule = leaderboard.getResultDiscardingRule(); final ResultDiscardingRule resultDiscardingRule = leaderboard.getResultDiscardingRule();
storeResultDiscardingRule(dbLeaderboard, resultDiscardingRule); storeResultDiscardingRule(dbLeaderboard, resultDiscardingRule, FieldNames.LEADERBOARD_DISCARDING_THRESHOLDS);
BasicDBList competitorDisplayNames = new BasicDBList(); BasicDBList competitorDisplayNames = new BasicDBList();
for (Competitor competitor : leaderboard.getCompetitors()) { for (Competitor competitor : leaderboard.getCompetitors()) {
String displayNameForCompetitor = leaderboard.getDisplayName(competitor); String displayNameForCompetitor = leaderboard.getDisplayName(competitor);
@@ -252,13 +253,20 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
dbLeaderboard.put(FieldNames.LEADERBOARD_COMPETITOR_DISPLAY_NAMES.name(), competitorDisplayNames); dbLeaderboard.put(FieldNames.LEADERBOARD_COMPETITOR_DISPLAY_NAMES.name(), competitorDisplayNames);
} }
private void storeResultDiscardingRule(BasicDBObject dbLeaderboard, /**
final ThresholdBasedResultDiscardingRule resultDiscardingRule) { * Stores the result discarding rule to <code>dbObject</code>'s field identified by <code>field</code> if the result discarding
BasicDBList dbResultDiscardingThresholds = new BasicDBList(); * rule is not <code>null</code> and is of type {@link ThresholdBasedResultDiscardingRule}. Otherwise, it is assumed that the
for (int threshold : resultDiscardingRule.getDiscardIndexResultsStartingWithHowManyRaces()) { * result discarding rule is otherwise implicitly obtained, e.g., from a definition of a regatta with its series, stored elsewhere.
dbResultDiscardingThresholds.add(threshold); */
private void storeResultDiscardingRule(DBObject dbObject,
final ResultDiscardingRule resultDiscardingRule, FieldNames field) {
if (resultDiscardingRule != null && resultDiscardingRule instanceof ThresholdBasedResultDiscardingRule) {
BasicDBList dbResultDiscardingThresholds = new BasicDBList();
for (int threshold : ((ThresholdBasedResultDiscardingRule) resultDiscardingRule).getDiscardIndexResultsStartingWithHowManyRaces()) {
dbResultDiscardingThresholds.add(threshold);
}
dbObject.put(field.name(), dbResultDiscardingThresholds);
} }
dbLeaderboard.put(FieldNames.LEADERBOARD_DISCARDING_THRESHOLDS.name(), dbResultDiscardingThresholds);
} }
private BasicDBObject storeRaceColumn(RaceColumn raceColumn) { private BasicDBObject storeRaceColumn(RaceColumn raceColumn) {
@@ -468,6 +476,7 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
DBObject dbSeries = new BasicDBObject(); DBObject dbSeries = new BasicDBObject();
dbSeries.put(FieldNames.SERIES_NAME.name(), s.getName()); dbSeries.put(FieldNames.SERIES_NAME.name(), s.getName());
dbSeries.put(FieldNames.SERIES_IS_MEDAL.name(), s.isMedal()); dbSeries.put(FieldNames.SERIES_IS_MEDAL.name(), s.isMedal());
dbSeries.put(FieldNames.SERIES_STARTS_WITH_ZERO_SCORE.name(), s.isStartsWithZeroScore());
BasicDBList dbFleets = new BasicDBList(); BasicDBList dbFleets = new BasicDBList();
for (Fleet fleet : s.getFleets()) { for (Fleet fleet : s.getFleets()) {
dbFleets.add(storeFleet(fleet)); dbFleets.add(storeFleet(fleet));
@@ -478,6 +487,9 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
dbRaceColumns.add(storeRaceColumn(raceColumn)); dbRaceColumns.add(storeRaceColumn(raceColumn));
} }
dbSeries.put(FieldNames.SERIES_RACE_COLUMNS.name(), dbRaceColumns); dbSeries.put(FieldNames.SERIES_RACE_COLUMNS.name(), dbRaceColumns);
if (s.getResultDiscardingRule() != null) {
storeResultDiscardingRule(dbSeries, s.getResultDiscardingRule(), FieldNames.SERIES_DISCARDING_THRESHOLDS);
}
return dbSeries; return dbSeries;
} }
@@ -17,7 +17,7 @@ public class RaceLogAnalyzerTest {
@Test @Test
public void testLastFlagFinder() { public void testLastFlagFinder() {
RaceLog raceLog = new RaceLogImpl("RaceLogTest"); RaceLog raceLog = new RaceLogImpl("RaceLogTest", "test-identifier");
TimePoint t1 = MillisecondsTimePoint.now(); TimePoint t1 = MillisecondsTimePoint.now();
int passId = 0; int passId = 0;
boolean isDisplayed = true; boolean isDisplayed = true;
@@ -28,7 +28,7 @@ public class RaceLogTest {
@Before @Before
public void setUp() { public void setUp() {
raceLog = new RaceLogImpl("testlock"); raceLog = new RaceLogImpl("testlock", "test-identifier");
} }
@Test(expected = IllegalStateException.class) @Test(expected = IllegalStateException.class)
@@ -43,6 +43,10 @@ public class RaceLogTest {
raceLog.unlockAfterRead(); raceLog.unlockAfterRead();
} }
public void testIdentifier() {
assertEquals("test-identifier", raceLog.getId());
}
@Test @Test
public void testAddEvent() { public void testAddEvent() {
RaceLogEvent event = mock(RaceLogEvent.class); RaceLogEvent event = mock(RaceLogEvent.class);
@@ -1,5 +1,6 @@
package com.sap.sailing.domain.racelog; package com.sap.sailing.domain.racelog;
import com.sap.sailing.domain.common.WithID;
import com.sap.sailing.domain.racelog.impl.RaceLogEventComparator; import com.sap.sailing.domain.racelog.impl.RaceLogEventComparator;
import com.sap.sailing.domain.tracking.Track; import com.sap.sailing.domain.tracking.Track;
@@ -15,7 +16,7 @@ import com.sap.sailing.domain.tracking.Track;
* Implementations should use the {@link RaceLogEventComparator} for sorting its content. * Implementations should use the {@link RaceLogEventComparator} for sorting its content.
* </p> * </p>
*/ */
public interface RaceLog extends Track<RaceLogEvent> { public interface RaceLog extends Track<RaceLogEvent>, WithID {
/** /**
* Gets the current pass id. * Gets the current pass id.
* *
@@ -2,6 +2,7 @@ package com.sap.sailing.domain.racelog.impl;
import java.io.IOException; import java.io.IOException;
import java.io.ObjectInputStream; import java.io.ObjectInputStream;
import java.io.Serializable;
import java.util.HashSet; import java.util.HashSet;
import java.util.NavigableSet; import java.util.NavigableSet;
import java.util.Set; import java.util.Set;
@@ -24,6 +25,7 @@ public class RaceLogImpl extends TrackImpl<RaceLogEvent> implements RaceLog {
private static final String DefaultLockName = RaceLogImpl.class.getName() + ".lock"; private static final String DefaultLockName = RaceLogImpl.class.getName() + ".lock";
private final static Logger logger = Logger.getLogger(RaceLogImpl.class.getName()); private final static Logger logger = Logger.getLogger(RaceLogImpl.class.getName());
private final Serializable id;
private transient Set<RaceLogEventVisitor> listeners; private transient Set<RaceLogEventVisitor> listeners;
private int currentPassId; private int currentPassId;
@@ -32,19 +34,25 @@ public class RaceLogImpl extends TrackImpl<RaceLogEvent> implements RaceLog {
/** /**
* Initializes a new {@link RaceLogImpl} with the default lock name. * Initializes a new {@link RaceLogImpl} with the default lock name.
*/ */
public RaceLogImpl() { public RaceLogImpl(Serializable identifier) {
this(DefaultLockName); this(DefaultLockName, identifier);
} }
/** /**
* Initializes a new {@link RaceLogImpl}. * Initializes a new {@link RaceLogImpl}.
* @param nameForReadWriteLock name of lock. * @param nameForReadWriteLock name of lock.
*/ */
public RaceLogImpl(String nameForReadWriteLock) { public RaceLogImpl(String nameForReadWriteLock, Serializable identifier) {
super(new ArrayListNavigableSet<Timed>(RaceLogEventComparator.INSTANCE), nameForReadWriteLock); super(new ArrayListNavigableSet<Timed>(RaceLogEventComparator.INSTANCE), nameForReadWriteLock);
this.listeners = new HashSet<RaceLogEventVisitor>(); this.listeners = new HashSet<RaceLogEventVisitor>();
this.currentPassId = DefaultPassId; this.currentPassId = DefaultPassId;
this.id = identifier;
}
@Override
public Serializable getId() {
return this.id;
} }
@Override @Override
@@ -132,4 +140,5 @@ public class RaceLogImpl extends TrackImpl<RaceLogEvent> implements RaceLog {
ois.defaultReadObject(); ois.defaultReadObject();
listeners = new HashSet<RaceLogEventVisitor>(); listeners = new HashSet<RaceLogEventVisitor>();
} }
} }
@@ -21,7 +21,7 @@ import com.sap.sailing.domain.common.impl.Util.Pair;
import com.sap.sailing.domain.common.impl.WindSourceImpl; import com.sap.sailing.domain.common.impl.WindSourceImpl;
import com.sap.sailing.domain.leaderboard.impl.FlexibleLeaderboardImpl; import com.sap.sailing.domain.leaderboard.impl.FlexibleLeaderboardImpl;
import com.sap.sailing.domain.leaderboard.impl.LowPoint; import com.sap.sailing.domain.leaderboard.impl.LowPoint;
import com.sap.sailing.domain.leaderboard.impl.ResultDiscardingRuleImpl; import com.sap.sailing.domain.leaderboard.impl.ThresholdBasedResultDiscardingRuleImpl;
import com.sap.sailing.domain.leaderboard.impl.ScoreCorrectionImpl; import com.sap.sailing.domain.leaderboard.impl.ScoreCorrectionImpl;
import com.sap.sailing.domain.tracking.impl.WindImpl; import com.sap.sailing.domain.tracking.impl.WindImpl;
import com.sap.sailing.domain.tractracadapter.ReceiverType; import com.sap.sailing.domain.tractracadapter.ReceiverType;
@@ -37,7 +37,7 @@ public class LeaderboardForKielWeekTest extends OnlineTracTracBasedTest {
@Test @Test
public void leaderboardWithOneRaceTest() throws URISyntaxException, NoWindException, IOException, InterruptedException { public void leaderboardWithOneRaceTest() throws URISyntaxException, NoWindException, IOException, InterruptedException {
leaderboard = new FlexibleLeaderboardImpl("Kiel Week 2011 505s", new ScoreCorrectionImpl(), leaderboard = new FlexibleLeaderboardImpl("Kiel Week 2011 505s", new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(new int[] { 3, 6 }), new LowPoint(), null); new ThresholdBasedResultDiscardingRuleImpl(new int[] { 3, 6 }), new LowPoint(), null);
Fleet defaultFleet = leaderboard.getFleet(null); Fleet defaultFleet = leaderboard.getFleet(null);
MillisecondsTimePoint now = MillisecondsTimePoint.now(); MillisecondsTimePoint now = MillisecondsTimePoint.now();
loadRace("357c700a-9d9a-11e0-85be-406186cbf87c"); // 505 Race 2 loadRace("357c700a-9d9a-11e0-85be-406186cbf87c"); // 505 Race 2
@@ -37,7 +37,7 @@ import com.sap.sailing.domain.common.impl.Util.Pair;
import com.sap.sailing.domain.leaderboard.FlexibleLeaderboard; import com.sap.sailing.domain.leaderboard.FlexibleLeaderboard;
import com.sap.sailing.domain.leaderboard.impl.FlexibleLeaderboardImpl; import com.sap.sailing.domain.leaderboard.impl.FlexibleLeaderboardImpl;
import com.sap.sailing.domain.leaderboard.impl.LowPoint; import com.sap.sailing.domain.leaderboard.impl.LowPoint;
import com.sap.sailing.domain.leaderboard.impl.ResultDiscardingRuleImpl; import com.sap.sailing.domain.leaderboard.impl.ThresholdBasedResultDiscardingRuleImpl;
import com.sap.sailing.domain.leaderboard.impl.ScoreCorrectionImpl; import com.sap.sailing.domain.leaderboard.impl.ScoreCorrectionImpl;
import com.sap.sailing.domain.test.mock.MockedTrackedRaceWithFixedRank; import com.sap.sailing.domain.test.mock.MockedTrackedRaceWithFixedRank;
import com.sap.sailing.domain.test.mock.MockedTrackedRaceWithFixedRankAndManyCompetitors; import com.sap.sailing.domain.test.mock.MockedTrackedRaceWithFixedRankAndManyCompetitors;
@@ -100,7 +100,7 @@ public class LeaderboardOfflineTest extends AbstractLeaderboardTest {
@Test @Test
public void ensureMedalRaceParamIsIgnoredIfRaceColumnAlreadyExists() { public void ensureMedalRaceParamIsIgnoredIfRaceColumnAlreadyExists() {
FlexibleLeaderboard leaderboard = new FlexibleLeaderboardImpl("Test Leaderboard", new ScoreCorrectionImpl(), new ResultDiscardingRuleImpl( FlexibleLeaderboard leaderboard = new FlexibleLeaderboardImpl("Test Leaderboard", new ScoreCorrectionImpl(), new ThresholdBasedResultDiscardingRuleImpl(
new int[] { 5, 8 }), new LowPoint(), null); new int[] { 5, 8 }), new LowPoint(), null);
Fleet defaultFleet = leaderboard.getFleet(null); Fleet defaultFleet = leaderboard.getFleet(null);
final String columnName = "abc"; final String columnName = "abc";
@@ -113,7 +113,7 @@ public class LeaderboardOfflineTest extends AbstractLeaderboardTest {
@Test @Test
public void testRepeatedLeaderboardDTOCacheInvalidation() throws NoWindException, InterruptedException, ExecutionException { public void testRepeatedLeaderboardDTOCacheInvalidation() throws NoWindException, InterruptedException, ExecutionException {
FlexibleLeaderboard leaderboard = new FlexibleLeaderboardImpl("Test Leaderboard", new ScoreCorrectionImpl(), new ResultDiscardingRuleImpl( FlexibleLeaderboard leaderboard = new FlexibleLeaderboardImpl("Test Leaderboard", new ScoreCorrectionImpl(), new ThresholdBasedResultDiscardingRuleImpl(
new int[] { 5, 8 }), new LowPoint(), null); new int[] { 5, 8 }), new LowPoint(), null);
Fleet defaultFleet = leaderboard.getFleet(null); Fleet defaultFleet = leaderboard.getFleet(null);
final String columnName = "abc"; final String columnName = "abc";
@@ -169,7 +169,7 @@ public class LeaderboardOfflineTest extends AbstractLeaderboardTest {
testRaces.add(r); // hash set should take care of more or less randomly permuting the races testRaces.add(r); // hash set should take care of more or less randomly permuting the races
} }
ScoreCorrectionImpl scoreCorrection = new ScoreCorrectionImpl(); ScoreCorrectionImpl scoreCorrection = new ScoreCorrectionImpl();
FlexibleLeaderboard leaderboard = new FlexibleLeaderboardImpl("Test Leaderboard", scoreCorrection, new ResultDiscardingRuleImpl( FlexibleLeaderboard leaderboard = new FlexibleLeaderboardImpl("Test Leaderboard", scoreCorrection, new ThresholdBasedResultDiscardingRuleImpl(
new int[] { 1 }), new LowPoint(), null); new int[] { 1 }), new LowPoint(), null);
Fleet defaultFleet = leaderboard.getFleet(null); Fleet defaultFleet = leaderboard.getFleet(null);
int i=0; int i=0;
@@ -203,7 +203,7 @@ public class LeaderboardOfflineTest extends AbstractLeaderboardTest {
testRace.addCompetitor(c2); testRace.addCompetitor(c2);
testRace.addCompetitor(c3); // this makes maxPoints==4 testRace.addCompetitor(c3); // this makes maxPoints==4
ScoreCorrectionImpl scoreCorrection = new ScoreCorrectionImpl(); ScoreCorrectionImpl scoreCorrection = new ScoreCorrectionImpl();
FlexibleLeaderboard leaderboard = new FlexibleLeaderboardImpl("Test Leaderboard", scoreCorrection, new ResultDiscardingRuleImpl( FlexibleLeaderboard leaderboard = new FlexibleLeaderboardImpl("Test Leaderboard", scoreCorrection, new ThresholdBasedResultDiscardingRuleImpl(
new int[] { 2 }), new LowPoint(), null); new int[] { 2 }), new LowPoint(), null);
Fleet defaultFleet = leaderboard.getFleet(null); Fleet defaultFleet = leaderboard.getFleet(null);
leaderboard.addRace(testRace, "R1", /* medalRace */ false, defaultFleet); leaderboard.addRace(testRace, "R1", /* medalRace */ false, defaultFleet);
@@ -232,7 +232,7 @@ public class LeaderboardOfflineTest extends AbstractLeaderboardTest {
testRace.addCompetitor(c2); testRace.addCompetitor(c2);
testRace.addCompetitor(c3); // this makes maxPoints==4 testRace.addCompetitor(c3); // this makes maxPoints==4
ScoreCorrectionImpl scoreCorrection = new ScoreCorrectionImpl(); ScoreCorrectionImpl scoreCorrection = new ScoreCorrectionImpl();
FlexibleLeaderboard leaderboard = new FlexibleLeaderboardImpl("Test Leaderboard", scoreCorrection, new ResultDiscardingRuleImpl( FlexibleLeaderboard leaderboard = new FlexibleLeaderboardImpl("Test Leaderboard", scoreCorrection, new ThresholdBasedResultDiscardingRuleImpl(
new int[] { 2 }), new LowPoint(), null); new int[] { 2 }), new LowPoint(), null);
Fleet defaultFleet = leaderboard.getFleet(null); Fleet defaultFleet = leaderboard.getFleet(null);
RaceColumn r1 = leaderboard.addRace(testRace, "R1", /* medalRace */ false, defaultFleet); RaceColumn r1 = leaderboard.addRace(testRace, "R1", /* medalRace */ false, defaultFleet);
@@ -260,7 +260,7 @@ public class LeaderboardOfflineTest extends AbstractLeaderboardTest {
testRaces.add(r); // hash set should take care of more or less randomly permuting the races testRaces.add(r); // hash set should take care of more or less randomly permuting the races
} }
ScoreCorrectionImpl scoreCorrection = new ScoreCorrectionImpl(); ScoreCorrectionImpl scoreCorrection = new ScoreCorrectionImpl();
FlexibleLeaderboard leaderboard = new FlexibleLeaderboardImpl("Test Leaderboard", scoreCorrection, new ResultDiscardingRuleImpl( FlexibleLeaderboard leaderboard = new FlexibleLeaderboardImpl("Test Leaderboard", scoreCorrection, new ThresholdBasedResultDiscardingRuleImpl(
new int[] { 1 }), new LowPoint(), null); new int[] { 1 }), new LowPoint(), null);
Fleet defaultFleet = leaderboard.getFleet(null); Fleet defaultFleet = leaderboard.getFleet(null);
int i=0; int i=0;
@@ -287,7 +287,7 @@ public class LeaderboardOfflineTest extends AbstractLeaderboardTest {
protected void testLeaderboard(int numberOfStartedRaces, int numberOfNotStartedRaces, int firstDiscardingThreshold, protected void testLeaderboard(int numberOfStartedRaces, int numberOfNotStartedRaces, int firstDiscardingThreshold,
int secondDiscardingThreshold, Integer carry, boolean addOneMedalRace, int numberOfUntrackedRaces) throws NoWindException { int secondDiscardingThreshold, Integer carry, boolean addOneMedalRace, int numberOfUntrackedRaces) throws NoWindException {
setupRaces(numberOfStartedRaces, numberOfNotStartedRaces); setupRaces(numberOfStartedRaces, numberOfNotStartedRaces);
FlexibleLeaderboard leaderboard = new FlexibleLeaderboardImpl("Test Leaderboard", new ScoreCorrectionImpl(), new ResultDiscardingRuleImpl( FlexibleLeaderboard leaderboard = new FlexibleLeaderboardImpl("Test Leaderboard", new ScoreCorrectionImpl(), new ThresholdBasedResultDiscardingRuleImpl(
new int[] { firstDiscardingThreshold, secondDiscardingThreshold }), new LowPoint(), null); new int[] { firstDiscardingThreshold, secondDiscardingThreshold }), new LowPoint(), null);
Fleet defaultFleet = leaderboard.getFleet(null); Fleet defaultFleet = leaderboard.getFleet(null);
int i=0; int i=0;
@@ -45,7 +45,7 @@ import com.sap.sailing.domain.leaderboard.impl.HighPoint;
import com.sap.sailing.domain.leaderboard.impl.HighPointExtremeSailingSeriesOverall; import com.sap.sailing.domain.leaderboard.impl.HighPointExtremeSailingSeriesOverall;
import com.sap.sailing.domain.leaderboard.impl.LeaderboardGroupImpl; import com.sap.sailing.domain.leaderboard.impl.LeaderboardGroupImpl;
import com.sap.sailing.domain.leaderboard.impl.RegattaLeaderboardImpl; import com.sap.sailing.domain.leaderboard.impl.RegattaLeaderboardImpl;
import com.sap.sailing.domain.leaderboard.impl.ResultDiscardingRuleImpl; import com.sap.sailing.domain.leaderboard.impl.ThresholdBasedResultDiscardingRuleImpl;
import com.sap.sailing.domain.leaderboard.impl.ScoreCorrectionImpl; import com.sap.sailing.domain.leaderboard.impl.ScoreCorrectionImpl;
import com.sap.sailing.domain.leaderboard.meta.LeaderboardGroupMetaLeaderboard; import com.sap.sailing.domain.leaderboard.meta.LeaderboardGroupMetaLeaderboard;
import com.sap.sailing.domain.test.mock.MockedTrackedRaceWithStartTimeAndRanks; import com.sap.sailing.domain.test.mock.MockedTrackedRaceWithStartTimeAndRanks;
@@ -60,7 +60,7 @@ public class LeaderboardScoringAndRankingTest extends AbstractLeaderboardTest {
private Leaderboard createLeaderboard(Regatta regatta, int[] discardingThresholds) { private Leaderboard createLeaderboard(Regatta regatta, int[] discardingThresholds) {
ScoreCorrectionImpl scoreCorrections = new ScoreCorrectionImpl(); ScoreCorrectionImpl scoreCorrections = new ScoreCorrectionImpl();
ResultDiscardingRuleImpl discardingRules = new ResultDiscardingRuleImpl(discardingThresholds); ThresholdBasedResultDiscardingRuleImpl discardingRules = new ThresholdBasedResultDiscardingRuleImpl(discardingThresholds);
return new RegattaLeaderboardImpl(regatta, scoreCorrections, discardingRules); return new RegattaLeaderboardImpl(regatta, scoreCorrections, discardingRules);
} }
@@ -801,7 +801,7 @@ public class LeaderboardScoringAndRankingTest extends AbstractLeaderboardTest {
TimePoint now = MillisecondsTimePoint.now(); TimePoint now = MillisecondsTimePoint.now();
TimePoint later = new MillisecondsTimePoint(now.asMillis()+1000); TimePoint later = new MillisecondsTimePoint(now.asMillis()+1000);
FlexibleLeaderboard leaderboard1 = new FlexibleLeaderboardImpl("Leaderboard 1", new ScoreCorrectionImpl(), FlexibleLeaderboard leaderboard1 = new FlexibleLeaderboardImpl("Leaderboard 1", new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(/* discarding thresholds */ new int[0]), new HighPoint(), null); new ThresholdBasedResultDiscardingRuleImpl(/* discarding thresholds */ new int[0]), new HighPoint(), null);
leaderboard1.addRace(new MockedTrackedRaceWithStartTimeAndRanks(now, Arrays.asList(f1)), "R1", /* medalRace */ false, leaderboard1.addRace(new MockedTrackedRaceWithStartTimeAndRanks(now, Arrays.asList(f1)), "R1", /* medalRace */ false,
leaderboard1.getFleet(null)); leaderboard1.getFleet(null));
leaderboard1.addRace(new MockedTrackedRaceWithStartTimeAndRanks(now, Arrays.asList(f2)), "R2", /* medalRace */ false, leaderboard1.addRace(new MockedTrackedRaceWithStartTimeAndRanks(now, Arrays.asList(f2)), "R2", /* medalRace */ false,
@@ -809,7 +809,7 @@ public class LeaderboardScoringAndRankingTest extends AbstractLeaderboardTest {
assertTrue(leaderboard1.getScoringScheme().getScoreComparator(/* nullScoresAreBetter */ false).compare( assertTrue(leaderboard1.getScoringScheme().getScoreComparator(/* nullScoresAreBetter */ false).compare(
leaderboard1.getTotalPoints(c[0], later), leaderboard1.getTotalPoints(c[3], later)) < 0); // c0 better than c3 leaderboard1.getTotalPoints(c[0], later), leaderboard1.getTotalPoints(c[3], later)) < 0); // c0 better than c3
FlexibleLeaderboard leaderboard2 = new FlexibleLeaderboardImpl("Leaderboard 3", new ScoreCorrectionImpl(), FlexibleLeaderboard leaderboard2 = new FlexibleLeaderboardImpl("Leaderboard 3", new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(/* discarding thresholds */ new int[0]), new HighPoint(), null); new ThresholdBasedResultDiscardingRuleImpl(/* discarding thresholds */ new int[0]), new HighPoint(), null);
leaderboard2.addRace(new MockedTrackedRaceWithStartTimeAndRanks(now, Arrays.asList(f3)), "R1", /* medalRace */ false, leaderboard2.addRace(new MockedTrackedRaceWithStartTimeAndRanks(now, Arrays.asList(f3)), "R1", /* medalRace */ false,
leaderboard1.getFleet(null)); leaderboard1.getFleet(null));
leaderboard2.addRace(new MockedTrackedRaceWithStartTimeAndRanks(now, Arrays.asList(f4)), "R2", /* medalRace */ false, leaderboard2.addRace(new MockedTrackedRaceWithStartTimeAndRanks(now, Arrays.asList(f4)), "R2", /* medalRace */ false,
@@ -817,7 +817,7 @@ public class LeaderboardScoringAndRankingTest extends AbstractLeaderboardTest {
assertTrue(leaderboard2.getScoringScheme().getScoreComparator(/* nullScoresAreBetter */ false).compare( assertTrue(leaderboard2.getScoringScheme().getScoreComparator(/* nullScoresAreBetter */ false).compare(
leaderboard2.getTotalPoints(c[3], later), leaderboard2.getTotalPoints(c[0], later)) < 0); // c3 better than c0 leaderboard2.getTotalPoints(c[3], later), leaderboard2.getTotalPoints(c[0], later)) < 0); // c3 better than c0
FlexibleLeaderboard leaderboard3 = new FlexibleLeaderboardImpl("Leaderboard 3", new ScoreCorrectionImpl(), FlexibleLeaderboard leaderboard3 = new FlexibleLeaderboardImpl("Leaderboard 3", new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(/* discarding thresholds */ new int[0]), new HighPoint(), null); new ThresholdBasedResultDiscardingRuleImpl(/* discarding thresholds */ new int[0]), new HighPoint(), null);
leaderboard3.addRace(new MockedTrackedRaceWithStartTimeAndRanks(now, Arrays.asList(f5)), "R1", /* medalRace */ false, leaderboard3.addRace(new MockedTrackedRaceWithStartTimeAndRanks(now, Arrays.asList(f5)), "R1", /* medalRace */ false,
leaderboard1.getFleet(null)); leaderboard1.getFleet(null));
leaderboard3.addRace(new MockedTrackedRaceWithStartTimeAndRanks(now, Arrays.asList(f6)), "R2", /* medalRace */ false, leaderboard3.addRace(new MockedTrackedRaceWithStartTimeAndRanks(now, Arrays.asList(f6)), "R2", /* medalRace */ false,
@@ -827,7 +827,7 @@ public class LeaderboardScoringAndRankingTest extends AbstractLeaderboardTest {
LeaderboardGroup leaderboardGroup = new LeaderboardGroupImpl("Leaderboard Group", "Leaderboard Group", false, Arrays.asList(leaderboard1, LeaderboardGroup leaderboardGroup = new LeaderboardGroupImpl("Leaderboard Group", "Leaderboard Group", false, Arrays.asList(leaderboard1,
leaderboard2, leaderboard3)); leaderboard2, leaderboard3));
leaderboardGroup.setOverallLeaderboard(new LeaderboardGroupMetaLeaderboard(leaderboardGroup, new HighPointExtremeSailingSeriesOverall(), leaderboardGroup.setOverallLeaderboard(new LeaderboardGroupMetaLeaderboard(leaderboardGroup, new HighPointExtremeSailingSeriesOverall(),
new ResultDiscardingRuleImpl(new int[0]))); new ThresholdBasedResultDiscardingRuleImpl(new int[0])));
leaderboardGroup.getOverallLeaderboard().setSuppressed(c[1], true); leaderboardGroup.getOverallLeaderboard().setSuppressed(c[1], true);
leaderboardGroup.getOverallLeaderboard().setSuppressed(c[2], true); leaderboardGroup.getOverallLeaderboard().setSuppressed(c[2], true);
List<Competitor> rankedCompetitors = leaderboardGroup.getOverallLeaderboard().getCompetitorsFromBestToWorst(later); List<Competitor> rankedCompetitors = leaderboardGroup.getOverallLeaderboard().getCompetitorsFromBestToWorst(later);
@@ -25,7 +25,7 @@ import com.sap.sailing.domain.base.impl.DomainFactoryImpl;
import com.sap.sailing.domain.base.impl.PersonImpl; import com.sap.sailing.domain.base.impl.PersonImpl;
import com.sap.sailing.domain.base.impl.TeamImpl; import com.sap.sailing.domain.base.impl.TeamImpl;
import com.sap.sailing.domain.common.impl.Util.Pair; import com.sap.sailing.domain.common.impl.Util.Pair;
import com.sap.sailing.domain.leaderboard.impl.ResultDiscardingRuleImpl; import com.sap.sailing.domain.leaderboard.impl.ThresholdBasedResultDiscardingRuleImpl;
public class OfflineSerializationTest extends AbstractSerializationTest { public class OfflineSerializationTest extends AbstractSerializationTest {
/** /**
@@ -81,8 +81,8 @@ public class OfflineSerializationTest extends AbstractSerializationTest {
@Test @Test
public void testSerializingResultDiscardingRuleImpl() throws ClassNotFoundException, IOException { public void testSerializingResultDiscardingRuleImpl() throws ClassNotFoundException, IOException {
DomainFactory receiverDomainFactory = new DomainFactoryImpl(); DomainFactory receiverDomainFactory = new DomainFactoryImpl();
ResultDiscardingRuleImpl rdri = new ResultDiscardingRuleImpl(new int[] { 5, 8 }); ThresholdBasedResultDiscardingRuleImpl rdri = new ThresholdBasedResultDiscardingRuleImpl(new int[] { 5, 8 });
ResultDiscardingRuleImpl clone = cloneBySerialization(rdri, receiverDomainFactory); ThresholdBasedResultDiscardingRuleImpl clone = cloneBySerialization(rdri, receiverDomainFactory);
assertTrue(Arrays.equals(rdri.getDiscardIndexResultsStartingWithHowManyRaces(), assertTrue(Arrays.equals(rdri.getDiscardIndexResultsStartingWithHowManyRaces(),
clone.getDiscardIndexResultsStartingWithHowManyRaces())); clone.getDiscardIndexResultsStartingWithHowManyRaces()));
} }
@@ -4,6 +4,8 @@ import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame; import static org.junit.Assert.assertSame;
import java.io.Serializable;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@@ -27,7 +29,7 @@ public class RaceLogAttachOnTrackedRaceTest {
private RaceLog raceLog; private RaceLog raceLog;
@Override @Override
public void detachRaceLog() { public void detachRaceLog(Serializable identifier) {
this.raceLog = null; this.raceLog = null;
} }
@@ -37,7 +39,7 @@ public class RaceLogAttachOnTrackedRaceTest {
} }
@Override @Override
public RaceLog getRaceLog() { public RaceLog getRaceLog(Serializable identifier) {
return raceLog; return raceLog;
} }
} }
@@ -59,7 +61,7 @@ public class RaceLogAttachOnTrackedRaceTest {
column.setTrackedRace(defaultFleet, trackedRace); column.setTrackedRace(defaultFleet, trackedRace);
assertSame(column.getRaceLog(defaultFleet), trackedRace.getRaceLog()); assertSame(column.getRaceLog(defaultFleet), trackedRace.getRaceLog(column.getRaceLogIdentifier(defaultFleet)));
} }
@Test @Test
@@ -72,7 +74,7 @@ public class RaceLogAttachOnTrackedRaceTest {
column.setTrackedRace(fleet, trackedRace); column.setTrackedRace(fleet, trackedRace);
column.setTrackedRace(fleet, null); column.setTrackedRace(fleet, null);
assertNull(trackedRace.getRaceLog()); assertNull(trackedRace.getRaceLog(column.getRaceLogIdentifier(fleet)));
} }
@Test @Test
@@ -86,8 +88,8 @@ public class RaceLogAttachOnTrackedRaceTest {
column.setTrackedRace(aFleet, trackedRace); column.setTrackedRace(aFleet, trackedRace);
assertSame(column.getRaceLog(aFleet), trackedRace.getRaceLog()); assertSame(column.getRaceLog(aFleet), trackedRace.getRaceLog(column.getRaceLogIdentifier(aFleet)));
assertNotSame(column.getRaceLog(bFleet), trackedRace.getRaceLog()); assertNotSame(column.getRaceLog(bFleet), trackedRace.getRaceLog(column.getRaceIdentifier(bFleet)));
} }
@Test @Test
@@ -102,8 +104,8 @@ public class RaceLogAttachOnTrackedRaceTest {
column.setTrackedRace(fleet, firstRace); column.setTrackedRace(fleet, firstRace);
column.setTrackedRace(fleet, secondRace); column.setTrackedRace(fleet, secondRace);
assertNull(firstRace.getRaceLog()); assertNull(firstRace.getRaceLog(column.getRaceLogIdentifier(fleet)));
assertSame(column.getRaceLog(fleet), secondRace.getRaceLog()); assertSame(column.getRaceLog(fleet), secondRace.getRaceLog(column.getRaceLogIdentifier(fleet)));
} }
} }
@@ -29,7 +29,7 @@ public class RaceLogTest {
public void testAddingTwoEventsOfDifferentPassesButSameTimestamps() { public void testAddingTwoEventsOfDifferentPassesButSameTimestamps() {
TimePoint t1 = MillisecondsTimePoint.now(); TimePoint t1 = MillisecondsTimePoint.now();
int passId = 0; int passId = 0;
RaceLog raceLog = new RaceLogImpl("RaceLogTest"); RaceLog raceLog = new RaceLogImpl("RaceLogTest", "test-identifier");
RaceLogFlagEvent rcEvent1 = RaceLogEventFactory.INSTANCE.createFlagEvent(t1, UUID.randomUUID(), new ArrayList<Competitor>(), passId, Flags.CLASS, Flags.NONE, false); RaceLogFlagEvent rcEvent1 = RaceLogEventFactory.INSTANCE.createFlagEvent(t1, UUID.randomUUID(), new ArrayList<Competitor>(), passId, Flags.CLASS, Flags.NONE, false);
RaceLogFlagEvent rcEvent2 = RaceLogEventFactory.INSTANCE.createFlagEvent(t1, UUID.randomUUID(), new ArrayList<Competitor>(), ++passId, Flags.CLASS, Flags.NONE, false); RaceLogFlagEvent rcEvent2 = RaceLogEventFactory.INSTANCE.createFlagEvent(t1, UUID.randomUUID(), new ArrayList<Competitor>(), ++passId, Flags.CLASS, Flags.NONE, false);
@@ -48,7 +48,7 @@ public class RaceLogTest {
@Test @Test
public void testAddOfEvent() { public void testAddOfEvent() {
RaceLog rcEventTrack = new RaceLogImpl("RaceLogTest"); RaceLog rcEventTrack = new RaceLogImpl("RaceLogTest", "test-identifier");
TimePoint t1 = MillisecondsTimePoint.now(); TimePoint t1 = MillisecondsTimePoint.now();
int passId = 0; int passId = 0;
boolean isDisplayed = true; boolean isDisplayed = true;
@@ -75,7 +75,7 @@ public class RaceLogTest {
@Test @Test
public void testTrackAPIForEventLogUsage() { public void testTrackAPIForEventLogUsage() {
RaceLog rcEventTrack = new RaceLogImpl("RaceLog"); RaceLog rcEventTrack = new RaceLogImpl("RaceLog", "test-identifier");
List<RaceLogEvent> expectedOrderingList = new ArrayList<RaceLogEvent>(); List<RaceLogEvent> expectedOrderingList = new ArrayList<RaceLogEvent>();
TimePoint t1 = MillisecondsTimePoint.now(); TimePoint t1 = MillisecondsTimePoint.now();
TimePoint t2 = new MillisecondsTimePoint(t1.asMillis() - 1000); TimePoint t2 = new MillisecondsTimePoint(t1.asMillis() - 1000);
@@ -405,6 +405,12 @@ public class MockedTrackedRace implements DynamicTrackedRace {
public void setDefaultCourseArea(CourseArea newCourseArea) { public void setDefaultCourseArea(CourseArea newCourseArea) {
// TODO Auto-generated method stub // TODO Auto-generated method stub
} }
@Override
public boolean definesSeriesDiscardThresholds() {
// TODO Auto-generated method stub
return false;
}
}; };
} }
@@ -741,7 +747,7 @@ public class MockedTrackedRace implements DynamicTrackedRace {
} }
@Override @Override
public void detachRaceLog() { public void detachRaceLog(Serializable identifier) {
// TODO Auto-generated method stub // TODO Auto-generated method stub
} }
@@ -753,7 +759,7 @@ public class MockedTrackedRace implements DynamicTrackedRace {
} }
@Override @Override
public RaceLog getRaceLog() { public RaceLog getRaceLog(Serializable identifier) {
// TODO Auto-generated method stub // TODO Auto-generated method stub
return null; return null;
} }
@@ -781,4 +787,22 @@ public class MockedTrackedRace implements DynamicTrackedRace {
// TODO Auto-generated method stub // TODO Auto-generated method stub
return null; return null;
} }
@Override
public void detachAllRaceLogs() {
// TODO Auto-generated method stub
}
@Override
public void invalidateStartTime() {
// TODO Auto-generated method stub
}
@Override
public void invalidateEndTime() {
// TODO Auto-generated method stub
}
} }
@@ -1,5 +1,6 @@
package com.sap.sailing.domain.test.mock; package com.sap.sailing.domain.test.mock;
import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@@ -477,7 +478,7 @@ public class MockedTrackedRaceWithStartTimeAndRanks implements TrackedRace {
} }
@Override @Override
public void detachRaceLog() { public void detachRaceLog(Serializable identifier) {
// TODO Auto-generated method stub // TODO Auto-generated method stub
} }
@@ -489,7 +490,7 @@ public class MockedTrackedRaceWithStartTimeAndRanks implements TrackedRace {
} }
@Override @Override
public RaceLog getRaceLog() { public RaceLog getRaceLog(Serializable identifier) {
// TODO Auto-generated method stub // TODO Auto-generated method stub
return null; return null;
} }
@@ -512,4 +513,10 @@ public class MockedTrackedRaceWithStartTimeAndRanks implements TrackedRace {
return null; return null;
} }
@Override
public void detachAllRaceLogs() {
// TODO Auto-generated method stub
}
} }
@@ -6,6 +6,7 @@ import com.sap.sailing.domain.common.impl.Util;
import com.sap.sailing.domain.common.impl.Util.Pair; import com.sap.sailing.domain.common.impl.Util.Pair;
import com.sap.sailing.domain.leaderboard.Leaderboard; import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.racelog.RaceLog; import com.sap.sailing.domain.racelog.RaceLog;
import com.sap.sailing.domain.racelog.RaceLogIdentifier;
import com.sap.sailing.domain.racelog.RaceLogInformation; import com.sap.sailing.domain.racelog.RaceLogInformation;
import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.util.impl.RaceColumnListeners; import com.sap.sailing.util.impl.RaceColumnListeners;
@@ -148,6 +149,15 @@ public interface RaceColumn extends Named {
*/ */
double getFactor(); double getFactor();
/**
* By default, a competitor's total score is computed by summing up the non-discarded total points of each race
* across the leaderboard, considering the {@link RaceColumn#getFactor() column factors}. Some race columns,
* however, are defined such that participating competitors start with a zero score from this race column on. If
* this method returns <code>true</code>, this column advises the leaderboard and scoring scheme to start counting
* the total points at this column with zero.
*/
boolean isStartsWithZeroScore();
/** /**
* @param factor if <code>null</code>, {@link #getFactor()} will again compute a default value; otherwise, {@link #getFactor()} will * @param factor if <code>null</code>, {@link #getFactor()} will again compute a default value; otherwise, {@link #getFactor()} will
* then return the double value of <code>factor</code>. * then return the double value of <code>factor</code>.
@@ -158,5 +168,12 @@ public interface RaceColumn extends Named {
* If <code>null</code>, the {@link #getFactor() factor} defaults to 1 for non-medal and {@link #DEFAULT_MEDAL_RACE_FACTOR} for * If <code>null</code>, the {@link #getFactor() factor} defaults to 1 for non-medal and {@link #DEFAULT_MEDAL_RACE_FACTOR} for
* medal races. Otherwise, the explicit factor is used. * medal races. Otherwise, the explicit factor is used.
*/ */
Double getExplicitFactor(); Double getExplicitFactor();
/**
* Returns the race log identifier associated with this fleet and race log
* @param fleet
* @return
*/
RaceLogIdentifier getRaceLogIdentifier(Fleet fleet);
} }
@@ -2,7 +2,7 @@ package com.sap.sailing.domain.base;
import java.io.Serializable; import java.io.Serializable;
import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule; import com.sap.sailing.domain.leaderboard.ResultDiscardingRule;
import com.sap.sailing.domain.racelog.RaceLogEvent; import com.sap.sailing.domain.racelog.RaceLogEvent;
import com.sap.sailing.domain.racelog.RaceLogIdentifier; import com.sap.sailing.domain.racelog.RaceLogIdentifier;
import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.domain.tracking.TrackedRace;
@@ -14,6 +14,8 @@ public interface RaceColumnListener extends Serializable {
void isMedalRaceChanged(RaceColumn raceColumn, boolean newIsMedalRace); void isMedalRaceChanged(RaceColumn raceColumn, boolean newIsMedalRace);
void isStartsWithZeroScoreChanged(RaceColumn raceColumn, boolean newIsStartsWithZeroScore);
boolean canAddRaceColumnToContainer(RaceColumn raceColumn); boolean canAddRaceColumnToContainer(RaceColumn raceColumn);
void raceColumnAddedToContainer(RaceColumn raceColumn); void raceColumnAddedToContainer(RaceColumn raceColumn);
@@ -26,7 +28,7 @@ public interface RaceColumnListener extends Serializable {
void competitorDisplayNameChanged(Competitor competitor, String oldDisplayName, String displayName); void competitorDisplayNameChanged(Competitor competitor, String oldDisplayName, String displayName);
void resultDiscardingRuleChanged(ThresholdBasedResultDiscardingRule oldDiscardingRule, ThresholdBasedResultDiscardingRule newDiscardingRule); void resultDiscardingRuleChanged(ResultDiscardingRule oldDiscardingRule, ResultDiscardingRule newDiscardingRule);
void raceLogEventAdded(RaceColumn raceColumn, RaceLogIdentifier raceLogIdentifier, RaceLogEvent event); void raceLogEventAdded(RaceColumn raceColumn, RaceLogIdentifier raceLogIdentifier, RaceLogEvent event);
@@ -93,4 +93,11 @@ public interface Regatta extends Named, WithID {
void addRaceColumnListener(RaceColumnListener listener); void addRaceColumnListener(RaceColumnListener listener);
void removeRaceColumnListener(RaceColumnListener listener); void removeRaceColumnListener(RaceColumnListener listener);
/**
* @return whether this regatta defines its local per-series result discarding rules; if so, any leaderboard based
* on the regatta has to respect this and has to use a result discarding rule implementation that
* keeps discards local to each series rather than spreading them across the entire leaderboard.
*/
boolean definesSeriesDiscardThresholds();
} }
@@ -1,17 +1,23 @@
package com.sap.sailing.domain.base; package com.sap.sailing.domain.base;
import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule;
import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.TrackedRegattaRegistry; import com.sap.sailing.domain.tracking.TrackedRegattaRegistry;
/** /**
* A series is a part of a {@link Regatta}. Rounds are ordered within the regatta, and rules for who is assigned to * A series is a part of a {@link Regatta}. Series are ordered within the regatta, and rules for who is assigned to
* which series may exist on the regatta. For example, a regatta may have a qualification series, a final series, and a * which series may exist on the regatta. For example, a regatta may have a qualification series, a final series, and a
* medal "series" with usually only a single medal race. Each series has one or more fleets, deciding how many races per * medal "series" with usually only a single medal race. Each series has one or more fleets, deciding how many races per
* race column have to be run in this round. For example, if the 49er regatta has so many competitors that they cannot * race column have to be run in this round. For example, if the 49er regatta has so many competitors that they cannot
* all start in one race, the qualification round can be split into two {@link Fleet}s, "Yellow" and "Blue," each * all start in one race, the qualification series can be split into two {@link Fleet}s, "Yellow" and "Blue," each
* getting their separate races. Fleet assignment may or may not vary. This usually depends on the round's * getting their separate races. Fleet assignment may or may not vary. This usually depends on the series'
* characteristics of having ordered or unordered fleets.<p> * characteristics of having ordered or unordered fleets.<p>
* *
* A series may define its result discarding scheme. If it does, a regatta leaderboard for the containing regatta
* has to adhere to this and may not define its own cross-cutting result discarding scheme. If one or more series
* in the regatta choose to define their own result discarding scheme, discards are determined per series and not
* per leaderboard.<p>
*
* To receive notifications when {@link TrackedRace tracked races} are linked to or unlinked from any of this series' * To receive notifications when {@link TrackedRace tracked races} are linked to or unlinked from any of this series'
* columns, {@link RaceColumnListener}s can be added / removed. * columns, {@link RaceColumnListener}s can be added / removed.
* *
@@ -42,6 +48,14 @@ public interface Series extends SeriesBase {
void removeRaceColumn(String raceColumnName); void removeRaceColumn(String raceColumnName);
/**
* If not <code>null</code>, a containing regatta's leaderboard must obey this rule and in particular cannot define
* a "cross-cutting" result discarding rule where discards may be arbitrarily distributed across series.
*/
ThresholdBasedResultDiscardingRule getResultDiscardingRule();
void setResultDiscardingRule(ThresholdBasedResultDiscardingRule resultDiscardingRule);
Regatta getRegatta(); Regatta getRegatta();
/** /**
@@ -52,4 +66,27 @@ public interface Series extends SeriesBase {
void addRaceColumnListener(RaceColumnListener listener); void addRaceColumnListener(RaceColumnListener listener);
void removeRaceColumnListener(RaceColumnListener listener); void removeRaceColumnListener(RaceColumnListener listener);
/**
* @return whether this series defines its local result discarding rule; if so, any leaderboard based on the
* enclosing regatta has to respect this and has to use a result discarding rule implementation that keeps
* discards local to each series rather than spreading them across the entire leaderboard.
*/
boolean definesSeriesDiscardThresholds();
/**
* By default, a competitor's total score is computed by summing up the non-discarded total points of each race
* across the leaderboard, considering the {@link RaceColumn#getFactor() column factors}. Some series, however, are
* defined such that participating competitors start the series with a zero score. Any carry-forward would then have
* to be modeled as a first carry-forward "race" in the series which the ISAF in 2013 has defined as being a race
* logically, therefore also being discardable. If this method returns <code>true</code>, this series advises the
* leaderboard and scoring scheme to start counting the total points at this series with zero.
* <p>
*
* This condition propagates to the first race column of the series which is then used by the leaderboard and
* scoring scheme.
*/
boolean isStartsWithZeroScore();
void setStartsWithZeroScore(boolean startsWithZeroScore);
} }
@@ -16,6 +16,7 @@ import com.sap.sailing.domain.racelog.RaceLogIdentifier;
import com.sap.sailing.domain.racelog.RaceLogIdentifierTemplate; import com.sap.sailing.domain.racelog.RaceLogIdentifierTemplate;
import com.sap.sailing.domain.racelog.RaceLogInformation; import com.sap.sailing.domain.racelog.RaceLogInformation;
import com.sap.sailing.domain.racelog.RaceLogStore; import com.sap.sailing.domain.racelog.RaceLogStore;
import com.sap.sailing.domain.racelog.impl.RaceLogIdentifierImpl;
import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.domain.tracking.TrackedRace;
public abstract class AbstractRaceColumn extends SimpleAbstractRaceColumn implements RaceColumn { public abstract class AbstractRaceColumn extends SimpleAbstractRaceColumn implements RaceColumn {
@@ -25,6 +26,7 @@ public abstract class AbstractRaceColumn extends SimpleAbstractRaceColumn implem
private final Map<Fleet, RaceIdentifier> raceIdentifiers; private final Map<Fleet, RaceIdentifier> raceIdentifiers;
private final Map<Fleet, RaceLog> raceLogs; private final Map<Fleet, RaceLog> raceLogs;
/** /**
* holds the race log identifer template needed to create the appropriate RaceLogIdentifer that is constructed from the * holds the race log identifer template needed to create the appropriate RaceLogIdentifer that is constructed from the
* parent object name, name of this raceColumn and the name of a fleet of this raceColumn to access the RaceLog in the * parent object name, name of this raceColumn and the name of a fleet of this raceColumn to access the RaceLog in the
@@ -55,6 +57,11 @@ public abstract class AbstractRaceColumn extends SimpleAbstractRaceColumn implem
public RaceLog getRaceLog(Fleet fleet) { public RaceLog getRaceLog(Fleet fleet) {
return raceLogs.get(fleet); return raceLogs.get(fleet);
} }
@Override
public RaceLogIdentifier getRaceLogIdentifier(Fleet fleet) {
return new RaceLogIdentifierImpl(raceLogsIdentifierTemplate, getName(), fleet);
}
@Override @Override
public TrackedRace getTrackedRace(Fleet fleet) { public TrackedRace getTrackedRace(Fleet fleet) {
@@ -75,7 +82,8 @@ public abstract class AbstractRaceColumn extends SimpleAbstractRaceColumn implem
} }
} }
if (previouslyLinkedRace != null) { if (previouslyLinkedRace != null) {
previouslyLinkedRace.detachRaceLog(); RaceLogIdentifierImpl identifier = new RaceLogIdentifierImpl(raceLogsIdentifierTemplate, getName(), fleet);
previouslyLinkedRace.detachRaceLog(identifier);
getRaceColumnListeners().notifyListenersAboutTrackedRaceUnlinked(this, fleet, previouslyLinkedRace); getRaceColumnListeners().notifyListenersAboutTrackedRaceUnlinked(this, fleet, previouslyLinkedRace);
} }
if (trackedRace != null) { if (trackedRace != null) {
@@ -1,6 +1,8 @@
package com.sap.sailing.domain.base.impl; package com.sap.sailing.domain.base.impl;
import java.util.Iterator;
import com.sap.sailing.domain.base.Fleet; import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.RaceColumnInSeries; import com.sap.sailing.domain.base.RaceColumnInSeries;
import com.sap.sailing.domain.base.RaceDefinition; import com.sap.sailing.domain.base.RaceDefinition;
@@ -89,5 +91,14 @@ public class RaceColumnInSeriesImpl extends AbstractRaceColumn implements RaceCo
// re-associating the TrackedRace needs to happen before the super call because the RaceIdentifier may have changed // re-associating the TrackedRace needs to happen before the super call because the RaceIdentifier may have changed
super.setTrackedRace(fleet, trackedRace); super.setTrackedRace(fleet, trackedRace);
} }
private boolean isFirstColumnInSeries() {
Iterator<? extends RaceColumnInSeries> columnIter = getSeries().getRaceColumns().iterator();
return columnIter.hasNext() && columnIter.next() == this;
}
@Override
public boolean isStartsWithZeroScore() {
return getSeries().isStartsWithZeroScore() && isFirstColumnInSeries();
}
} }
@@ -9,7 +9,6 @@ import java.util.HashSet;
import java.util.Set; import java.util.Set;
import java.util.logging.Logger; import java.util.logging.Logger;
import com.sap.sailing.domain.base.BoatClass; import com.sap.sailing.domain.base.BoatClass;
import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.CourseArea; import com.sap.sailing.domain.base.CourseArea;
@@ -24,8 +23,8 @@ import com.sap.sailing.domain.common.LeaderboardNameConstants;
import com.sap.sailing.domain.common.RegattaIdentifier; import com.sap.sailing.domain.common.RegattaIdentifier;
import com.sap.sailing.domain.common.RegattaName; import com.sap.sailing.domain.common.RegattaName;
import com.sap.sailing.domain.common.impl.NamedImpl; import com.sap.sailing.domain.common.impl.NamedImpl;
import com.sap.sailing.domain.leaderboard.ResultDiscardingRule;
import com.sap.sailing.domain.leaderboard.ScoringScheme; import com.sap.sailing.domain.leaderboard.ScoringScheme;
import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule;
import com.sap.sailing.domain.racelog.RaceLogEvent; import com.sap.sailing.domain.racelog.RaceLogEvent;
import com.sap.sailing.domain.racelog.RaceLogIdentifier; import com.sap.sailing.domain.racelog.RaceLogIdentifier;
import com.sap.sailing.domain.racelog.RaceLogStore; import com.sap.sailing.domain.racelog.RaceLogStore;
@@ -269,6 +268,11 @@ public class RegattaImpl extends NamedImpl implements Regatta, RaceColumnListene
raceColumnListeners.notifyListenersAboutIsMedalRaceChanged(raceColumn, newIsMedalRace); raceColumnListeners.notifyListenersAboutIsMedalRaceChanged(raceColumn, newIsMedalRace);
} }
@Override
public void isStartsWithZeroScoreChanged(RaceColumn raceColumn, boolean newIsStartsWithZeroScore) {
raceColumnListeners.notifyListenersAboutIsStartsWithZeroScoreChanged(raceColumn, newIsStartsWithZeroScore);
}
@Override @Override
public boolean canAddRaceColumnToContainer(RaceColumn raceColumn) { public boolean canAddRaceColumnToContainer(RaceColumn raceColumn) {
return raceColumnListeners.canAddRaceColumnToContainer(raceColumn); return raceColumnListeners.canAddRaceColumnToContainer(raceColumn);
@@ -302,8 +306,7 @@ public class RegattaImpl extends NamedImpl implements Regatta, RaceColumnListene
} }
@Override @Override
public void resultDiscardingRuleChanged(ThresholdBasedResultDiscardingRule oldDiscardingRule, public void resultDiscardingRuleChanged(ResultDiscardingRule oldDiscardingRule, ResultDiscardingRule newDiscardingRule) {
ThresholdBasedResultDiscardingRule newDiscardingRule) {
raceColumnListeners.notifyListenersAboutResultDiscardingRuleChanged(oldDiscardingRule, newDiscardingRule); raceColumnListeners.notifyListenersAboutResultDiscardingRuleChanged(oldDiscardingRule, newDiscardingRule);
} }
@@ -342,4 +345,19 @@ public class RegattaImpl extends NamedImpl implements Regatta, RaceColumnListene
this.defaultCourseArea = newCourseArea; this.defaultCourseArea = newCourseArea;
} }
/**
* @return whether this regatta defines its local per-series result discarding rules; if so, any leaderboard based
* on the regatta has to respect this and has to use a result discarding rule implementation that
* keeps discards local to each series rather than spreading them across the entire leaderboard.
*/
@Override
public boolean definesSeriesDiscardThresholds() {
for (Series s : series) {
if (s.definesSeriesDiscardThresholds()) {
return true;
}
}
return false;
}
} }
@@ -16,6 +16,7 @@ import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.base.Series; import com.sap.sailing.domain.base.Series;
import com.sap.sailing.domain.common.impl.NamedImpl; import com.sap.sailing.domain.common.impl.NamedImpl;
import com.sap.sailing.domain.common.impl.Util; import com.sap.sailing.domain.common.impl.Util;
import com.sap.sailing.domain.leaderboard.ResultDiscardingRule;
import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule; import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule;
import com.sap.sailing.domain.racelog.RaceLogEvent; import com.sap.sailing.domain.racelog.RaceLogEvent;
import com.sap.sailing.domain.racelog.RaceLogIdentifier; import com.sap.sailing.domain.racelog.RaceLogIdentifier;
@@ -32,6 +33,8 @@ public class SeriesImpl extends NamedImpl implements Series, RaceColumnListener
private boolean isMedal; private boolean isMedal;
private Regatta regatta; private Regatta regatta;
private final RaceColumnListeners raceColumnListeners; private final RaceColumnListeners raceColumnListeners;
private ThresholdBasedResultDiscardingRule resultDiscardingRule;
private boolean startsWithZeroScore;
/** /**
* @param fleets * @param fleets
@@ -136,32 +139,53 @@ public class SeriesImpl extends NamedImpl implements Series, RaceColumnListener
@Override @Override
public void moveRaceColumnUp(String raceColumnName) { public void moveRaceColumnUp(String raceColumnName) {
boolean didFirstColumnChange = false; // if it changes and the series starts scoring with zero, notify the change
// start at second element because first can't be moved up // start at second element because first can't be moved up
for (int i=1; i<raceColumns.size(); i++) { for (int i=1; i<raceColumns.size(); i++) {
RaceColumnInSeries rc = raceColumns.get(i); RaceColumnInSeries rc = raceColumns.get(i);
if (rc.getName().equals(raceColumnName)) { if (rc.getName().equals(raceColumnName)) {
raceColumns.remove(i); raceColumns.remove(i);
if (i==1) {
didFirstColumnChange = true;
}
raceColumnListeners.notifyListenersAboutRaceColumnRemovedFromContainer(rc); raceColumnListeners.notifyListenersAboutRaceColumnRemovedFromContainer(rc);
raceColumns.add(i-1, rc); raceColumns.add(i-1, rc);
raceColumnListeners.notifyListenersAboutRaceColumnAddedToContainer(rc); raceColumnListeners.notifyListenersAboutRaceColumnAddedToContainer(rc);
break; break;
} }
} }
if (didFirstColumnChange && startsWithZeroScore) {
notifyIsStartsWithZeroScoreChangedForFirstTwoColumns();
}
}
private void notifyIsStartsWithZeroScoreChangedForFirstTwoColumns() {
final RaceColumnInSeries first = raceColumns.get(0);
raceColumnListeners.notifyListenersAboutIsStartsWithZeroScoreChanged(first, first.isStartsWithZeroScore());
final RaceColumnInSeries second = raceColumns.get(1);
raceColumnListeners.notifyListenersAboutIsStartsWithZeroScoreChanged(second, second.isStartsWithZeroScore());
} }
@Override @Override
public void moveRaceColumnDown(String raceColumnName) { public void moveRaceColumnDown(String raceColumnName) {
boolean didFirstColumnChange = false; // if it changes and the series starts scoring with zero, notify the change
// end at second-last element because last can't be moved down // end at second-last element because last can't be moved down
for (int i=0; i<raceColumns.size()-1; i++) { for (int i=0; i<raceColumns.size()-1; i++) {
RaceColumnInSeries rc = raceColumns.get(i); RaceColumnInSeries rc = raceColumns.get(i);
if (rc.getName().equals(raceColumnName)) { if (rc.getName().equals(raceColumnName)) {
raceColumns.remove(i); raceColumns.remove(i);
if (i==0) {
didFirstColumnChange = true;
}
raceColumnListeners.notifyListenersAboutRaceColumnRemovedFromContainer(rc); raceColumnListeners.notifyListenersAboutRaceColumnRemovedFromContainer(rc);
raceColumns.add(i+1, rc); raceColumns.add(i+1, rc);
raceColumnListeners.notifyListenersAboutRaceColumnAddedToContainer(rc); raceColumnListeners.notifyListenersAboutRaceColumnAddedToContainer(rc);
break; break;
} }
} }
if (didFirstColumnChange && startsWithZeroScore) {
notifyIsStartsWithZeroScoreChangedForFirstTwoColumns();
}
} }
@Override @Override
@@ -209,6 +233,11 @@ public class SeriesImpl extends NamedImpl implements Series, RaceColumnListener
raceColumnListeners.notifyListenersAboutIsMedalRaceChanged(raceColumn, newIsMedalRace); raceColumnListeners.notifyListenersAboutIsMedalRaceChanged(raceColumn, newIsMedalRace);
} }
@Override
public void isStartsWithZeroScoreChanged(RaceColumn raceColumn, boolean newIsStartsWithZeroScore) {
raceColumnListeners.notifyListenersAboutIsStartsWithZeroScoreChanged(raceColumn, newIsStartsWithZeroScore);
}
/** /**
* A series listens on its columns; individual columns, however, don't ask whether they can be added; the series itself does. * A series listens on its columns; individual columns, however, don't ask whether they can be added; the series itself does.
* *
@@ -245,8 +274,7 @@ public class SeriesImpl extends NamedImpl implements Series, RaceColumnListener
} }
@Override @Override
public void resultDiscardingRuleChanged(ThresholdBasedResultDiscardingRule oldDiscardingRule, public void resultDiscardingRuleChanged(ResultDiscardingRule oldDiscardingRule, ResultDiscardingRule newDiscardingRule) {
ThresholdBasedResultDiscardingRule newDiscardingRule) {
raceColumnListeners.notifyListenersAboutResultDiscardingRuleChanged(oldDiscardingRule, newDiscardingRule); raceColumnListeners.notifyListenersAboutResultDiscardingRuleChanged(oldDiscardingRule, newDiscardingRule);
} }
@@ -259,4 +287,48 @@ public class SeriesImpl extends NamedImpl implements Series, RaceColumnListener
public boolean isTransient() { public boolean isTransient() {
return false; return false;
} }
@Override
public ThresholdBasedResultDiscardingRule getResultDiscardingRule() {
return resultDiscardingRule;
}
@Override
public void setResultDiscardingRule(ThresholdBasedResultDiscardingRule resultDiscardingRule) {
this.resultDiscardingRule = resultDiscardingRule;
}
@Override
public boolean definesSeriesDiscardThresholds() {
return getResultDiscardingRule() != null;
}
@Override
public boolean isStartsWithZeroScore() {
return startsWithZeroScore;
}
/**
* @return <code>null</code> if the series doesn't currently have any columns
*/
private RaceColumn getFirstRaceColumn() {
RaceColumn result = null;
if (!raceColumns.isEmpty()) {
result = raceColumns.get(0);
}
return result;
}
@Override
public void setStartsWithZeroScore(boolean startsWithZeroScore) {
boolean oldStartsWithZeroScore = this.startsWithZeroScore;
if (oldStartsWithZeroScore != startsWithZeroScore) {
this.startsWithZeroScore = startsWithZeroScore;
RaceColumn firstRaceColumnInSeries = getFirstRaceColumn();
if (firstRaceColumnInSeries != null) {
raceColumnListeners.notifyListenersAboutIsStartsWithZeroScoreChanged(firstRaceColumnInSeries, startsWithZeroScore);
}
}
}
} }
@@ -75,4 +75,13 @@ public abstract class SimpleAbstractRaceColumn implements RaceColumn {
public Double getExplicitFactor() { public Double getExplicitFactor() {
return explicitFactor; return explicitFactor;
} }
/**
* Returns <code>false</code>, meaning that by default when aggregating total scores across a leaderboard, this race column's score
* is added to the aggregate of previous scores.
*/
@Override
public boolean isStartsWithZeroScore() {
return false;
}
} }
@@ -198,7 +198,7 @@ public interface Leaderboard extends Named {
/** /**
* Sums up the {@link #getTotalPoints(Competitor, TrackedRace, TimePoint) total points} of <code>competitor</code> * Sums up the {@link #getTotalPoints(Competitor, TrackedRace, TimePoint) total points} of <code>competitor</code>
* across all races tracked by this leaderboard. * across all races tracked by this leaderboard, respecting the {@link RaceColumn#isStartsWithZeroScore()} property.
*/ */
Double getTotalPoints(Competitor competitor, TimePoint timePoint) throws NoWindException; Double getTotalPoints(Competitor competitor, TimePoint timePoint) throws NoWindException;
@@ -282,8 +282,6 @@ public interface Leaderboard extends Named {
SettableScoreCorrection getScoreCorrection(); SettableScoreCorrection getScoreCorrection();
ThresholdBasedResultDiscardingRule getResultDiscardingRule();
Competitor getCompetitorByName(String competitorName); Competitor getCompetitorByName(String competitorName);
void setDisplayName(Competitor competitor, String displayName); void setDisplayName(Competitor competitor, String displayName);
@@ -314,7 +312,19 @@ public interface Leaderboard extends Named {
*/ */
boolean countRaceForComparisonWithDiscardingThresholds(Competitor competitor, RaceColumn raceColumn, TimePoint timePoint); boolean countRaceForComparisonWithDiscardingThresholds(Competitor competitor, RaceColumn raceColumn, TimePoint timePoint);
public void setResultDiscardingRule(ThresholdBasedResultDiscardingRule discardingRule); /**
* Returns the result discarding rule for this leaderboard. This may be an explicit rule set for the entire leaderboard,
* or it may be an implicit rule that relies on other data such as a regatta's series-specific result discarding rules.
*/
ResultDiscardingRule getResultDiscardingRule();
/**
* Trying to set a cross-leaderboard result discarding rule may fail with an exception if the leaderboard obtains
* its result discarding rule in another way. For example, if the leaderboard is a {@link RegattaLeaderboard} and the
* regatta's series define their own result discarding rules, the regatta leaderboard will use a composite
* result discarding rule that considers all series-specific result discarding rules.
*/
public void setCrossLeaderboardResultDiscardingRule(ThresholdBasedResultDiscardingRule discardingRule);
Competitor getCompetitorByIdAsString(String idAsString); Competitor getCompetitorByIdAsString(String idAsString);
@@ -228,6 +228,11 @@ public class LeaderboardCacheManager {
removeFromCache(leaderboard); removeFromCache(leaderboard);
} }
@Override
public void isStartsWithZeroScoreChanged(RaceColumn raceColumn, boolean newIsStartsWithZeroScore) {
removeFromCache(leaderboard);
}
@Override @Override
public boolean canAddRaceColumnToContainer(RaceColumn raceColumn) { public boolean canAddRaceColumnToContainer(RaceColumn raceColumn) {
return true; return true;
@@ -254,8 +259,7 @@ public class LeaderboardCacheManager {
} }
@Override @Override
public void resultDiscardingRuleChanged(ThresholdBasedResultDiscardingRule oldDiscardingRule, public void resultDiscardingRuleChanged(ResultDiscardingRule oldDiscardingRule, ResultDiscardingRule newDiscardingRule) {
ThresholdBasedResultDiscardingRule newDiscardingRule) {
removeFromCache(leaderboard); removeFromCache(leaderboard);
} }
@@ -61,6 +61,7 @@ import com.sap.sailing.domain.common.impl.Util;
import com.sap.sailing.domain.common.impl.Util.Pair; import com.sap.sailing.domain.common.impl.Util.Pair;
import com.sap.sailing.domain.leaderboard.Leaderboard; import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.leaderboard.NumberOfCompetitorsInLeaderboardFetcher; import com.sap.sailing.domain.leaderboard.NumberOfCompetitorsInLeaderboardFetcher;
import com.sap.sailing.domain.leaderboard.ResultDiscardingRule;
import com.sap.sailing.domain.leaderboard.ScoreCorrection.Result; import com.sap.sailing.domain.leaderboard.ScoreCorrection.Result;
import com.sap.sailing.domain.leaderboard.SettableScoreCorrection; import com.sap.sailing.domain.leaderboard.SettableScoreCorrection;
import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule; import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule;
@@ -100,7 +101,7 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
private final SettableScoreCorrection scoreCorrection; private final SettableScoreCorrection scoreCorrection;
private ThresholdBasedResultDiscardingRule resultDiscardingRule; private ThresholdBasedResultDiscardingRule crossLeaderboardResultDiscardingRule;
/** /**
* The optional display name mappings for competitors. This allows a user to override the tracking-provided * The optional display name mappings for competitors. This allows a user to override the tracking-provided
@@ -330,7 +331,7 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
this.carriedPoints = new HashMap<Competitor, Double>(); this.carriedPoints = new HashMap<Competitor, Double>();
this.scoreCorrection = scoreCorrection; this.scoreCorrection = scoreCorrection;
this.displayNames = new HashMap<Competitor, String>(); this.displayNames = new HashMap<Competitor, String>();
this.resultDiscardingRule = resultDiscardingRule; this.crossLeaderboardResultDiscardingRule = resultDiscardingRule;
this.suppressedCompetitors = Collections.synchronizedSet(new HashSet<Competitor>()); this.suppressedCompetitors = Collections.synchronizedSet(new HashSet<Competitor>());
this.raceColumnListeners = new RaceColumnListeners(); this.raceColumnListeners = new RaceColumnListeners();
this.raceDetailsAtEndOfTrackingCache = new HashMap<Pair<TrackedRace, Competitor>, RunnableFuture<RaceDetails>>(); this.raceDetailsAtEndOfTrackingCache = new HashMap<Pair<TrackedRace, Competitor>, RunnableFuture<RaceDetails>>();
@@ -383,8 +384,8 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
} }
@Override @Override
public ThresholdBasedResultDiscardingRule getResultDiscardingRule() { public ResultDiscardingRule getResultDiscardingRule() {
return resultDiscardingRule; return crossLeaderboardResultDiscardingRule;
} }
@Override @Override
@@ -423,9 +424,9 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
} }
@Override @Override
public void setResultDiscardingRule(ThresholdBasedResultDiscardingRule discardingRule) { public void setCrossLeaderboardResultDiscardingRule(ThresholdBasedResultDiscardingRule discardingRule) {
ThresholdBasedResultDiscardingRule oldDiscardingRule = getResultDiscardingRule(); ResultDiscardingRule oldDiscardingRule = getResultDiscardingRule();
this.resultDiscardingRule = discardingRule; this.crossLeaderboardResultDiscardingRule = discardingRule;
getRaceColumnListeners().notifyListenersAboutResultDiscardingRuleChanged(oldDiscardingRule, discardingRule); getRaceColumnListeners().notifyListenersAboutResultDiscardingRuleChanged(oldDiscardingRule, discardingRule);
} }
@@ -480,11 +481,20 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
@Override @Override
public Double getTotalPoints(Competitor competitor, TimePoint timePoint) throws NoWindException { public Double getTotalPoints(Competitor competitor, TimePoint timePoint) throws NoWindException {
// when a column with isStartsWithZeroScore() is found, only reset score if the competitor scored in any race from there on
boolean needToResetScoreUponNextNonEmptyEntry = false;
double result = getCarriedPoints(competitor); double result = getCarriedPoints(competitor);
for (RaceColumn r : getRaceColumns()) { for (RaceColumn r : getRaceColumns()) {
if (r.isStartsWithZeroScore()) {
needToResetScoreUponNextNonEmptyEntry = true;
}
if (getScoringScheme().isValidInTotalScore(this, r, timePoint)) { if (getScoringScheme().isValidInTotalScore(this, r, timePoint)) {
final Double totalPoints = getTotalPoints(competitor, r, timePoint); final Double totalPoints = getTotalPoints(competitor, r, timePoint);
if (totalPoints != null) { if (totalPoints != null) {
if (needToResetScoreUponNextNonEmptyEntry) {
result = 0;
needToResetScoreUponNextNonEmptyEntry = false;
}
result += totalPoints; result += totalPoints;
} }
} }
@@ -694,6 +704,11 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
getRaceColumnListeners().notifyListenersAboutIsMedalRaceChanged(raceColumn, newIsMedalRace); getRaceColumnListeners().notifyListenersAboutIsMedalRaceChanged(raceColumn, newIsMedalRace);
} }
@Override
public void isStartsWithZeroScoreChanged(RaceColumn raceColumn, boolean newIsStartsWithZeroScore) {
getRaceColumnListeners().notifyListenersAboutIsStartsWithZeroScoreChanged(raceColumn, newIsStartsWithZeroScore);
}
@Override @Override
public void raceColumnMoved(RaceColumn raceColumn, int newIndex) { public void raceColumnMoved(RaceColumn raceColumn, int newIndex) {
getRaceColumnListeners().notifyListenersAboutRaceColumnMoved(raceColumn, newIndex); getRaceColumnListeners().notifyListenersAboutRaceColumnMoved(raceColumn, newIndex);
@@ -735,8 +750,7 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
} }
@Override @Override
public void resultDiscardingRuleChanged(ThresholdBasedResultDiscardingRule oldDiscardingRule, public void resultDiscardingRuleChanged(ResultDiscardingRule oldDiscardingRule, ResultDiscardingRule newDiscardingRule) {
ThresholdBasedResultDiscardingRule newDiscardingRule) {
getRaceColumnListeners().notifyListenersAboutResultDiscardingRuleChanged(oldDiscardingRule, newDiscardingRule); getRaceColumnListeners().notifyListenersAboutResultDiscardingRuleChanged(oldDiscardingRule, newDiscardingRule);
} }
@@ -1021,7 +1035,11 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
result.setDelayToLiveInMillisForLatestRace(this.getDelayToLiveInMillis()); result.setDelayToLiveInMillisForLatestRace(this.getDelayToLiveInMillis());
result.rows = new HashMap<CompetitorDTO, LeaderboardRowDTO>(); result.rows = new HashMap<CompetitorDTO, LeaderboardRowDTO>();
result.hasCarriedPoints = this.hasCarriedPoints(); result.hasCarriedPoints = this.hasCarriedPoints();
result.discardThresholds = this.getResultDiscardingRule().getDiscardIndexResultsStartingWithHowManyRaces(); if (this.getResultDiscardingRule() instanceof ThresholdBasedResultDiscardingRule) {
result.discardThresholds = ((ThresholdBasedResultDiscardingRule) this.getResultDiscardingRule()).getDiscardIndexResultsStartingWithHowManyRaces();
} else {
result.discardThresholds = null;
}
// Computing the competitor leg ranks is expensive, especially in live mode, in case new events keep invalidating // Computing the competitor leg ranks is expensive, especially in live mode, in case new events keep invalidating
// the ranks cache in TrackedLegImpl. Then problem then is that the sorting based on wind data is repeated for each // the ranks cache in TrackedLegImpl. Then problem then is that the sorting based on wind data is repeated for each
// competitor, leading to square effort. We therefore need to compute the leg ranks for those race where leg details // competitor, leading to square effort. We therefore need to compute the leg ranks for those race where leg details
@@ -1052,6 +1070,7 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
row.competitor = competitorDTO; row.competitor = competitorDTO;
row.fieldsByRaceColumnName = new HashMap<String, LeaderboardEntryDTO>(); row.fieldsByRaceColumnName = new HashMap<String, LeaderboardEntryDTO>();
row.carriedPoints = this.hasCarriedPoints(competitor) ? this.getCarriedPoints(competitor) : null; row.carriedPoints = this.hasCarriedPoints(competitor) ? this.getCarriedPoints(competitor) : null;
row.totalPoints = this.getTotalPoints(competitor, timePoint);
addOverallDetailsToRow(timePoint, competitor, row); addOverallDetailsToRow(timePoint, competitor, row);
result.competitors.add(competitorDTO); result.competitors.add(competitorDTO);
Map<String, Future<LeaderboardEntryDTO>> futuresForColumnName = new HashMap<String, Future<LeaderboardEntryDTO>>(); Map<String, Future<LeaderboardEntryDTO>> futuresForColumnName = new HashMap<String, Future<LeaderboardEntryDTO>>();
@@ -99,17 +99,31 @@ public class LeaderboardTotalRankComparator implements Comparator<Competitor> {
double o2ScoreSum = getLeaderboard().getCarriedPoints(o2); double o2ScoreSum = getLeaderboard().getCarriedPoints(o2);
Double o1MedalRaceScore = 0.0; Double o1MedalRaceScore = 0.0;
Double o2MedalRaceScore = 0.0; Double o2MedalRaceScore = 0.0;
// When a column has isStartsWithZeroScore, the competitor's score only need to be reset to zero if from there on
// the competitor scored in this or any subsequent columns
boolean needToResetO1ScoreUponNextValidResult = false;
boolean needToResetO2ScoreUponNextValidResult = false;
for (RaceColumn raceColumn : getLeaderboard().getRaceColumns()) { for (RaceColumn raceColumn : getLeaderboard().getRaceColumns()) {
needToResetO1ScoreUponNextValidResult = raceColumn.isStartsWithZeroScore();
needToResetO2ScoreUponNextValidResult = raceColumn.isStartsWithZeroScore();
if (getLeaderboard().getScoringScheme().isValidInTotalScore(getLeaderboard(), raceColumn, timePoint)) { if (getLeaderboard().getScoringScheme().isValidInTotalScore(getLeaderboard(), raceColumn, timePoint)) {
int preemptiveColumnResult = 0; int preemptiveColumnResult = 0;
final Double o1Score = totalPointsCache.get(new Pair<Competitor, RaceColumn>(o1, raceColumn)); final Double o1Score = totalPointsCache.get(new Pair<Competitor, RaceColumn>(o1, raceColumn));
if (o1Score != null) { if (o1Score != null) {
o1Scores.add(new Pair<RaceColumn, Double>(raceColumn, o1Score)); o1Scores.add(new Pair<RaceColumn, Double>(raceColumn, o1Score));
if (needToResetO1ScoreUponNextValidResult) {
o1ScoreSum = 0;
needToResetO1ScoreUponNextValidResult = false;
}
o1ScoreSum += o1Score; o1ScoreSum += o1Score;
} }
final Double o2Score = totalPointsCache.get(new Pair<Competitor, RaceColumn>(o2, raceColumn)); final Double o2Score = totalPointsCache.get(new Pair<Competitor, RaceColumn>(o2, raceColumn));
if (o2Score != null) { if (o2Score != null) {
o2Scores.add(new Pair<RaceColumn, Double>(raceColumn, o2Score)); o2Scores.add(new Pair<RaceColumn, Double>(raceColumn, o2Score));
if (needToResetO2ScoreUponNextValidResult) {
o2ScoreSum = 0;
needToResetO2ScoreUponNextValidResult = false;
}
o2ScoreSum += o2Score; o2ScoreSum += o2Score;
} }
if (raceColumn.isMedalRace()) { if (raceColumn.isMedalRace()) {
@@ -128,10 +142,8 @@ public class LeaderboardTotalRankComparator implements Comparator<Competitor> {
} }
} }
// now count the races in which they scored; if they scored in a different number of races, prefer the // now count the races in which they scored; if they scored in a different number of races, prefer the
// competitor // competitor who scored more often; otherwise, prefer the competitor who has a better score sum; if score sums are equal,
// who scored more often; otherwise, prefer the competitor who has a better score sum; if score sums are equal, // break tie by sorting scores and looking for the first score difference.
// break
// tie by sorting scores and looking for the first score difference.
int result = compareByNumberOfRacesScored(o1Scores.size(), o2Scores.size()); int result = compareByNumberOfRacesScored(o1Scores.size(), o2Scores.size());
if (result == 0) { if (result == 0) {
result = compareByScoreSum(o1ScoreSum, o2ScoreSum); result = compareByScoreSum(o1ScoreSum, o2ScoreSum);
@@ -0,0 +1,55 @@
package com.sap.sailing.domain.leaderboard.impl;
import java.util.HashSet;
import java.util.Set;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.RaceColumn;
import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.base.Series;
import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.common.impl.Util;
import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.leaderboard.ResultDiscardingRule;
/**
* A result discarding rule that is used when one or more of a {@link Regatta}'s {@link Series} define their own
* result discarding rule. In this case, discards are decided per series and not per regatta or leaderboard.
*
* @author Axel Uhl (d043530)
*
*/
public class PerSeriesResultDiscardingRuleImpl implements ResultDiscardingRule {
private static final long serialVersionUID = 4596442640424592181L;
private final Regatta regatta;
public PerSeriesResultDiscardingRuleImpl(Regatta regatta) {
super();
this.regatta = regatta;
}
@Override
public Set<RaceColumn> getDiscardedRaceColumns(Competitor competitor, Leaderboard leaderboard,
Iterable<RaceColumn> raceColumnsToConsider, TimePoint timePoint) {
Set<RaceColumn> result = new HashSet<>();
for (Series s : regatta.getSeries()) {
if (s.getResultDiscardingRule() != null) {
Iterable<RaceColumn> columnsToConsiderInSeries = getColumnsToConsiderInSeries(s, raceColumnsToConsider);
result.addAll(s.getResultDiscardingRule().getDiscardedRaceColumns(competitor, leaderboard, columnsToConsiderInSeries, timePoint));
}
}
return result;
}
private Iterable<RaceColumn> getColumnsToConsiderInSeries(Series series, Iterable<RaceColumn> allRaceColumnsToConsider) {
Set<RaceColumn> result = new HashSet<>();
for (RaceColumn seriesColumn : series.getRaceColumns()) {
if (Util.contains(allRaceColumnsToConsider, seriesColumn)) {
result.add(seriesColumn);
}
}
return result;
}
}
@@ -12,6 +12,7 @@ import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.base.Series; import com.sap.sailing.domain.base.Series;
import com.sap.sailing.domain.base.impl.RaceColumnInSeriesImpl; import com.sap.sailing.domain.base.impl.RaceColumnInSeriesImpl;
import com.sap.sailing.domain.leaderboard.RegattaLeaderboard; import com.sap.sailing.domain.leaderboard.RegattaLeaderboard;
import com.sap.sailing.domain.leaderboard.ResultDiscardingRule;
import com.sap.sailing.domain.leaderboard.ScoringScheme; import com.sap.sailing.domain.leaderboard.ScoringScheme;
import com.sap.sailing.domain.leaderboard.SettableScoreCorrection; import com.sap.sailing.domain.leaderboard.SettableScoreCorrection;
import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule; import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule;
@@ -81,4 +82,21 @@ public class RegattaLeaderboardImpl extends AbstractLeaderboardImpl implements R
return regatta.getDefaultCourseArea(); return regatta.getDefaultCourseArea();
} }
/**
* If the regatta' series {@link Regatta#definesSeriesDiscardThresholds() define} their own result discarding rules, this leaderboard uses
* a composite result discarding rule of type {@link PerSeriesResultDiscardingRuleImpl} that contains discards within each series according
* to the series' discarding rules. Otherwise, the default cross-leaderboard result discarding rule is obtained from the super class
* implementation.
*/
@Override
public ResultDiscardingRule getResultDiscardingRule() {
if (regatta.definesSeriesDiscardThresholds()) {
return new PerSeriesResultDiscardingRuleImpl(regatta);
} else {
return super.getResultDiscardingRule();
}
}
} }
@@ -164,7 +164,7 @@ public class ScoreCorrectionImpl implements SettableScoreCorrection {
*/ */
@Override @Override
public Result getCorrectedScore(Callable<Integer> trackedRankProvider, final Competitor competitor, public Result getCorrectedScore(Callable<Integer> trackedRankProvider, final Competitor competitor,
final RaceColumn raceColumn, TimePoint timePoint,NumberOfCompetitorsInLeaderboardFetcher numberOfCompetitorsInLeaderboardFetcher, final RaceColumn raceColumn, TimePoint timePoint, NumberOfCompetitorsInLeaderboardFetcher numberOfCompetitorsInLeaderboardFetcher,
ScoringScheme scoringScheme) { ScoringScheme scoringScheme) {
Double result; Double result;
final MaxPointsReason maxPointsReason = getMaxPointsReason(competitor, raceColumn); final MaxPointsReason maxPointsReason = getMaxPointsReason(competitor, raceColumn);
@@ -30,11 +30,11 @@ import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule;
* @author Axel Uhl (d043530) * @author Axel Uhl (d043530)
* *
*/ */
public class ResultDiscardingRuleImpl implements ThresholdBasedResultDiscardingRule { public class ThresholdBasedResultDiscardingRuleImpl implements ThresholdBasedResultDiscardingRule {
private static final long serialVersionUID = 386341628517357988L; private static final long serialVersionUID = 386341628517357988L;
private final int[] discardIndexResultsStartingWithHowManyRaces; private final int[] discardIndexResultsStartingWithHowManyRaces;
public ResultDiscardingRuleImpl(int[] discardIndexResultsStartingWithHowManyRaces) { public ThresholdBasedResultDiscardingRuleImpl(int[] discardIndexResultsStartingWithHowManyRaces) {
super(); super();
this.discardIndexResultsStartingWithHowManyRaces = new int[discardIndexResultsStartingWithHowManyRaces.length]; this.discardIndexResultsStartingWithHowManyRaces = new int[discardIndexResultsStartingWithHowManyRaces.length];
System.arraycopy(discardIndexResultsStartingWithHowManyRaces, 0, System.arraycopy(discardIndexResultsStartingWithHowManyRaces, 0,
@@ -9,7 +9,7 @@ import com.sap.sailing.domain.base.RaceColumnListener;
import com.sap.sailing.domain.base.impl.SimpleAbstractRaceColumn; import com.sap.sailing.domain.base.impl.SimpleAbstractRaceColumn;
import com.sap.sailing.domain.common.RaceIdentifier; import com.sap.sailing.domain.common.RaceIdentifier;
import com.sap.sailing.domain.leaderboard.Leaderboard; import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule; import com.sap.sailing.domain.leaderboard.ResultDiscardingRule;
import com.sap.sailing.domain.racelog.RaceLog; import com.sap.sailing.domain.racelog.RaceLog;
import com.sap.sailing.domain.racelog.RaceLogEvent; import com.sap.sailing.domain.racelog.RaceLogEvent;
import com.sap.sailing.domain.racelog.RaceLogIdentifier; import com.sap.sailing.domain.racelog.RaceLogIdentifier;
@@ -121,6 +121,11 @@ public class MetaLeaderboardColumn extends SimpleAbstractRaceColumn implements R
getRaceColumnListeners().notifyListenersAboutIsMedalRaceChanged(raceColumn, newIsMedalRace); getRaceColumnListeners().notifyListenersAboutIsMedalRaceChanged(raceColumn, newIsMedalRace);
} }
@Override
public void isStartsWithZeroScoreChanged(RaceColumn raceColumn, boolean newIsStartsWithZeroScore) {
getRaceColumnListeners().notifyListenersAboutIsStartsWithZeroScoreChanged(raceColumn, newIsStartsWithZeroScore);
}
@Override @Override
public boolean canAddRaceColumnToContainer(RaceColumn raceColumn) { public boolean canAddRaceColumnToContainer(RaceColumn raceColumn) {
return getRaceColumnListeners().canAddRaceColumnToContainer(raceColumn); return getRaceColumnListeners().canAddRaceColumnToContainer(raceColumn);
@@ -152,8 +157,7 @@ public class MetaLeaderboardColumn extends SimpleAbstractRaceColumn implements R
} }
@Override @Override
public void resultDiscardingRuleChanged(ThresholdBasedResultDiscardingRule oldDiscardingRule, public void resultDiscardingRuleChanged(ResultDiscardingRule oldDiscardingRule, ResultDiscardingRule newDiscardingRule) {
ThresholdBasedResultDiscardingRule newDiscardingRule) {
getRaceColumnListeners().notifyListenersAboutResultDiscardingRuleChanged(oldDiscardingRule, newDiscardingRule); getRaceColumnListeners().notifyListenersAboutResultDiscardingRuleChanged(oldDiscardingRule, newDiscardingRule);
} }
@@ -166,4 +170,9 @@ public class MetaLeaderboardColumn extends SimpleAbstractRaceColumn implements R
public boolean isTransient() { public boolean isTransient() {
return false; return false;
} }
@Override
public RaceLogIdentifier getRaceLogIdentifier(Fleet fleet) {
return null;
}
} }
@@ -12,6 +12,6 @@ public enum EmptyRaceLogStore implements RaceLogStore {
@Override @Override
public RaceLog getRaceLog(RaceLogIdentifier identifier) { public RaceLog getRaceLog(RaceLogIdentifier identifier) {
return new RaceLogImpl(UUID.randomUUID().toString()); return new RaceLogImpl(UUID.randomUUID().toString(), identifier.getIdentifier());
} }
} }
@@ -88,4 +88,8 @@ public interface DynamicTrackedRace extends TrackedRace {
* the new course design to be published * the new course design to be published
*/ */
void onCourseDesignChangedByRaceCommittee(CourseBase courseDesign); void onCourseDesignChangedByRaceCommittee(CourseBase courseDesign);
void invalidateStartTime();
void invalidateEndTime();
} }
@@ -51,10 +51,12 @@ public interface TrackedRace extends Serializable {
/** /**
* Computes the estimated start time for this race (not to be confused with the {@link #getStartOfTracking()} time * Computes the estimated start time for this race (not to be confused with the {@link #getStartOfTracking()} time
* point which is expected to be before the race start time). When there are no {@link MarkPassing}s for the first * point which is expected to be before the race start time). The highest precedence take the
* mark, <code>null</code> is returned. If there are mark passings for the first mark and the start time is less * {@link #attachedRaceLogs race logs} and their start time events, followed by the field {@link #startTimeReceived}
* than {@link #MAX_TIME_BETWEEN_START_AND_FIRST_MARK_PASSING_IN_MILLISECONDS} before the first mark passing for the * which can explicitly be set using {@link #setStartTimeReceived(TimePoint)}. When there are no {@link MarkPassing}s
* first mark. Otherwise, the first mark passing for the first mark minus * for the first mark, <code>null</code> is returned. If there are mark passings for the first mark and the start
* time is less than {@link #MAX_TIME_BETWEEN_START_AND_FIRST_MARK_PASSING_IN_MILLISECONDS} before the first mark
* passing for the first mark. Otherwise, the first mark passing for the first mark minus
* {@link #MAX_TIME_BETWEEN_START_AND_FIRST_MARK_PASSING_IN_MILLISECONDS} is returned as the race start time. * {@link #MAX_TIME_BETWEEN_START_AND_FIRST_MARK_PASSING_IN_MILLISECONDS} is returned as the race start time.
* <p> * <p>
* *
@@ -482,7 +484,12 @@ public interface TrackedRace extends Serializable {
/** /**
* Detaches the race log associated with this {@link TrackedRace}. * Detaches the race log associated with this {@link TrackedRace}.
*/ */
void detachRaceLog(); void detachRaceLog(Serializable identifier);
/**
* Detaches all {@link RaceLog} instances from this race
*/
void detachAllRaceLogs();
/** /**
* Attaches the passed race log with this {@link TrackedRace}. * Attaches the passed race log with this {@link TrackedRace}.
@@ -494,7 +501,7 @@ public interface TrackedRace extends Serializable {
* Returns the attached race log event track for this race if any. * Returns the attached race log event track for this race if any.
* Otherwise <code>null</code>. * Otherwise <code>null</code>.
*/ */
RaceLog getRaceLog(); RaceLog getRaceLog(Serializable identifier);
/** /**
* a setter for the listener on course design changes. The listener is mostly part of the tracking provider adapter. * a setter for the listener on course design changes. The listener is mostly part of the tracking provider adapter.
@@ -2,6 +2,7 @@ package com.sap.sailing.domain.tracking.impl;
import java.io.IOException; import java.io.IOException;
import java.io.ObjectInputStream; import java.io.ObjectInputStream;
import java.io.Serializable;
import java.util.Collection; import java.util.Collection;
import java.util.ConcurrentModificationException; import java.util.ConcurrentModificationException;
import java.util.HashMap; import java.util.HashMap;
@@ -665,15 +666,16 @@ DynamicTrackedRace, GPSTrackListener<Competitor, GPSFixMoving> {
@Override @Override
public void attachRaceLog(RaceLog raceLog) { public void attachRaceLog(RaceLog raceLog) {
super.attachRaceLog(raceLog); super.attachRaceLog(raceLog);
logListener.addTo(attachedRaceLog); logListener.addTo(raceLog);
} }
@Override @Override
public void detachRaceLog() { public void detachRaceLog(Serializable identifier) {
RaceLog attachedRaceLog = attachedRaceLogs.get(identifier);
if (attachedRaceLog != null) { if (attachedRaceLog != null) {
logListener.removeFrom(attachedRaceLog); logListener.removeFrom(attachedRaceLog);
} }
super.detachRaceLog(); super.detachRaceLog(identifier);
} }
@Override @Override
@@ -1,7 +1,6 @@
package com.sap.sailing.domain.tracking.impl; package com.sap.sailing.domain.tracking.impl;
import com.sap.sailing.domain.base.CourseBase; import com.sap.sailing.domain.base.CourseBase;
import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.racelog.RaceLog; import com.sap.sailing.domain.racelog.RaceLog;
import com.sap.sailing.domain.racelog.RaceLogCourseAreaChangedEvent; import com.sap.sailing.domain.racelog.RaceLogCourseAreaChangedEvent;
import com.sap.sailing.domain.racelog.RaceLogCourseDesignChangedEvent; import com.sap.sailing.domain.racelog.RaceLogCourseDesignChangedEvent;
@@ -17,15 +16,12 @@ import com.sap.sailing.domain.racelog.RaceLogStartProcedureChangedEvent;
import com.sap.sailing.domain.racelog.RaceLogStartTimeEvent; import com.sap.sailing.domain.racelog.RaceLogStartTimeEvent;
import com.sap.sailing.domain.racelog.analyzing.impl.LastPublishedCourseDesignFinder; import com.sap.sailing.domain.racelog.analyzing.impl.LastPublishedCourseDesignFinder;
import com.sap.sailing.domain.racelog.analyzing.impl.RaceStatusAnalyzer; import com.sap.sailing.domain.racelog.analyzing.impl.RaceStatusAnalyzer;
import com.sap.sailing.domain.racelog.analyzing.impl.StartTimeFinder;
import com.sap.sailing.domain.tracking.DynamicTrackedRace; import com.sap.sailing.domain.tracking.DynamicTrackedRace;
public class DynamicTrackedRaceLogListener implements RaceLogEventVisitor { public class DynamicTrackedRaceLogListener implements RaceLogEventVisitor {
private DynamicTrackedRace trackedRace; private DynamicTrackedRace trackedRace;
private StartTimeFinder startTimeFinder;
private RaceStatusAnalyzer statusAnalyzer; private RaceStatusAnalyzer statusAnalyzer;
private LastPublishedCourseDesignFinder courseDesignFinder; private LastPublishedCourseDesignFinder courseDesignFinder;
@@ -36,7 +32,8 @@ public class DynamicTrackedRaceLogListener implements RaceLogEventVisitor {
public void addTo(RaceLog raceLog) { public void addTo(RaceLog raceLog) {
raceLog.addListener(this); raceLog.addListener(this);
startTimeFinder = new StartTimeFinder(raceLog); trackedRace.invalidateStartTime();
trackedRace.invalidateEndTime();
courseDesignFinder = new LastPublishedCourseDesignFinder(raceLog); courseDesignFinder = new LastPublishedCourseDesignFinder(raceLog);
statusAnalyzer = new RaceStatusAnalyzer(raceLog); statusAnalyzer = new RaceStatusAnalyzer(raceLog);
analyze(); analyze();
@@ -55,24 +52,10 @@ public class DynamicTrackedRaceLogListener implements RaceLogEventVisitor {
} }
private void analyze() { private void analyze() {
analyzeStartTime();
analyzeStatus(); analyzeStatus();
analyzeCourseDesign(); analyzeCourseDesign();
} }
private void analyzeStartTime() {
TimePoint startTime = startTimeFinder.getStartTime();
// Because this code can be triggered by an obsolete (delayed) event or on an empty pass...
// ... the current pass's start time might be null
if (startTime != null) {
// ... or setStartTimeReceived(TimePoint) might be called more than once with the same start time.
trackedRace.setStartTimeReceived(startTime);
} else {
// TODO: Can we somehow withdraw a previously set start time?
}
}
private void analyzeStatus() { private void analyzeStatus() {
/* RaceLogRaceStatus newStatus = */statusAnalyzer.getStatus(); /* RaceLogRaceStatus newStatus = */statusAnalyzer.getStatus();
@@ -92,13 +75,11 @@ public class DynamicTrackedRaceLogListener implements RaceLogEventVisitor {
@Override @Override
public void visit(RaceLogPassChangeEvent event) { public void visit(RaceLogPassChangeEvent event) {
analyzeStartTime();
analyzeStatus(); analyzeStatus();
} }
@Override @Override
public void visit(RaceLogStartTimeEvent event) { public void visit(RaceLogStartTimeEvent event) {
analyzeStartTime();
analyzeStatus(); analyzeStatus();
} }
@@ -3,6 +3,7 @@ package com.sap.sailing.domain.tracking.impl;
import java.io.IOException; import java.io.IOException;
import java.io.ObjectInputStream; import java.io.ObjectInputStream;
import java.io.ObjectOutputStream; import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.ConcurrentModificationException; import java.util.ConcurrentModificationException;
@@ -71,6 +72,7 @@ import com.sap.sailing.domain.confidence.Weigher;
import com.sap.sailing.domain.confidence.impl.HyperbolicTimeDifferenceWeigher; import com.sap.sailing.domain.confidence.impl.HyperbolicTimeDifferenceWeigher;
import com.sap.sailing.domain.confidence.impl.PositionAndTimePointWeigher; import com.sap.sailing.domain.confidence.impl.PositionAndTimePointWeigher;
import com.sap.sailing.domain.racelog.RaceLog; import com.sap.sailing.domain.racelog.RaceLog;
import com.sap.sailing.domain.racelog.analyzing.impl.StartTimeFinder;
import com.sap.sailing.domain.tracking.GPSFix; import com.sap.sailing.domain.tracking.GPSFix;
import com.sap.sailing.domain.tracking.GPSFixMoving; import com.sap.sailing.domain.tracking.GPSFixMoving;
import com.sap.sailing.domain.tracking.GPSFixTrack; import com.sap.sailing.domain.tracking.GPSFixTrack;
@@ -220,7 +222,7 @@ public abstract class TrackedRaceImpl implements TrackedRace, CourseListener {
private transient CombinedWindTrackImpl combinedWindTrack; private transient CombinedWindTrackImpl combinedWindTrack;
protected transient RaceLog attachedRaceLog; protected transient HashMap<Serializable, RaceLog> attachedRaceLogs;
/** /**
* The time delay to the current point in time in milliseconds. * The time delay to the current point in time in milliseconds.
@@ -256,6 +258,7 @@ public abstract class TrackedRaceImpl implements TrackedRace, CourseListener {
long millisecondsOverWhichToAverageSpeed, long delayForWindEstimationCacheInvalidation) { long millisecondsOverWhichToAverageSpeed, long delayForWindEstimationCacheInvalidation) {
super(); super();
locksForMarkPassings = new IdentityHashMap<>(); locksForMarkPassings = new IdentityHashMap<>();
attachedRaceLogs = new HashMap<>();
this.status = new TrackedRaceStatusImpl(TrackedRaceStatusEnum.PREPARED, 0.0); this.status = new TrackedRaceStatusImpl(TrackedRaceStatusEnum.PREPARED, 0.0);
this.statusNotifier = new Object[0]; this.statusNotifier = new Object[0];
this.serializationLock = new NamedReentrantReadWriteLock("Serialization lock for tracked race " this.serializationLock = new NamedReentrantReadWriteLock("Serialization lock for tracked race "
@@ -365,6 +368,7 @@ public abstract class TrackedRaceImpl implements TrackedRace, CourseListener {
*/ */
private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException { private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
ois.defaultReadObject(); ois.defaultReadObject();
attachedRaceLogs = new HashMap<>();
markPassingsTimes = new ArrayList<Pair<Waypoint, Pair<TimePoint, TimePoint>>>(); markPassingsTimes = new ArrayList<Pair<Waypoint, Pair<TimePoint, TimePoint>>>();
cacheInvalidationTimerLock = new Object(); cacheInvalidationTimerLock = new Object();
windStore = EmptyWindStore.INSTANCE; windStore = EmptyWindStore.INSTANCE;
@@ -451,11 +455,11 @@ public abstract class TrackedRaceImpl implements TrackedRace, CourseListener {
return endOfTrackingReceived; return endOfTrackingReceived;
} }
protected void invalidateStartTime() { public void invalidateStartTime() {
startTime = null; startTime = null;
} }
protected void invalidateEndTime() { public void invalidateEndTime() {
endTime = null; endTime = null;
} }
@@ -466,35 +470,46 @@ public abstract class TrackedRaceImpl implements TrackedRace, CourseListener {
} }
/** /**
* Calculates the start time of the race from various sources * Calculates the start time of the race from various sources. The highest precedence take the {@link #attachedRaceLogs race logs},
* followed by the field {@link #startTimeReceived} which can explicitly be set using {@link #setStartTimeReceived(TimePoint)}.
* If that does not provide any start time either, a start time is attempted to be inferred from the time points
* of the start mark passing events.
*/ */
@Override @Override
public TimePoint getStartOfRace() { public TimePoint getStartOfRace() {
if (startTime == null) { if (startTime == null) {
startTime = startTimeReceived; for (RaceLog raceLog : attachedRaceLogs.values()) {
// If not null, check if the first mark passing for the start line is too much after the startTimeReceived; startTime = new StartTimeFinder(raceLog).getStartTime();
// if so, return an adjusted, later start time. if (startTime != null) {
// If no official start time was received, try to estimate the start time using the mark passings for the break;
// start line. }
final Waypoint firstWaypoint = getRace().getCourse().getFirstWaypoint(); }
if (firstWaypoint != null) { if (startTime == null) {
if (startTimeReceived != null) { startTime = startTimeReceived;
TimePoint timeOfFirstMarkPassing = getFirstPassingTime(firstWaypoint); // If not null, check if the first mark passing for the start line is too much after the
if (timeOfFirstMarkPassing != null) { // startTimeReceived; if so, return an adjusted, later start time.
long startTimeReceived2timeOfFirstMarkPassingFirstMark = timeOfFirstMarkPassing.asMillis() // If no official start time was received, try to estimate the start time using the mark passings for
- startTimeReceived.asMillis(); // the start line.
if (startTimeReceived2timeOfFirstMarkPassingFirstMark > MAX_TIME_BETWEEN_START_AND_FIRST_MARK_PASSING_IN_MILLISECONDS) { final Waypoint firstWaypoint = getRace().getCourse().getFirstWaypoint();
startTime = new MillisecondsTimePoint(timeOfFirstMarkPassing.asMillis() if (firstWaypoint != null) {
- MAX_TIME_BETWEEN_START_AND_FIRST_MARK_PASSING_IN_MILLISECONDS); if (startTimeReceived != null) {
} else { TimePoint timeOfFirstMarkPassing = getFirstPassingTime(firstWaypoint);
startTime = startTimeReceived; if (timeOfFirstMarkPassing != null) {
long startTimeReceived2timeOfFirstMarkPassingFirstMark = timeOfFirstMarkPassing.asMillis()
- startTimeReceived.asMillis();
if (startTimeReceived2timeOfFirstMarkPassingFirstMark > MAX_TIME_BETWEEN_START_AND_FIRST_MARK_PASSING_IN_MILLISECONDS) {
startTime = new MillisecondsTimePoint(timeOfFirstMarkPassing.asMillis()
- MAX_TIME_BETWEEN_START_AND_FIRST_MARK_PASSING_IN_MILLISECONDS);
} else {
startTime = startTimeReceived;
}
}
} else {
final NavigableSet<MarkPassing> markPassingsForFirstWaypointInOrder = getMarkPassingsInOrderAsNavigableSet(firstWaypoint);
if (markPassingsForFirstWaypointInOrder != null) {
startTime = calculateStartOfRaceFromMarkPassings(markPassingsForFirstWaypointInOrder,
getRace().getCompetitors());
} }
}
} else {
final NavigableSet<MarkPassing> markPassingsForFirstWaypointInOrder = getMarkPassingsInOrderAsNavigableSet(firstWaypoint);
if (markPassingsForFirstWaypointInOrder != null) {
startTime = calculateStartOfRaceFromMarkPassings(markPassingsForFirstWaypointInOrder, getRace()
.getCompetitors());
} }
} }
} }
@@ -2331,17 +2346,22 @@ public abstract class TrackedRaceImpl implements TrackedRace, CourseListener {
@Override @Override
public void attachRaceLog(RaceLog raceLog) { public void attachRaceLog(RaceLog raceLog) {
this.attachedRaceLog = raceLog; this.attachedRaceLogs.put(raceLog.getId(), raceLog);
} }
@Override @Override
public void detachRaceLog() { public void detachRaceLog(Serializable identifier) {
this.attachedRaceLog = null; this.attachedRaceLogs.remove(identifier);
}
@Override
public void detachAllRaceLogs() {
this.attachedRaceLogs.clear();
} }
@Override @Override
public RaceLog getRaceLog() { public RaceLog getRaceLog(Serializable identifier) {
return attachedRaceLog; return attachedRaceLogs.get(identifier);
} }
@Override @Override
@@ -11,7 +11,7 @@ import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Fleet; import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.RaceColumn; import com.sap.sailing.domain.base.RaceColumn;
import com.sap.sailing.domain.base.RaceColumnListener; import com.sap.sailing.domain.base.RaceColumnListener;
import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule; import com.sap.sailing.domain.leaderboard.ResultDiscardingRule;
import com.sap.sailing.domain.racelog.RaceLogEvent; import com.sap.sailing.domain.racelog.RaceLogEvent;
import com.sap.sailing.domain.racelog.RaceLogIdentifier; import com.sap.sailing.domain.racelog.RaceLogIdentifier;
import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.domain.tracking.TrackedRace;
@@ -85,6 +85,12 @@ public class RaceColumnListeners implements Serializable {
} }
} }
public void notifyListenersAboutIsStartsWithZeroScoreChanged(RaceColumn raceColumn, boolean newIsStartsWithZeroScore) {
for (RaceColumnListener listener : getRaceColumnListeners()) {
listener.isStartsWithZeroScoreChanged(raceColumn, newIsStartsWithZeroScore);
}
}
public void notifyListenersAboutRaceColumnAddedToContainer(RaceColumn raceColumn) { public void notifyListenersAboutRaceColumnAddedToContainer(RaceColumn raceColumn) {
for (RaceColumnListener listener : getRaceColumnListeners()) { for (RaceColumnListener listener : getRaceColumnListeners()) {
listener.raceColumnAddedToContainer(raceColumn); listener.raceColumnAddedToContainer(raceColumn);
@@ -126,8 +132,8 @@ public class RaceColumnListeners implements Serializable {
} }
} }
public void notifyListenersAboutResultDiscardingRuleChanged(ThresholdBasedResultDiscardingRule oldDiscardingRule, public void notifyListenersAboutResultDiscardingRuleChanged(ResultDiscardingRule oldDiscardingRule,
ThresholdBasedResultDiscardingRule newDiscardingRule) { ResultDiscardingRule newDiscardingRule) {
for (RaceColumnListener listener : getRaceColumnListeners()) { for (RaceColumnListener listener : getRaceColumnListeners()) {
listener.resultDiscardingRuleChanged(oldDiscardingRule, newDiscardingRule); listener.resultDiscardingRuleChanged(oldDiscardingRule, newDiscardingRule);
} }
@@ -406,6 +406,12 @@ public class MockedTrackedRace implements DynamicTrackedRace {
// TODO Auto-generated method stub // TODO Auto-generated method stub
} }
@Override
public boolean definesSeriesDiscardThresholds() {
// TODO Auto-generated method stub
return false;
}
}; };
} }
@@ -742,7 +748,7 @@ public class MockedTrackedRace implements DynamicTrackedRace {
} }
@Override @Override
public void detachRaceLog() { public void detachRaceLog(Serializable identifier) {
// TODO Auto-generated method stub // TODO Auto-generated method stub
} }
@@ -754,7 +760,7 @@ public class MockedTrackedRace implements DynamicTrackedRace {
} }
@Override @Override
public RaceLog getRaceLog() { public RaceLog getRaceLog(Serializable identifier) {
// TODO Auto-generated method stub // TODO Auto-generated method stub
return null; return null;
} }
@@ -783,4 +789,22 @@ public class MockedTrackedRace implements DynamicTrackedRace {
return null; return null;
} }
@Override
public void detachAllRaceLogs() {
// TODO Auto-generated method stub
}
@Override
public void invalidateStartTime() {
// TODO Auto-generated method stub
}
@Override
public void invalidateEndTime() {
// TODO Auto-generated method stub
}
} }
@@ -1,10 +1,6 @@
package com.sap.sailing.gwt.ui.adminconsole; package com.sap.sailing.gwt.ui.adminconsole;
import java.util.ArrayList;
import java.util.List;
import com.google.gwt.user.client.ui.ListBox; import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.LongBox;
import com.google.gwt.user.client.ui.TextBox; import com.google.gwt.user.client.ui.TextBox;
import com.sap.sailing.domain.common.ScoringSchemeType; import com.sap.sailing.domain.common.ScoringSchemeType;
import com.sap.sailing.gwt.ui.client.DataEntryDialog; import com.sap.sailing.gwt.ui.client.DataEntryDialog;
@@ -17,73 +13,30 @@ public abstract class AbstractLeaderboardDialog extends DataEntryDialog<Leaderbo
protected TextBox displayNameTextBox; protected TextBox displayNameTextBox;
protected LeaderboardDescriptor leaderboardDescriptor; protected LeaderboardDescriptor leaderboardDescriptor;
protected LongBox[] discardThresholdBoxes; protected DiscardThresholdBoxes discardThresholdBoxes;
protected static final int MAX_NUMBER_OF_DISCARDED_RESULTS = 4; protected static final int MAX_NUMBER_OF_DISCARDED_RESULTS = 4;
public AbstractLeaderboardDialog(String title, LeaderboardDescriptor leaderboardDescriptor, StringMessages stringConstants, public AbstractLeaderboardDialog(String title, LeaderboardDescriptor leaderboardDescriptor, StringMessages stringMessages,
Validator<LeaderboardDescriptor> validator, DialogCallback<LeaderboardDescriptor> callback) { Validator<LeaderboardDescriptor> validator, DialogCallback<LeaderboardDescriptor> callback) {
super(title, null, stringConstants.ok(), stringConstants.cancel(), validator, callback); super(title, null, stringMessages.ok(), stringMessages.cancel(), validator, callback);
this.stringMessages = stringConstants; this.stringMessages = stringMessages;
this.leaderboardDescriptor = leaderboardDescriptor; this.leaderboardDescriptor = leaderboardDescriptor;
} }
@Override @Override
protected LeaderboardDescriptor getResult() { protected LeaderboardDescriptor getResult() {
int[] discardThresholdsBoxContents = getDiscardThresholds(discardThresholdBoxes);
leaderboardDescriptor.setName(nameTextBox.getValue()); leaderboardDescriptor.setName(nameTextBox.getValue());
leaderboardDescriptor.setDisplayName(displayNameTextBox.getValue().trim().isEmpty() ? null : displayNameTextBox.getValue()); leaderboardDescriptor.setDisplayName(displayNameTextBox.getValue().trim().isEmpty() ? null : displayNameTextBox.getValue());
leaderboardDescriptor.setDiscardThresholds(discardThresholdsBoxContents); leaderboardDescriptor.setDiscardThresholds(discardThresholdBoxes==null?null:discardThresholdBoxes.getDiscardThresholds());
return leaderboardDescriptor; return leaderboardDescriptor;
} }
protected static int[] getDiscardThresholds(LongBox[] discardThresholdBoxes) {
List<Integer> discardThresholds = new ArrayList<Integer>();
// go backwards; starting from first non-zero element, add them; take over leading zeroes which validator shall discard
for (int i = discardThresholdBoxes.length-1; i>=0; i--) {
if ((discardThresholdBoxes[i].getValue() != null
&& discardThresholdBoxes[i].getValue().toString().length() > 0) || !discardThresholds.isEmpty()) {
if (discardThresholdBoxes[i].getValue() == null) {
discardThresholds.add(0, 0);
} else {
discardThresholds.add(0, discardThresholdBoxes[i].getValue().intValue());
}
}
}
int[] discardThresholdsBoxContents = new int[discardThresholds.size()];
for (int i = 0; i < discardThresholds.size(); i++) {
discardThresholdsBoxContents[i] = discardThresholds.get(i);
}
return discardThresholdsBoxContents;
}
@Override @Override
public void show() { public void show() {
super.show(); super.show();
nameTextBox.setFocus(true); nameTextBox.setFocus(true);
} }
protected static LongBox[] initEmptyDiscardThresholdBoxes(DataEntryDialog<?> dialog) {
LongBox[] result = new LongBox[MAX_NUMBER_OF_DISCARDED_RESULTS];
for (int i = 0; i < result.length; i++) {
result[i] = dialog.createLongBoxWithOptionalValue(null, 2);
result[i].setVisibleLength(2);
}
return result;
}
protected static LongBox[] initPrefilledDiscardThresholdBoxes(int[] valuesToShow, DataEntryDialog<?> dialog) {
LongBox[] result = new LongBox[MAX_NUMBER_OF_DISCARDED_RESULTS];
for (int i = 0; i < result.length; i++) {
if (i < valuesToShow.length) {
result[i] = dialog.createLongBox(valuesToShow[i], 2);
} else {
result[i] = dialog.createLongBoxWithOptionalValue(null, 2);
}
result[i].setVisibleLength(2);
}
return result;
}
protected static ListBox createScoringSchemeListBox(DataEntryDialog<?> dialog, StringMessages stringMessages) { protected static ListBox createScoringSchemeListBox(DataEntryDialog<?> dialog, StringMessages stringMessages) {
ListBox scoringSchemeListBox2 = dialog.createListBox(false); ListBox scoringSchemeListBox2 = dialog.createListBox(false);
for (ScoringSchemeType scoringSchemeType: ScoringSchemeType.values()) { for (ScoringSchemeType scoringSchemeType: ScoringSchemeType.values()) {
@@ -0,0 +1,116 @@
package com.sap.sailing.gwt.ui.adminconsole;
import java.util.ArrayList;
import java.util.List;
import com.google.gwt.user.client.ui.HasVerticalAlignment;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.LongBox;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.sap.sailing.gwt.ui.client.DataEntryDialog;
import com.sap.sailing.gwt.ui.client.StringMessages;
/**
* For a result discarding rule based on thresholds that tell after how many races the next discard kicks in, an instance of
* this class offers the UI components and validation rules that help in composing a UI that, among other things, allows a user
* to configure the discarding thresholds.<p>
*
* TODO produce error messages during validation
*
* @author Axel Uhl (d043530)
*
*/
public class DiscardThresholdBoxes {
private static final int MAX_NUMBER_OF_DISCARDED_RESULTS = 4;
private final LongBox[] discardThresholdBoxes;
private final DataEntryDialog<?> parent;
/**
* The widget used to represent the UI
*/
private final Widget widget;
public DiscardThresholdBoxes(DataEntryDialog<?> parent, StringMessages stringMessages) {
this(parent, /* values to show */ new int[0], stringMessages);
}
public DiscardThresholdBoxes(DataEntryDialog<?> parent, int[] initialDiscardThresholds, StringMessages stringMessages) {
this.parent = parent;
discardThresholdBoxes = new LongBox[MAX_NUMBER_OF_DISCARDED_RESULTS];
for (int i = 0; i < discardThresholdBoxes.length; i++) {
if (initialDiscardThresholds != null && i < initialDiscardThresholds.length) {
discardThresholdBoxes[i] = parent.createLongBox(initialDiscardThresholds[i], 2);
} else {
discardThresholdBoxes[i] = parent.createLongBoxWithOptionalValue(null, 2);
}
discardThresholdBoxes[i].setVisibleLength(2);
}
widget = createDiscardThresholdBoxesPanel(stringMessages);
}
/**
* @return the widget that can be added to the parent dialog passed to the constructor to visualize and edit the
* result discarding thresholds. The result returned from this object remains the same across this object's
* life time.
*/
public Widget getWidget() {
return widget;
}
public int[] getDiscardThresholds() {
List<Integer> discardThresholds = new ArrayList<Integer>();
// go backwards; starting from first non-zero element, add them; take over leading zeroes which validator shall discard
for (int i = discardThresholdBoxes.length-1; i>=0; i--) {
if ((discardThresholdBoxes[i].getValue() != null
&& discardThresholdBoxes[i].getValue().toString().length() > 0) || !discardThresholds.isEmpty()) {
if (discardThresholdBoxes[i].getValue() == null) {
discardThresholds.add(0, 0);
} else {
discardThresholds.add(0, discardThresholdBoxes[i].getValue().intValue());
}
}
}
int[] discardThresholdsBoxContents = new int[discardThresholds.size()];
for (int i = 0; i < discardThresholds.size(); i++) {
discardThresholdsBoxContents[i] = discardThresholds.get(i);
}
return discardThresholdsBoxContents;
}
private Widget createDiscardThresholdBoxesPanel(StringMessages stringMessages) {
assert discardThresholdBoxes != null && discardThresholdBoxes.length == MAX_NUMBER_OF_DISCARDED_RESULTS;
VerticalPanel vp = new VerticalPanel();
vp.add(new Label(stringMessages.discardRacesFromHowManyStartedRacesOn()));
HorizontalPanel hp = new HorizontalPanel();
vp.add(hp);
hp.setSpacing(3);
for (int i = 0; i < discardThresholdBoxes.length; i++) {
hp.add(new Label("" + (i + 1) + "."));
hp.add(discardThresholdBoxes[i]);
}
parent.alignAllPanelWidgetsVertically(hp, HasVerticalAlignment.ALIGN_MIDDLE);
return vp;
}
public static String getErrorMessage(int[] discardThresholds, StringMessages stringMessages) {
String errorMessage = null;
if (discardThresholds != null) {
boolean discardThresholdsAscending = true;
for (int i = 1; i < discardThresholds.length; i++) {
if (0 < discardThresholds.length) {
discardThresholdsAscending = discardThresholdsAscending
&& discardThresholds[i - 1] < discardThresholds[i]
// and if one box is empty, all subsequent boxes need to be empty too
&& (discardThresholds[i] == 0 || discardThresholds[i - 1] > 0);
}
}
if (!discardThresholdsAscending) {
errorMessage = stringMessages.discardThresholdsMustBeAscending();
}
}
return errorMessage;
}
}
@@ -11,7 +11,8 @@ public class FlexibleLeaderboardCreateDialog extends FlexibleLeaderboardDialog {
public FlexibleLeaderboardCreateDialog(Collection<StrippedLeaderboardDTO> existingLeaderboards, StringMessages stringMessages, public FlexibleLeaderboardCreateDialog(Collection<StrippedLeaderboardDTO> existingLeaderboards, StringMessages stringMessages,
Collection<EventDTO> existingEvents, ErrorReporter errorReporter, DialogCallback<LeaderboardDescriptor> callback) { Collection<EventDTO> existingEvents, ErrorReporter errorReporter, DialogCallback<LeaderboardDescriptor> callback) {
super(stringMessages.createFlexibleLeaderboard(), new LeaderboardDescriptor(), stringMessages, existingEvents, errorReporter, new FlexibleLeaderboardDialog.LeaderboardParameterValidator(stringMessages, existingLeaderboards), callback); super(stringMessages.createFlexibleLeaderboard(), new LeaderboardDescriptor(), stringMessages, existingEvents,
errorReporter, new FlexibleLeaderboardDialog.LeaderboardParameterValidator(stringMessages, existingLeaderboards), callback);
nameTextBox = createTextBox(null); nameTextBox = createTextBox(null);
nameTextBox.ensureDebugId("LeaderboardNameField"); nameTextBox.ensureDebugId("LeaderboardNameField");
displayNameTextBox = createTextBox(null); displayNameTextBox = createTextBox(null);
@@ -20,6 +21,6 @@ public class FlexibleLeaderboardCreateDialog extends FlexibleLeaderboardDialog {
scoringSchemeListBox = createScoringSchemeListBox(this, stringMessages); scoringSchemeListBox = createScoringSchemeListBox(this, stringMessages);
sailingEventsListBox = createSailingEventListBox(); sailingEventsListBox = createSailingEventListBox();
discardThresholdBoxes = initEmptyDiscardThresholdBoxes(this); discardThresholdBoxes = new DiscardThresholdBoxes(this, stringMessages);
} }
} }
@@ -6,8 +6,6 @@ import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler; import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.user.client.ui.FlowPanel; import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Grid; import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.HasVerticalAlignment;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ListBox; import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.Widget; import com.google.gwt.user.client.ui.Widget;
@@ -24,12 +22,12 @@ public abstract class FlexibleLeaderboardDialog extends AbstractLeaderboardDialo
protected ListBox courseAreaListBox; protected ListBox courseAreaListBox;
protected static class LeaderboardParameterValidator implements Validator<LeaderboardDescriptor> { protected static class LeaderboardParameterValidator implements Validator<LeaderboardDescriptor> {
protected final StringMessages stringConstants; protected final StringMessages stringMessages;
protected final Collection<StrippedLeaderboardDTO> existingLeaderboards; protected final Collection<StrippedLeaderboardDTO> existingLeaderboards;
public LeaderboardParameterValidator(StringMessages stringConstants, Collection<StrippedLeaderboardDTO> existingLeaderboards){ public LeaderboardParameterValidator(StringMessages stringConstants, Collection<StrippedLeaderboardDTO> existingLeaderboards) {
super(); super();
this.stringConstants = stringConstants; this.stringMessages = stringConstants;
this.existingLeaderboards = existingLeaderboards; this.existingLeaderboards = existingLeaderboards;
} }
@@ -37,33 +35,23 @@ public abstract class FlexibleLeaderboardDialog extends AbstractLeaderboardDialo
public String getErrorMessage(LeaderboardDescriptor leaderboardToValidate) { public String getErrorMessage(LeaderboardDescriptor leaderboardToValidate) {
String errorMessage; String errorMessage;
boolean nonEmpty = leaderboardToValidate.getName() != null && leaderboardToValidate.getName().length() > 0; boolean nonEmpty = leaderboardToValidate.getName() != null && leaderboardToValidate.getName().length() > 0;
boolean discardThresholdsAscending = true;
for (int i = 1; i < leaderboardToValidate.getDiscardThresholds().length; i++) {
// TODO what are correct values for discarding Thresholds?
if (0 < leaderboardToValidate.getDiscardThresholds().length){
discardThresholdsAscending = discardThresholdsAscending
&& leaderboardToValidate.getDiscardThresholds()[i - 1] < leaderboardToValidate.getDiscardThresholds()[i]
// and if one box is empty, all subsequent boxes need to be empty too
&& (leaderboardToValidate.getDiscardThresholds()[i] == 0 || leaderboardToValidate.getDiscardThresholds()[i-1] > 0);
}
}
boolean unique = true; boolean unique = true;
for (StrippedLeaderboardDTO dao : existingLeaderboards) { for (StrippedLeaderboardDTO dao : existingLeaderboards) {
if(dao.name.equals(leaderboardToValidate.getName())){ if(dao.name.equals(leaderboardToValidate.getName())){
unique = false; unique = false;
} }
} }
if (!nonEmpty) { if (!nonEmpty) {
errorMessage = stringConstants.pleaseEnterAName(); errorMessage = stringMessages.pleaseEnterAName();
} else if(!unique){ } else if(!unique){
errorMessage = stringConstants.leaderboardWithThisNameAlreadyExists(); errorMessage = stringMessages.leaderboardWithThisNameAlreadyExists();
} else if (!discardThresholdsAscending) {
errorMessage = stringConstants.discardThresholdsMustBeAscending();
} else { } else {
errorMessage = null; String discardThresholdErrorMessage = DiscardThresholdBoxes.getErrorMessage(leaderboardToValidate.getDiscardThresholds(), stringMessages);
if (discardThresholdErrorMessage != null) {
errorMessage = discardThresholdErrorMessage;
} else {
errorMessage = null;
}
} }
return errorMessage; return errorMessage;
} }
@@ -112,15 +100,7 @@ public abstract class FlexibleLeaderboardDialog extends AbstractLeaderboardDialo
formGrid.setWidget(4, 0, new Label(stringMessages.courseArea() + ":")); formGrid.setWidget(4, 0, new Label(stringMessages.courseArea() + ":"));
formGrid.setWidget(4, 1, courseAreaListBox); formGrid.setWidget(4, 1, courseAreaListBox);
mainPanel.add(formGrid); mainPanel.add(formGrid);
mainPanel.add(new Label(stringMessages.discardRacesFromHowManyStartedRacesOn())); mainPanel.add(discardThresholdBoxes.getWidget());
HorizontalPanel hp = new HorizontalPanel();
hp.setSpacing(3);
for (int i = 0; i < discardThresholdBoxes.length; i++) {
hp.add(new Label("" + (i + 1) + "."));
hp.add(discardThresholdBoxes[i]);
}
alignAllPanelWidgetsVertically(hp, HasVerticalAlignment.ALIGN_MIDDLE);
mainPanel.add(hp);
return mainPanel; return mainPanel;
} }
@@ -14,10 +14,10 @@ import com.sap.sailing.gwt.ui.shared.StrippedLeaderboardDTO;
public class FlexibleLeaderboardEditDialog extends FlexibleLeaderboardDialog { public class FlexibleLeaderboardEditDialog extends FlexibleLeaderboardDialog {
public FlexibleLeaderboardEditDialog(Collection<StrippedLeaderboardDTO> otherExistingLeaderboards, public FlexibleLeaderboardEditDialog(Collection<StrippedLeaderboardDTO> otherExistingLeaderboards,
LeaderboardDescriptor leaderboard, StringMessages stringConstants, List<EventDTO> existingEvents, ErrorReporter errorReporter, LeaderboardDescriptor leaderboard, StringMessages stringMessages, List<EventDTO> existingEvents, ErrorReporter errorReporter,
DialogCallback<LeaderboardDescriptor> callback) { DialogCallback<LeaderboardDescriptor> callback) {
super(stringConstants.editFlexibleLeaderboard(), leaderboard, stringConstants, existingEvents, errorReporter, new FlexibleLeaderboardDialog.LeaderboardParameterValidator( super(stringMessages.editFlexibleLeaderboard(), leaderboard, stringMessages, existingEvents, errorReporter, new FlexibleLeaderboardDialog.LeaderboardParameterValidator(
stringConstants, otherExistingLeaderboards), callback); stringMessages, otherExistingLeaderboards), callback);
nameTextBox = createTextBox(leaderboard.getName()); nameTextBox = createTextBox(leaderboard.getName());
displayNameTextBox = createTextBox(leaderboard.getDisplayName()); displayNameTextBox = createTextBox(leaderboard.getDisplayName());
@@ -27,7 +27,7 @@ public class FlexibleLeaderboardEditDialog extends FlexibleLeaderboardDialog {
int j = 0; int j = 0;
for (ScoringSchemeType scoringSchemeType: ScoringSchemeType.values()) { for (ScoringSchemeType scoringSchemeType: ScoringSchemeType.values()) {
scoringSchemeListBox.addItem(ScoringSchemeTypeFormatter.format(scoringSchemeType, stringConstants)); scoringSchemeListBox.addItem(ScoringSchemeTypeFormatter.format(scoringSchemeType, stringMessages));
if(leaderboard.getScoringScheme() == scoringSchemeType) { if(leaderboard.getScoringScheme() == scoringSchemeType) {
scoringSchemeListBox.setSelectedIndex(j); scoringSchemeListBox.setSelectedIndex(j);
} }
@@ -48,6 +48,6 @@ public class FlexibleLeaderboardEditDialog extends FlexibleLeaderboardDialog {
} }
} }
} }
discardThresholdBoxes = initPrefilledDiscardThresholdBoxes(leaderboard.getDiscardThresholds(), this); discardThresholdBoxes = new DiscardThresholdBoxes(this, leaderboard.getDiscardThresholds(), stringMessages);
} }
} }
@@ -269,8 +269,10 @@ public class LeaderboardConfigPanel extends FormPanel implements SelectedLeaderb
Window.alert("This is a meta leaderboard. It can't be changed here."); Window.alert("This is a meta leaderboard. It can't be changed here.");
} else { } else {
if (leaderboardDTO.isRegattaLeaderboard) { if (leaderboardDTO.isRegattaLeaderboard) {
LeaderboardDescriptor descriptor = new LeaderboardDescriptor(leaderboardDTO.name, LeaderboardDescriptor descriptor = new LeaderboardDescriptor(leaderboardDTO.name,
leaderboardDTO.displayName, null, leaderboardDTO.discardThresholds, leaderboardDTO.regattaName, leaderboardDTO.defaultCourseAreaIdAsString); leaderboardDTO.displayName, /* scoring scheme provided by regatta */ null,
leaderboardDTO.discardThresholds, leaderboardDTO.regattaName,
leaderboardDTO.defaultCourseAreaIdAsString);
AbstractLeaderboardDialog dialog = new RegattaLeaderboardEditDialog(Collections AbstractLeaderboardDialog dialog = new RegattaLeaderboardEditDialog(Collections
.unmodifiableCollection(otherExistingLeaderboard), Collections.unmodifiableCollection(allRegattas), .unmodifiableCollection(otherExistingLeaderboard), Collections.unmodifiableCollection(allRegattas),
descriptor, stringMessages, errorReporter, descriptor, stringMessages, errorReporter,
@@ -3,9 +3,10 @@ package com.sap.sailing.gwt.ui.adminconsole;
import com.sap.sailing.domain.common.ScoringSchemeType; import com.sap.sailing.domain.common.ScoringSchemeType;
/** /**
* A descriptor class for creating and editing basic data of a leaderboard * A descriptor class for creating and editing basic data of a leaderboard.
*
* @author Frank * @author Frank
* *
*/ */
public class LeaderboardDescriptor { public class LeaderboardDescriptor {
private String name; private String name;
@@ -27,6 +28,9 @@ public class LeaderboardDescriptor {
this.courseAreaIdAsString = courseAreaIdAsString; this.courseAreaIdAsString = courseAreaIdAsString;
} }
/**
* Leaves the {@link #regattaName} <code>null</code>, representing a flexible leaderboard, not a regatta leaderboard
*/
public LeaderboardDescriptor(String name, String displayName, ScoringSchemeType scoringScheme, int[] discardThresholds, String courseAreaIdAsString) { public LeaderboardDescriptor(String name, String displayName, ScoringSchemeType scoringScheme, int[] discardThresholds, String courseAreaIdAsString) {
this.name = name; this.name = name;
this.displayName = displayName; this.displayName = displayName;
@@ -51,6 +55,11 @@ public class LeaderboardDescriptor {
this.scoringScheme = scoringScheme; this.scoringScheme = scoringScheme;
} }
/**
* @return <code>null</code>, in case the leaderboard does not explicitly define result discarding thresholds itself
* (e.g., a regatta leaderboard that obtains its result discarding rules from the underlying regatta's
* series definitions); a valid but possibly empty array otherwise
*/
public int[] getDiscardThresholds() { public int[] getDiscardThresholds() {
return discardThresholds; return discardThresholds;
} }
@@ -10,10 +10,8 @@ import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler; import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.user.client.ui.CheckBox; import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.Grid; import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ListBox; import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.LongBox;
import com.google.gwt.user.client.ui.Panel; import com.google.gwt.user.client.ui.Panel;
import com.google.gwt.user.client.ui.TextArea; import com.google.gwt.user.client.ui.TextArea;
import com.google.gwt.user.client.ui.TextBox; import com.google.gwt.user.client.ui.TextBox;
@@ -32,7 +30,7 @@ public class LeaderboardGroupDialog extends DataEntryDialog<LeaderboardGroupDial
protected CheckBox displayLeaderboardsInReverseOrderCheckBox; protected CheckBox displayLeaderboardsInReverseOrderCheckBox;
protected CheckBox useOverallLeaderboardCheckBox; protected CheckBox useOverallLeaderboardCheckBox;
private Panel overallLeaderboardConfigPanel; private Panel overallLeaderboardConfigPanel;
private LongBox[] overallLeaderboardDiscardThresholdFields; private DiscardThresholdBoxes overallLeaderboardDiscardThresholdBoxes;
private ListBox overallLeaderboardScoringSchemeListBox; private ListBox overallLeaderboardScoringSchemeListBox;
public static class LeaderboardGroupDescriptor { public static class LeaderboardGroupDescriptor {
@@ -120,10 +118,6 @@ public class LeaderboardGroupDialog extends DataEntryDialog<LeaderboardGroupDial
return overallLeaderboardScoringSchemeListBox; return overallLeaderboardScoringSchemeListBox;
} }
protected LongBox[] getOverallLeaderboardDiscardThresholdFields() {
return overallLeaderboardDiscardThresholdFields;
}
public LeaderboardGroupDialog(LeaderboardGroupDTO group, StringMessages stringMessages, public LeaderboardGroupDialog(LeaderboardGroupDTO group, StringMessages stringMessages,
DialogCallback<LeaderboardGroupDescriptor> callback, Collection<LeaderboardGroupDTO> existingLeaderboardGroups) { DialogCallback<LeaderboardGroupDescriptor> callback, Collection<LeaderboardGroupDTO> existingLeaderboardGroups) {
super(stringMessages.leaderboardGroup(), null, stringMessages.ok(), stringMessages.cancel(), super(stringMessages.leaderboardGroup(), null, stringMessages.ok(), stringMessages.cancel(),
@@ -140,20 +134,12 @@ public class LeaderboardGroupDialog extends DataEntryDialog<LeaderboardGroupDial
group.getOverallLeaderboardScoringSchemeType())); group.getOverallLeaderboardScoringSchemeType()));
} }
formGrid.setWidget(0, 1, overallLeaderboardScoringSchemeListBox); formGrid.setWidget(0, 1, overallLeaderboardScoringSchemeListBox);
formGrid.setWidget(1, 0, new Label(stringMessages.discardRacesFromHowManyStartedRacesOn()));
HorizontalPanel hp = new HorizontalPanel();
hp.setSpacing(3);
if (group.hasOverallLeaderboard()) { if (group.hasOverallLeaderboard()) {
overallLeaderboardDiscardThresholdFields = AbstractLeaderboardDialog.initPrefilledDiscardThresholdBoxes( overallLeaderboardDiscardThresholdBoxes = new DiscardThresholdBoxes(this, group.getOverallLeaderboardDiscardThresholds(), stringMessages);
group.getOverallLeaderboardDiscardThresholds(), this);
} else { } else {
overallLeaderboardDiscardThresholdFields = AbstractLeaderboardDialog.initEmptyDiscardThresholdBoxes(this); overallLeaderboardDiscardThresholdBoxes = new DiscardThresholdBoxes(this, stringMessages);
} }
for (int i = 0; i < overallLeaderboardDiscardThresholdFields.length; i++) { formGrid.setWidget(1, 1, overallLeaderboardDiscardThresholdBoxes.getWidget());
hp.add(new Label("" + (i + 1) + "."));
hp.add(overallLeaderboardDiscardThresholdFields[i]);
}
formGrid.setWidget(1, 1, hp);
overallLeaderboardConfigPanel = formGrid; overallLeaderboardConfigPanel = formGrid;
} }
@@ -162,8 +148,7 @@ public class LeaderboardGroupDialog extends DataEntryDialog<LeaderboardGroupDial
return new LeaderboardGroupDescriptor(nameEntryField.getText(), descriptionEntryField.getText(), return new LeaderboardGroupDescriptor(nameEntryField.getText(), descriptionEntryField.getText(),
displayLeaderboardsInReverseOrderCheckBox.getValue(), displayLeaderboardsInReverseOrderCheckBox.getValue(),
useOverallLeaderboardCheckBox.getValue(), useOverallLeaderboardCheckBox.getValue(),
useOverallLeaderboardCheckBox.getValue() ? AbstractLeaderboardDialog useOverallLeaderboardCheckBox.getValue() ? overallLeaderboardDiscardThresholdBoxes.getDiscardThresholds() : null,
.getDiscardThresholds(getOverallLeaderboardDiscardThresholdFields()) : null,
useOverallLeaderboardCheckBox.getValue() ? AbstractLeaderboardDialog.getSelectedScoringSchemeType( useOverallLeaderboardCheckBox.getValue() ? AbstractLeaderboardDialog.getSelectedScoringSchemeType(
overallLeaderboardScoringSchemeListBox, stringMessages) : null); overallLeaderboardScoringSchemeListBox, stringMessages) : null);
} }
@@ -1,6 +1,7 @@
package com.sap.sailing.gwt.ui.adminconsole; package com.sap.sailing.gwt.ui.adminconsole;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import java.util.List;
import com.google.gwt.cell.client.FieldUpdater; import com.google.gwt.cell.client.FieldUpdater;
@@ -24,12 +25,11 @@ import com.sap.sailing.domain.common.ScoringSchemeType;
import com.sap.sailing.domain.common.dto.FleetDTO; import com.sap.sailing.domain.common.dto.FleetDTO;
import com.sap.sailing.domain.common.dto.RaceColumnDTO; import com.sap.sailing.domain.common.dto.RaceColumnDTO;
import com.sap.sailing.domain.common.dto.RaceColumnInSeriesDTO; import com.sap.sailing.domain.common.dto.RaceColumnInSeriesDTO;
import com.sap.sailing.domain.common.impl.Util.Pair; import com.sap.sailing.gwt.ui.client.DataEntryDialog.DialogCallback;
import com.sap.sailing.gwt.ui.client.ErrorReporter; import com.sap.sailing.gwt.ui.client.ErrorReporter;
import com.sap.sailing.gwt.ui.client.RegattaRefresher; import com.sap.sailing.gwt.ui.client.RegattaRefresher;
import com.sap.sailing.gwt.ui.client.SailingServiceAsync; import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
import com.sap.sailing.gwt.ui.client.StringMessages; import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.client.DataEntryDialog.DialogCallback;
import com.sap.sailing.gwt.ui.leaderboard.ScoringSchemeTypeFormatter; import com.sap.sailing.gwt.ui.leaderboard.ScoringSchemeTypeFormatter;
import com.sap.sailing.gwt.ui.shared.RegattaDTO; import com.sap.sailing.gwt.ui.shared.RegattaDTO;
import com.sap.sailing.gwt.ui.shared.SeriesDTO; import com.sap.sailing.gwt.ui.shared.SeriesDTO;
@@ -62,32 +62,24 @@ public class RegattaDetailsComposite extends Composite {
this.regattaRefresher = regattaRefresher; this.regattaRefresher = regattaRefresher;
this.errorReporter = errorReporter; this.errorReporter = errorReporter;
this.stringMessages = stringMessages; this.stringMessages = stringMessages;
regatta = null; regatta = null;
mainPanel = new CaptionPanel(stringMessages.regatta()); mainPanel = new CaptionPanel(stringMessages.regatta());
VerticalPanel vPanel = new VerticalPanel(); VerticalPanel vPanel = new VerticalPanel();
mainPanel.add(vPanel); mainPanel.add(vPanel);
Grid grid = new Grid(4, 2); Grid grid = new Grid(4, 2);
vPanel.add(grid); vPanel.add(grid);
regattaName = new Label(); regattaName = new Label();
grid.setWidget(0 , 0, new Label(stringMessages.regattaName() + ":")); grid.setWidget(0 , 0, new Label(stringMessages.regattaName() + ":"));
grid.setWidget(0 , 1, regattaName); grid.setWidget(0 , 1, regattaName);
boatClassName = new Label(); boatClassName = new Label();
grid.setWidget(1 , 0, new Label(stringMessages.boatClass() + ":")); grid.setWidget(1 , 0, new Label(stringMessages.boatClass() + ":"));
grid.setWidget(1 , 1, boatClassName); grid.setWidget(1 , 1, boatClassName);
defaultCourseArea = new Label(); defaultCourseArea = new Label();
grid.setWidget(2 , 0, new Label(stringMessages.courseArea() + ":")); grid.setWidget(2 , 0, new Label(stringMessages.courseArea() + ":"));
grid.setWidget(2 , 1, defaultCourseArea); grid.setWidget(2 , 1, defaultCourseArea);
scoringSystem = new Label(); scoringSystem = new Label();
grid.setWidget(3 , 0, new Label(stringMessages.scoringSystem() + ":")); grid.setWidget(3 , 0, new Label(stringMessages.scoringSystem() + ":"));
grid.setWidget(3 , 1, scoringSystem); grid.setWidget(3 , 1, scoringSystem);
seriesTable = createRegattaSeriesTable(); seriesTable = createRegattaSeriesTable();
seriesSelectionModel = new SingleSelectionModel<SeriesDTO>(); seriesSelectionModel = new SingleSelectionModel<SeriesDTO>();
seriesSelectionModel.addSelectionChangeHandler(new SelectionChangeEvent.Handler() { seriesSelectionModel.addSelectionChangeHandler(new SelectionChangeEvent.Handler() {
@@ -96,11 +88,9 @@ public class RegattaDetailsComposite extends Composite {
} }
}); });
seriesTable.setSelectionModel(seriesSelectionModel); seriesTable.setSelectionModel(seriesSelectionModel);
seriesListDataProvider = new ListDataProvider<SeriesDTO>(); seriesListDataProvider = new ListDataProvider<SeriesDTO>();
seriesListDataProvider.addDataDisplay(seriesTable); seriesListDataProvider.addDataDisplay(seriesTable);
vPanel.add(seriesTable); vPanel.add(seriesTable);
initWidget(mainPanel); initWidget(mainPanel);
} }
@@ -122,6 +112,13 @@ public class RegattaDetailsComposite extends Composite {
} }
}; };
TextColumn<SeriesDTO> startsWithZeroScoreColumn = new TextColumn<SeriesDTO>() {
@Override
public String getValue(SeriesDTO series) {
return series.isStartsWithZeroScore() ? stringMessages.yes() : stringMessages.no();
}
};
TextColumn<SeriesDTO> racesColumn = new TextColumn<SeriesDTO>() { TextColumn<SeriesDTO> racesColumn = new TextColumn<SeriesDTO>() {
@Override @Override
public String getValue(SeriesDTO series) { public String getValue(SeriesDTO series) {
@@ -161,6 +158,29 @@ public class RegattaDetailsComposite extends Composite {
} }
}; };
TextColumn<SeriesDTO> discardsColumn = new TextColumn<SeriesDTO>() {
@Override
public String getValue(SeriesDTO series) {
final String result;
if (series.getDiscardThresholds() == null) {
result = stringMessages.no();
} else {
StringBuilder sb = new StringBuilder();
boolean first = true;
for (int threshold : series.getDiscardThresholds()) {
if (first) {
first = false;
} else {
sb.append(", ");
}
sb.append(threshold);
}
result = sb.toString();
}
return result;
}
};
ImagesBarColumn<SeriesDTO, SeriesConfigImagesBarCell> seriesActionColumn = new ImagesBarColumn<SeriesDTO, SeriesConfigImagesBarCell>( ImagesBarColumn<SeriesDTO, SeriesConfigImagesBarCell> seriesActionColumn = new ImagesBarColumn<SeriesDTO, SeriesConfigImagesBarCell>(
new SeriesConfigImagesBarCell(stringMessages)); new SeriesConfigImagesBarCell(stringMessages));
seriesActionColumn.setFieldUpdater(new FieldUpdater<SeriesDTO, String>() { seriesActionColumn.setFieldUpdater(new FieldUpdater<SeriesDTO, String>() {
@@ -179,6 +199,8 @@ public class RegattaDetailsComposite extends Composite {
table.addColumn(isMedalSeriesColumn, stringMessages.medalSeries()); table.addColumn(isMedalSeriesColumn, stringMessages.medalSeries());
table.addColumn(racesColumn, stringMessages.races()); table.addColumn(racesColumn, stringMessages.races());
table.addColumn(fleetsColumn, stringMessages.fleets()); table.addColumn(fleetsColumn, stringMessages.fleets());
table.addColumn(discardsColumn, stringMessages.discarding());
table.addColumn(startsWithZeroScoreColumn, stringMessages.startsWithZeroScore());
table.addColumn(seriesActionColumn, stringMessages.actions()); table.addColumn(seriesActionColumn, stringMessages.actions());
return table; return table;
@@ -194,21 +216,27 @@ public class RegattaDetailsComposite extends Composite {
} }
private void editRacesOfRegattaSeries(final RegattaDTO regatta, final SeriesDTO series) { private void editRacesOfRegattaSeries(final RegattaDTO regatta, final SeriesDTO series) {
RaceColumnInRegattaSeriesDialog raceDialog = new RaceColumnInRegattaSeriesDialog(regatta, series, stringMessages, SeriesEditDialog raceDialog = new SeriesEditDialog(regatta, series, stringMessages,
new DialogCallback<Pair<SeriesDTO, List<RaceColumnDTO>>>() { new DialogCallback<SeriesDescriptor>() {
@Override @Override
public void cancel() { public void cancel() {
} }
@Override @Override
public void ok(final Pair<SeriesDTO, List<RaceColumnDTO>> result) { public void ok(final SeriesDescriptor result) {
updateRacesOfRegattaSeries(regatta, result.getA(), result.getB()); updateRacesOfRegattaSeries(regatta, result);
} }
}); });
raceDialog.show(); raceDialog.show();
} }
private void updateRacesOfRegattaSeries(final RegattaDTO regatta, final SeriesDTO series, List<RaceColumnDTO> newRaceColumns) { private void updateRacesOfRegattaSeries(final RegattaDTO regatta, final SeriesDescriptor seriesDescriptor) {
final SeriesDTO series = seriesDescriptor.getSeries();
final List<RaceColumnDTO> newRaceColumns = seriesDescriptor.getRaces();
final boolean isMedalChanged = series.isMedal() != seriesDescriptor.isMedal();
final boolean isStartsWithZeroScoreChanged = series.isStartsWithZeroScore() != seriesDescriptor.isStartsWithZeroScore();
final boolean seriesResultDiscardingThresholdsChanged = !Arrays.equals(series.getDiscardThresholds(),
seriesDescriptor.getResultDiscardingThresholds());
final RegattaIdentifier regattaIdentifier = new RegattaName(regatta.name); final RegattaIdentifier regattaIdentifier = new RegattaName(regatta.name);
List<RaceColumnDTO> existingRaceColumns = series.getRaceColumns(); List<RaceColumnDTO> existingRaceColumns = series.getRaceColumns();
final List<String> raceColumnsToAdd = new ArrayList<String>(); final List<String> raceColumnsToAdd = new ArrayList<String>();
@@ -224,7 +252,6 @@ public class RegattaDetailsComposite extends Composite {
raceColumnsToRemove.add(existingRaceColumn.name); raceColumnsToRemove.add(existingRaceColumn.name);
} }
} }
sailingService.addRaceColumnsToSeries(regattaIdentifier, series.name, raceColumnsToAdd, new AsyncCallback<List<RaceColumnInSeriesDTO>>() { sailingService.addRaceColumnsToSeries(regattaIdentifier, series.name, raceColumnsToAdd, new AsyncCallback<List<RaceColumnInSeriesDTO>>() {
@Override @Override
public void onFailure(Throwable caught) { public void onFailure(Throwable caught) {
@@ -246,7 +273,6 @@ public class RegattaDetailsComposite extends Composite {
errorReporter.reportError("Error trying to remove race columns " errorReporter.reportError("Error trying to remove race columns "
+ raceColumnsToAdd + " from series " + series.name + raceColumnsToAdd + " from series " + series.name
+ ": " + caught.getMessage()); + ": " + caught.getMessage());
} }
@Override @Override
@@ -254,6 +280,23 @@ public class RegattaDetailsComposite extends Composite {
regattaRefresher.fillRegattas(); regattaRefresher.fillRegattas();
} }
}); });
if (isMedalChanged || seriesResultDiscardingThresholdsChanged || isStartsWithZeroScoreChanged) {
sailingService.updateSeries(regattaIdentifier, series.name, seriesDescriptor.isMedal(),
seriesDescriptor.getResultDiscardingThresholds(), seriesDescriptor.isStartsWithZeroScore(),
new AsyncCallback<Void>() {
@Override
public void onFailure(Throwable caught) {
errorReporter.reportError("Error trying to remove race columns "
+ raceColumnsToAdd + " from series " + series.name
+ ": " + caught.getMessage());
}
@Override
public void onSuccess(Void result) {
regattaRefresher.fillRegattas();
}
});
}
} }
private void updateRegattaDetails() { private void updateRegattaDetails() {
@@ -11,9 +11,9 @@ import com.sap.sailing.gwt.ui.shared.StrippedLeaderboardDTO;
public class RegattaLeaderboardCreateDialog extends RegattaLeaderboardDialog { public class RegattaLeaderboardCreateDialog extends RegattaLeaderboardDialog {
public RegattaLeaderboardCreateDialog(Collection<StrippedLeaderboardDTO> existingLeaderboards, Collection<RegattaDTO> existingRegattas, StringMessages stringConstants, public RegattaLeaderboardCreateDialog(Collection<StrippedLeaderboardDTO> existingLeaderboards, Collection<RegattaDTO> existingRegattas, StringMessages stringMessages,
ErrorReporter errorReporter, DialogCallback<LeaderboardDescriptor> callback) { ErrorReporter errorReporter, DialogCallback<LeaderboardDescriptor> callback) {
super(stringConstants.createRegattaLeaderboard(), new LeaderboardDescriptor(), existingRegattas, stringConstants, errorReporter, new RegattaLeaderboardDialog.LeaderboardParameterValidator(stringConstants, existingLeaderboards), callback); super(stringMessages.createRegattaLeaderboard(), new LeaderboardDescriptor(), existingRegattas, stringMessages, errorReporter, new RegattaLeaderboardDialog.LeaderboardParameterValidator(stringMessages, existingLeaderboards), callback);
nameTextBox = createTextBox(null); nameTextBox = createTextBox(null);
displayNameTextBox = createTextBox(null); displayNameTextBox = createTextBox(null);
@@ -30,11 +30,10 @@ public class RegattaLeaderboardCreateDialog extends RegattaLeaderboardDialog {
if (selectedIndex > 0) { if (selectedIndex > 0) {
nameTextBox.setText(regattaListBox.getValue(selectedIndex)); nameTextBox.setText(regattaListBox.getValue(selectedIndex));
} }
adjustVisibilityOfResultDiscardingRuleComponent();
} }
}); });
discardThresholdBoxes = new DiscardThresholdBoxes(this, stringMessages);
discardThresholdBoxes = initEmptyDiscardThresholdBoxes(this);
} }
} }
@@ -6,8 +6,6 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import com.google.gwt.user.client.ui.Grid; import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.HasVerticalAlignment;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ListBox; import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.VerticalPanel; import com.google.gwt.user.client.ui.VerticalPanel;
@@ -21,58 +19,62 @@ import com.sap.sailing.gwt.ui.shared.StrippedLeaderboardDTO;
public abstract class RegattaLeaderboardDialog extends AbstractLeaderboardDialog { public abstract class RegattaLeaderboardDialog extends AbstractLeaderboardDialog {
protected ListBox regattaListBox; protected ListBox regattaListBox;
protected Collection<RegattaDTO> existingRegattas; protected Collection<RegattaDTO> existingRegattas;
private Label regattaDefinesDiscardsLabel;
protected static class LeaderboardParameterValidator implements Validator<LeaderboardDescriptor> { protected static class LeaderboardParameterValidator implements Validator<LeaderboardDescriptor> {
protected final StringMessages stringConstants; protected final StringMessages stringMessages;
protected final Collection<StrippedLeaderboardDTO> existingLeaderboards; protected final Collection<StrippedLeaderboardDTO> existingLeaderboards;
public LeaderboardParameterValidator(StringMessages stringConstants, Collection<StrippedLeaderboardDTO> existingLeaderboards){ public LeaderboardParameterValidator(StringMessages stringMessages, Collection<StrippedLeaderboardDTO> existingLeaderboards){
super(); super();
this.stringConstants = stringConstants; this.stringMessages = stringMessages;
this.existingLeaderboards = existingLeaderboards; this.existingLeaderboards = existingLeaderboards;
} }
@Override @Override
public String getErrorMessage(LeaderboardDescriptor leaderboardToValidate) { public String getErrorMessage(LeaderboardDescriptor leaderboardToValidate) {
String errorMessage; String errorMessage;
boolean discardThresholdsAscending = true;
for (int i = 1; i < leaderboardToValidate.getDiscardThresholds().length; i++) {
// TODO what are correct values for discarding Thresholds?
if (0 < leaderboardToValidate.getDiscardThresholds().length){
discardThresholdsAscending = discardThresholdsAscending
&& leaderboardToValidate.getDiscardThresholds()[i - 1] < leaderboardToValidate.getDiscardThresholds()[i]
// and if one box is empty, all subsequent boxes need to be empty too
&& (leaderboardToValidate.getDiscardThresholds()[i] == 0 || leaderboardToValidate.getDiscardThresholds()[i-1] > 0);
}
}
boolean unique = true; boolean unique = true;
for (StrippedLeaderboardDTO dao : existingLeaderboards) { for (StrippedLeaderboardDTO dao : existingLeaderboards) {
if(dao.name.equals(leaderboardToValidate.getRegattaName())){ if(dao.name.equals(leaderboardToValidate.getRegattaName())){
unique = false; unique = false;
} }
} }
boolean regattaSelected = leaderboardToValidate.getRegattaName() != null ? true : false; boolean regattaSelected = leaderboardToValidate.getRegattaName() != null ? true : false;
if(!regattaSelected){ if(!regattaSelected){
errorMessage = stringConstants.pleaseSelectARegatta(); errorMessage = stringMessages.pleaseSelectARegatta();
} else if(!unique){ } else if(!unique){
errorMessage = stringConstants.leaderboardWithThisNameAlreadyExists(); errorMessage = stringMessages.leaderboardWithThisNameAlreadyExists();
} else if (!discardThresholdsAscending) {
errorMessage = stringConstants.discardThresholdsMustBeAscending();
} else { } else {
errorMessage = null; String discardThresholdErrorMessage = DiscardThresholdBoxes.getErrorMessage(
leaderboardToValidate.getDiscardThresholds(), stringMessages);
if (discardThresholdErrorMessage != null) {
errorMessage = discardThresholdErrorMessage;
} else {
errorMessage = null;
}
} }
return errorMessage; return errorMessage;
} }
} }
public RegattaLeaderboardDialog(String title, LeaderboardDescriptor leaderboardDTO, Collection<RegattaDTO> existingRegattas, StringMessages stringConstants, public RegattaLeaderboardDialog(String title, LeaderboardDescriptor leaderboardDTO, Collection<RegattaDTO> existingRegattas, StringMessages stringMessages,
ErrorReporter errorReporter, LeaderboardParameterValidator validator, DialogCallback<LeaderboardDescriptor> callback) { ErrorReporter errorReporter, LeaderboardParameterValidator validator, DialogCallback<LeaderboardDescriptor> callback) {
super(title, leaderboardDTO, stringConstants, validator, callback); super(title, leaderboardDTO, stringMessages, validator, callback);
this.existingRegattas = existingRegattas; this.existingRegattas = existingRegattas;
regattaDefinesDiscardsLabel = new Label(stringMessages.regattaDefinesResultDiscardingRules());
}
protected void adjustVisibilityOfResultDiscardingRuleComponent() {
if (getSelectedRegatta().definesSeriesDiscardThresholds()) {
discardThresholdBoxes.getWidget().setVisible(false);
regattaDefinesDiscardsLabel.setVisible(true);
} else {
discardThresholdBoxes.getWidget().setVisible(true);
regattaDefinesDiscardsLabel.setVisible(false);
}
} }
protected ListBox createSortedRegattaListBox(Collection<RegattaDTO> regattas, String preSelectedRegattaName) { protected ListBox createSortedRegattaListBox(Collection<RegattaDTO> regattas, String preSelectedRegattaName) {
@@ -107,7 +109,6 @@ public abstract class RegattaLeaderboardDialog extends AbstractLeaderboardDialog
@Override @Override
protected Widget getAdditionalWidget() { protected Widget getAdditionalWidget() {
VerticalPanel mainPanel = new VerticalPanel(); VerticalPanel mainPanel = new VerticalPanel();
Grid formGrid = new Grid(3,3); Grid formGrid = new Grid(3,3);
formGrid.setCellSpacing(3); formGrid.setCellSpacing(3);
formGrid.setWidget(0, 0, createLabel(stringMessages.regatta())); formGrid.setWidget(0, 0, createLabel(stringMessages.regatta()));
@@ -116,28 +117,22 @@ public abstract class RegattaLeaderboardDialog extends AbstractLeaderboardDialog
formGrid.setWidget(1, 1, nameTextBox); formGrid.setWidget(1, 1, nameTextBox);
formGrid.setWidget(2, 0, createLabel(stringMessages.displayName())); formGrid.setWidget(2, 0, createLabel(stringMessages.displayName()));
formGrid.setWidget(2, 1, displayNameTextBox); formGrid.setWidget(2, 1, displayNameTextBox);
mainPanel.add(formGrid); mainPanel.add(formGrid);
mainPanel.add(regattaDefinesDiscardsLabel);
mainPanel.add(new Label(stringMessages.discardRacesFromHowManyStartedRacesOn())); if (discardThresholdBoxes != null) {
HorizontalPanel hp = new HorizontalPanel(); mainPanel.add(discardThresholdBoxes.getWidget());
hp.setSpacing(3); regattaDefinesDiscardsLabel.setVisible(false);
for (int i = 0; i < discardThresholdBoxes.length; i++) {
hp.add(new Label("" + (i + 1) + "."));
hp.add(discardThresholdBoxes[i]);
} }
alignAllPanelWidgetsVertically(hp, HasVerticalAlignment.ALIGN_MIDDLE);
mainPanel.add(hp);
return mainPanel; return mainPanel;
} }
public RegattaDTO getSelectedRegatta() { public RegattaDTO getSelectedRegatta() {
RegattaDTO result = null; RegattaDTO result = null;
int selIndex = regattaListBox.getSelectedIndex(); int selIndex = regattaListBox.getSelectedIndex();
if(selIndex > 0) { // the zero index represents the 'no selection' text if (selIndex > 0) { // the zero index represents the 'no selection' text
String itemText = regattaListBox.getItemText(selIndex); String itemText = regattaListBox.getItemText(selIndex);
for(RegattaDTO regattaDTO: existingRegattas) { for (RegattaDTO regattaDTO : existingRegattas) {
if(regattaDTO.name.equals(itemText)) { if (regattaDTO.name.equals(itemText)) {
result = regattaDTO; result = regattaDTO;
break; break;
} }
@@ -23,6 +23,9 @@ public class RegattaLeaderboardEditDialog extends RegattaLeaderboardDialog {
regattaListBox = createSortedRegattaListBox(existingRegattas, leaderboardDescriptor.getRegattaName()); regattaListBox = createSortedRegattaListBox(existingRegattas, leaderboardDescriptor.getRegattaName());
regattaListBox.setEnabled(false); regattaListBox.setEnabled(false);
discardThresholdBoxes = initPrefilledDiscardThresholdBoxes(leaderboardDescriptor.getDiscardThresholds(), this); if (leaderboardDescriptor.getDiscardThresholds() != null) {
discardThresholdBoxes = new DiscardThresholdBoxes(this, leaderboardDescriptor.getDiscardThresholds(), stringMessages);
} // else, the regatta leaderboard obtains its result discarding rule implicitly from the underlying regatta
adjustVisibilityOfResultDiscardingRuleComponent();
} }
} }
@@ -118,15 +118,17 @@ public class RegattaStructureManagementPanel extends SimplePanel implements Rega
} }
private void createNewRegatta(final RegattaDTO newRegatta) { private void createNewRegatta(final RegattaDTO newRegatta) {
LinkedHashMap<String, Pair<List<Triple<String, Integer, Color>>, Boolean>> seriesStructure = LinkedHashMap<String, Triple<List<Triple<String, Integer, Color>>, Pair<Boolean, Boolean>, int[]>> seriesStructure =
new LinkedHashMap<String, Pair<List<Triple<String, Integer, Color>>, Boolean>>(); new LinkedHashMap<String, Triple<List<Triple<String,Integer,Color>>,Pair<Boolean, Boolean>,int[]>>();
for (SeriesDTO seriesDTO : newRegatta.series) { for (SeriesDTO seriesDTO : newRegatta.series) {
List<Triple<String, Integer, Color>> fleets = new ArrayList<Triple<String, Integer, Color>>(); List<Triple<String, Integer, Color>> fleets = new ArrayList<Triple<String, Integer, Color>>();
for(FleetDTO fleetDTO : seriesDTO.getFleets()) { for(FleetDTO fleetDTO : seriesDTO.getFleets()) {
Triple<String, Integer, Color> fleetTriple = new Triple<String, Integer, Color>(fleetDTO.name, fleetDTO.getOrderNo(), fleetDTO.getColor()); Triple<String, Integer, Color> fleetTriple = new Triple<String, Integer, Color>(fleetDTO.name, fleetDTO.getOrderNo(), fleetDTO.getColor());
fleets.add(fleetTriple); fleets.add(fleetTriple);
} }
Pair<List<Triple<String, Integer, Color>>, Boolean> seriesPair = new Pair<List<Triple<String, Integer, Color>>, Boolean>(fleets, seriesDTO.isMedal()); Triple<List<Triple<String, Integer, Color>>, Pair<Boolean, Boolean>, int[]> seriesPair =
new Triple<List<Triple<String, Integer, Color>>, Pair<Boolean, Boolean>, int[]>(
fleets, new Pair<Boolean, Boolean>(seriesDTO.isMedal(), seriesDTO.isStartsWithZeroScore()), seriesDTO.getDiscardThresholds());
seriesStructure.put(seriesDTO.name, seriesPair); seriesStructure.put(seriesDTO.name, seriesPair);
} }
sailingService.createRegatta(newRegatta.name, newRegatta.boatClass==null?null:newRegatta.boatClass.name, seriesStructure, true, sailingService.createRegatta(newRegatta.name, newRegatta.boatClass==null?null:newRegatta.boatClass.name, seriesStructure, true,
@@ -106,7 +106,7 @@ public class RegattaWithSeriesAndFleetsCreateDialog extends RegattaWithSeriesAnd
protected void setupAdditionalWidgetsOnPanel(final VerticalPanel panel) { protected void setupAdditionalWidgetsOnPanel(final VerticalPanel panel) {
panel.add(createHeadlineLabel(stringMessages.series())); panel.add(createHeadlineLabel(stringMessages.series()));
panel.add(seriesGrid); panel.add(seriesGrid);
Button addSeriesButton = new Button("Add series"); Button addSeriesButton = new Button(stringMessages.addSeries());
addSeriesButton.addClickHandler(new ClickHandler() { addSeriesButton.addClickHandler(new ClickHandler() {
@Override @Override
public void onClick(ClickEvent event) { public void onClick(ClickEvent event) {
@@ -17,7 +17,6 @@ public class RegattaWithSeriesAndFleetsEditDialog extends RegattaWithSeriesAndFl
nameEntryField.setEnabled(false); nameEntryField.setEnabled(false);
boatClassEntryField.setEnabled(false); boatClassEntryField.setEnabled(false);
scoringSchemeListBox.setEnabled(false); scoringSchemeListBox.setEnabled(false);
sailingEventsListBox.setEnabled(true); sailingEventsListBox.setEnabled(true);
courseAreaListBox.setEnabled(true); courseAreaListBox.setEnabled(true);
} }
@@ -0,0 +1,49 @@
package com.sap.sailing.gwt.ui.adminconsole;
import java.util.List;
import com.sap.sailing.domain.common.dto.RaceColumnDTO;
import com.sap.sailing.gwt.ui.shared.SeriesDTO;
/**
* Used by {@link SeriesEditDialog} to capture the result of the user's configuration changes,
* including the race columns that the series shall define, the medal status and the discarding rules, if any.
*
* @author Axel Uhl (d043530)
*
*/
public class SeriesDescriptor {
private final SeriesDTO series;
private final List<RaceColumnDTO> races;
private final boolean isMedal;
private final boolean startsWithZeroScore;
private final int[] resultDiscardingThresholds;
public SeriesDescriptor(SeriesDTO series, List<RaceColumnDTO> races, boolean isMedal, int[] resultDiscardingThresholds, boolean startsWithZeroScore) {
this.series = series;
this.races = races;
this.isMedal = isMedal;
this.startsWithZeroScore = startsWithZeroScore;
this.resultDiscardingThresholds = resultDiscardingThresholds;
}
public SeriesDTO getSeries() {
return series;
}
public List<RaceColumnDTO> getRaces() {
return races;
}
public boolean isMedal() {
return isMedal;
}
public int[] getResultDiscardingThresholds() {
return resultDiscardingThresholds;
}
public boolean isStartsWithZeroScore() {
return startsWithZeroScore;
}
}
@@ -7,8 +7,11 @@ import java.util.Set;
import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.user.client.Window; import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Button; import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.Grid; import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.HorizontalPanel; import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.Label;
@@ -17,15 +20,17 @@ import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel; import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget; import com.google.gwt.user.client.ui.Widget;
import com.sap.sailing.domain.common.dto.RaceColumnDTO; import com.sap.sailing.domain.common.dto.RaceColumnDTO;
import com.sap.sailing.domain.common.impl.Util.Pair;
import com.sap.sailing.gwt.ui.client.DataEntryDialog; import com.sap.sailing.gwt.ui.client.DataEntryDialog;
import com.sap.sailing.gwt.ui.client.StringMessages; import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.shared.RegattaDTO; import com.sap.sailing.gwt.ui.shared.RegattaDTO;
import com.sap.sailing.gwt.ui.shared.SeriesDTO; import com.sap.sailing.gwt.ui.shared.SeriesDTO;
public class RaceColumnInRegattaSeriesDialog extends DataEntryDialog<Pair<SeriesDTO, List<RaceColumnDTO>>> { public class SeriesEditDialog extends DataEntryDialog<SeriesDescriptor> {
private final ListBox addRacesListBox; private final ListBox addRacesListBox;
private Button addRacesBtn; private Button addRacesBtn;
private CheckBox isMedalCheckbox;
private CheckBox startWithZeroScoreCheckbox;
private CheckBox useSeriesResultDiscardingThresholdsCheckbox;
private final List<TextBox> raceNameEntryFields; private final List<TextBox> raceNameEntryFields;
private final List<Button> raceNameDeleteButtons; private final List<Button> raceNameDeleteButtons;
private final StringMessages stringMessages; private final StringMessages stringMessages;
@@ -33,22 +38,22 @@ public class RaceColumnInRegattaSeriesDialog extends DataEntryDialog<Pair<Series
private Grid raceColumnsGrid; private Grid raceColumnsGrid;
private VerticalPanel additionalWidgetPanel; private VerticalPanel additionalWidgetPanel;
private final SeriesDTO selectedSeries; private final SeriesDTO selectedSeries;
private final DiscardThresholdBoxes discardThresholdBoxes;
private static class RaceDialogValidator implements Validator<Pair<SeriesDTO, List<RaceColumnDTO>>> { private static class RaceDialogValidator implements Validator<SeriesDescriptor> {
private StringMessages stringConstants; private StringMessages stringMessages;
private RegattaDTO regatta; private RegattaDTO regatta;
public RaceDialogValidator(RegattaDTO regatta, StringMessages stringConstants) { public RaceDialogValidator(RegattaDTO regatta, StringMessages stringMessages) {
this.stringConstants = stringConstants; this.stringMessages = stringMessages;
this.regatta = regatta; this.regatta = regatta;
} }
@Override @Override
public String getErrorMessage(Pair<SeriesDTO, List<RaceColumnDTO>> valueToValidate) { public String getErrorMessage(SeriesDescriptor valueToValidate) {
Set<String> raceColumnNamesOfOtherSeries = new HashSet<String>(); Set<String> raceColumnNamesOfOtherSeries = new HashSet<String>();
SeriesDTO seriesToValidate = valueToValidate.getA(); SeriesDTO seriesToValidate = valueToValidate.getSeries();
List<RaceColumnDTO> raceColumnsToValidate = valueToValidate.getB(); List<RaceColumnDTO> raceColumnsToValidate = valueToValidate.getRaces();
String errorMessage = null; String errorMessage = null;
for (SeriesDTO seriesDTO: regatta.series) { for (SeriesDTO seriesDTO: regatta.series) {
if (!seriesDTO.name.equals(seriesToValidate.name)) { if (!seriesDTO.name.equals(seriesToValidate.name)) {
@@ -57,10 +62,8 @@ public class RaceColumnInRegattaSeriesDialog extends DataEntryDialog<Pair<Series
} }
} }
} }
boolean raceColumnNameNotEmpty = true; boolean raceColumnNameNotEmpty = true;
RaceColumnDTO wrongRaceColumn = null; RaceColumnDTO wrongRaceColumn = null;
for (RaceColumnDTO raceColumn : raceColumnsToValidate) { for (RaceColumnDTO raceColumn : raceColumnsToValidate) {
raceColumnNameNotEmpty = raceColumn.name != null && raceColumn.name.length() > 0; raceColumnNameNotEmpty = raceColumn.name != null && raceColumn.name.length() > 0;
if (!raceColumnNameNotEmpty) { if (!raceColumnNameNotEmpty) {
@@ -68,10 +71,8 @@ public class RaceColumnInRegattaSeriesDialog extends DataEntryDialog<Pair<Series
break; break;
} }
} }
boolean raceColumnUniqueInSeries = true; boolean raceColumnUniqueInSeries = true;
boolean raceColumnUniqueInRegatta = true; boolean raceColumnUniqueInRegatta = true;
HashSet<String> setToFindDuplicates = new HashSet<String>(); HashSet<String> setToFindDuplicates = new HashSet<String>();
for (RaceColumnDTO raceColumn : raceColumnsToValidate) { for (RaceColumnDTO raceColumn : raceColumnsToValidate) {
if (!setToFindDuplicates.add(raceColumn.name)) { if (!setToFindDuplicates.add(raceColumn.name)) {
@@ -84,34 +85,33 @@ public class RaceColumnInRegattaSeriesDialog extends DataEntryDialog<Pair<Series
break; break;
} }
} }
if (!raceColumnNameNotEmpty) { if (!raceColumnNameNotEmpty) {
errorMessage = stringConstants.race() + " " + wrongRaceColumn.name + ": " errorMessage = stringMessages.race() + " " + wrongRaceColumn.name + ": "
+ stringConstants.pleaseEnterAName(); + stringMessages.pleaseEnterAName();
} else if (!raceColumnUniqueInSeries) { } else if (!raceColumnUniqueInSeries) {
errorMessage = stringConstants.race() + " " + wrongRaceColumn.name + ": " errorMessage = stringMessages.race() + " " + wrongRaceColumn.name + ": "
+ stringConstants.raceWithThisNameAlreadyExists(); + stringMessages.raceWithThisNameAlreadyExists();
} else if (!raceColumnUniqueInRegatta) { } else if (!raceColumnUniqueInRegatta) {
errorMessage = stringConstants.race() + " " + wrongRaceColumn.name + ": " errorMessage = stringMessages.race() + " " + wrongRaceColumn.name + ": "
+ stringConstants.raceWithThisNameAlreadyExistsInRegatta(); + stringMessages.raceWithThisNameAlreadyExistsInRegatta();
} else {
errorMessage = DiscardThresholdBoxes.getErrorMessage(valueToValidate.getResultDiscardingThresholds(), stringMessages);
} }
return errorMessage; return errorMessage;
} }
} }
public RaceColumnInRegattaSeriesDialog(RegattaDTO regatta, SeriesDTO selectedSeries, StringMessages stringConstants, public SeriesEditDialog(RegattaDTO regatta, SeriesDTO selectedSeries, StringMessages stringMessages,DialogCallback<SeriesDescriptor> callback) {
DialogCallback<Pair<SeriesDTO, List<RaceColumnDTO>>> callback) { super(stringMessages.actionEditRaces(), null, stringMessages.ok(), stringMessages.cancel(),
super(stringConstants.actionEditRaces(), null, stringConstants.ok(), stringConstants.cancel(), new RaceDialogValidator(regatta, stringMessages), callback);
new RaceDialogValidator(regatta, stringConstants), callback);
this.selectedSeries = selectedSeries; this.selectedSeries = selectedSeries;
this.stringMessages = stringConstants; this.stringMessages = stringMessages;
addRacesListBox = createListBox(false); addRacesListBox = createListBox(false);
raceNamePrefixTextBox = createTextBox(null); raceNamePrefixTextBox = createTextBox(null);
raceNameEntryFields = new ArrayList<TextBox>(); raceNameEntryFields = new ArrayList<TextBox>();
raceNameDeleteButtons = new ArrayList<Button>(); raceNameDeleteButtons = new ArrayList<Button>();
raceColumnsGrid = new Grid(0, 0); raceColumnsGrid = new Grid(0, 0);
discardThresholdBoxes = new DiscardThresholdBoxes(this, selectedSeries.getDiscardThresholds(), stringMessages);
} }
private Widget createRaceNameWidget(String defaultName, boolean enabled) { private Widget createRaceNameWidget(String defaultName, boolean enabled) {
@@ -146,7 +146,7 @@ public class RaceColumnInRegattaSeriesDialog extends DataEntryDialog<Pair<Series
} }
@Override @Override
protected Pair<SeriesDTO, List<RaceColumnDTO>> getResult() { protected SeriesDescriptor getResult() {
SeriesDTO selectedSeries = getSelectedSeries(); SeriesDTO selectedSeries = getSelectedSeries();
List<RaceColumnDTO> races = new ArrayList<RaceColumnDTO>(); List<RaceColumnDTO> races = new ArrayList<RaceColumnDTO>();
int racesCount = raceNameEntryFields.size(); int racesCount = raceNameEntryFields.size();
@@ -159,7 +159,9 @@ public class RaceColumnInRegattaSeriesDialog extends DataEntryDialog<Pair<Series
} }
races.add(raceColumnDTO); races.add(raceColumnDTO);
} }
return new Pair<SeriesDTO, List<RaceColumnDTO>>(selectedSeries, races); return new SeriesDescriptor(selectedSeries, races, isMedalCheckbox.getValue(),
useSeriesResultDiscardingThresholdsCheckbox.getValue() ?
discardThresholdBoxes.getDiscardThresholds() : null, startWithZeroScoreCheckbox.getValue());
} }
private RaceColumnDTO findRaceColumnInSeriesByName(SeriesDTO series, String raceColumnName) { private RaceColumnDTO findRaceColumnInSeriesByName(SeriesDTO series, String raceColumnName) {
@@ -182,18 +184,28 @@ public class RaceColumnInRegattaSeriesDialog extends DataEntryDialog<Pair<Series
if (additionalWidget != null) { if (additionalWidget != null) {
additionalWidgetPanel.add(additionalWidget); additionalWidgetPanel.add(additionalWidget);
} }
HorizontalPanel seriesPanel = new HorizontalPanel(); HorizontalPanel seriesPanel = new HorizontalPanel();
seriesPanel.setSpacing(3); seriesPanel.setSpacing(3);
String seriesName = getSelectedSeries().name; String seriesName = getSelectedSeries().name;
seriesPanel.add(new Label(stringMessages.series() + ": " + seriesName)); seriesPanel.add(new Label(stringMessages.series() + ": " + seriesName));
if ("Default".equals(seriesName)) {
raceNamePrefixTextBox.setText("R");
} else {
raceNamePrefixTextBox.setText(seriesName.substring(0, 1).toUpperCase());
}
additionalWidgetPanel.add(seriesPanel); additionalWidgetPanel.add(seriesPanel);
isMedalCheckbox = createCheckbox(stringMessages.medalSeries());
isMedalCheckbox.setValue(selectedSeries.isMedal());
additionalWidgetPanel.add(isMedalCheckbox);
startWithZeroScoreCheckbox = createCheckbox(stringMessages.startsWithZeroScore());
startWithZeroScoreCheckbox.setValue(selectedSeries.isStartsWithZeroScore());
additionalWidgetPanel.add(startWithZeroScoreCheckbox);
useSeriesResultDiscardingThresholdsCheckbox = createCheckbox(stringMessages.seriesDefinesResultDiscardingRule());
useSeriesResultDiscardingThresholdsCheckbox.setValue(selectedSeries.getDiscardThresholds() != null);
useSeriesResultDiscardingThresholdsCheckbox.addValueChangeHandler(new ValueChangeHandler<Boolean>() {
@Override
public void onValueChange(ValueChangeEvent<Boolean> event) {
discardThresholdBoxes.getWidget().setVisible(event.getValue());
}
});
additionalWidgetPanel.add(useSeriesResultDiscardingThresholdsCheckbox);
additionalWidgetPanel.add(discardThresholdBoxes.getWidget());
discardThresholdBoxes.getWidget().setVisible(useSeriesResultDiscardingThresholdsCheckbox.getValue());
// add races controls // add races controls
HorizontalPanel addRacesPanel = new HorizontalPanel(); HorizontalPanel addRacesPanel = new HorizontalPanel();
addRacesPanel.setSpacing(3); addRacesPanel.setSpacing(3);
@@ -203,6 +215,11 @@ public class RaceColumnInRegattaSeriesDialog extends DataEntryDialog<Pair<Series
addRacesListBox.addItem("" + i); addRacesListBox.addItem("" + i);
} }
addRacesListBox.setSelectedIndex(0); addRacesListBox.setSelectedIndex(0);
if ("Default".equals(seriesName)) {
raceNamePrefixTextBox.setText("R");
} else {
raceNamePrefixTextBox.setText(seriesName.substring(0, 1).toUpperCase());
}
raceNamePrefixTextBox.setWidth("20px"); raceNamePrefixTextBox.setWidth("20px");
addRacesPanel.add(raceNamePrefixTextBox); addRacesPanel.add(raceNamePrefixTextBox);
addRacesBtn = new Button(stringMessages.add()); addRacesBtn = new Button(stringMessages.add());
@@ -9,6 +9,8 @@ import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler; import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.user.client.ui.Button; import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.CheckBox; import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.Grid; import com.google.gwt.user.client.ui.Grid;
@@ -26,11 +28,14 @@ import com.sap.sailing.gwt.ui.shared.SeriesDTO;
public class SeriesWithFleetsCreateDialog extends DataEntryDialog<SeriesDTO> { public class SeriesWithFleetsCreateDialog extends DataEntryDialog<SeriesDTO> {
private StringMessages stringConstants; private StringMessages stringMessages;
private SeriesDTO series; private SeriesDTO series;
private TextBox nameEntryField; private TextBox nameEntryField;
private CheckBox isMedalSeriesCheckbox; private CheckBox isMedalSeriesCheckbox;
private CheckBox startsWithZeroScoreCheckbox;
private CheckBox useSeriesResultDiscardingThresholdsCheckbox;
private DiscardThresholdBoxes discardThresholdBoxes;
private List<TextBox> fleetNameEntryFields; private List<TextBox> fleetNameEntryFields;
private List<ListBox> fleetColorEntryFields; private List<ListBox> fleetColorEntryFields;
@@ -39,12 +44,11 @@ public class SeriesWithFleetsCreateDialog extends DataEntryDialog<SeriesDTO> {
private Grid fleetsGrid; private Grid fleetsGrid;
protected static class SeriesParameterValidator implements Validator<SeriesDTO> { protected static class SeriesParameterValidator implements Validator<SeriesDTO> {
private StringMessages stringMessages;
private StringMessages stringConstants;
private ArrayList<SeriesDTO> existingSeries; private ArrayList<SeriesDTO> existingSeries;
public SeriesParameterValidator(StringMessages stringConstants, Collection<SeriesDTO> existingSeries) { public SeriesParameterValidator(StringMessages stringConstants, Collection<SeriesDTO> existingSeries) {
this.stringConstants = stringConstants; this.stringMessages = stringConstants;
this.existingSeries = new ArrayList<SeriesDTO>(existingSeries); this.existingSeries = new ArrayList<SeriesDTO>(existingSeries);
} }
@@ -60,9 +64,9 @@ public class SeriesWithFleetsCreateDialog extends DataEntryDialog<SeriesDTO> {
} }
} }
if (!nameNotEmpty) { if (!nameNotEmpty) {
errorMessage = stringConstants.pleaseEnterAName(); errorMessage = stringMessages.pleaseEnterAName();
} else if (!unique) { } else if (!unique) {
errorMessage = stringConstants.seriesWithThisNameAlreadyExists(); errorMessage = stringMessages.seriesWithThisNameAlreadyExists();
} }
if (errorMessage == null) { if (errorMessage == null) {
List<FleetDTO> fleetsToValidate = seriesToValidate.getFleets(); List<FleetDTO> fleetsToValidate = seriesToValidate.getFleets();
@@ -86,24 +90,36 @@ public class SeriesWithFleetsCreateDialog extends DataEntryDialog<SeriesDTO> {
index2++; index2++;
} }
if (!fleetNameNotEmpty) { if (!fleetNameNotEmpty) {
errorMessage = stringConstants.fleet() + " " + (index + 1) + ": " + stringConstants.pleaseEnterAName(); errorMessage = stringMessages.fleet() + " " + (index + 1) + ": " + stringMessages.pleaseEnterAName();
} else if (!fleetUnique) { } else if (!fleetUnique) {
errorMessage = stringConstants.fleet() + " " + (index2 + 1) + ": " + stringConstants.fleetWithThisNameAlreadyExists(); errorMessage = stringMessages.fleet() + " " + (index2 + 1) + ": " + stringMessages.fleetWithThisNameAlreadyExists();
} else {
errorMessage = DiscardThresholdBoxes.getErrorMessage(seriesToValidate.getDiscardThresholds(), stringMessages);
} }
} }
return errorMessage; return errorMessage;
} }
} }
public SeriesWithFleetsCreateDialog(Collection<SeriesDTO> existingSeries, StringMessages stringConstants, public SeriesWithFleetsCreateDialog(Collection<SeriesDTO> existingSeries, StringMessages stringMessages,
DialogCallback<SeriesDTO> callback) { DialogCallback<SeriesDTO> callback) {
super(stringConstants.series(), null, stringConstants.ok(), stringConstants.cancel(), super(stringMessages.series(), null, stringMessages.ok(), stringMessages.cancel(),
new SeriesParameterValidator(stringConstants, existingSeries), callback); new SeriesParameterValidator(stringMessages, existingSeries), callback);
this.stringConstants = stringConstants; this.stringMessages = stringMessages;
this.series = new SeriesDTO(); this.series = new SeriesDTO();
nameEntryField = createTextBox(null); nameEntryField = createTextBox(null);
nameEntryField.setVisibleLength(40); nameEntryField.setVisibleLength(40);
isMedalSeriesCheckbox = createCheckbox(stringConstants.medalSeries()); isMedalSeriesCheckbox = createCheckbox(stringMessages.medalSeries());
startsWithZeroScoreCheckbox = createCheckbox(stringMessages.startsWithZeroScore());
useSeriesResultDiscardingThresholdsCheckbox = createCheckbox(stringMessages.seriesDefinesResultDiscardingRule());
useSeriesResultDiscardingThresholdsCheckbox.addValueChangeHandler(new ValueChangeHandler<Boolean>() {
@Override
public void onValueChange(ValueChangeEvent<Boolean> event) {
discardThresholdBoxes.getWidget().setVisible(event.getValue());
}
});
discardThresholdBoxes = new DiscardThresholdBoxes(this, stringMessages);
discardThresholdBoxes.getWidget().setVisible(false);
fleetNameEntryFields = new ArrayList<TextBox>(); fleetNameEntryFields = new ArrayList<TextBox>();
fleetColorEntryFields = new ArrayList<ListBox>(); fleetColorEntryFields = new ArrayList<ListBox>();
fleetOrderNoEntryFields = new ArrayList<IntegerBox>(); fleetOrderNoEntryFields = new ArrayList<IntegerBox>();
@@ -143,7 +159,7 @@ public class SeriesWithFleetsCreateDialog extends DataEntryDialog<SeriesDTO> {
validate(); validate();
} }
}); });
listBox.addItem(stringConstants.noColor()); listBox.addItem(stringMessages.noColor());
for(FleetColors value: FleetColors.values()) { for(FleetColors value: FleetColors.values()) {
listBox.addItem(value.name()); listBox.addItem(value.name());
} }
@@ -164,6 +180,7 @@ public class SeriesWithFleetsCreateDialog extends DataEntryDialog<SeriesDTO> {
protected SeriesDTO getResult() { protected SeriesDTO getResult() {
series.name = nameEntryField.getText(); series.name = nameEntryField.getText();
series.setMedal(isMedalSeriesCheckbox.getValue()); series.setMedal(isMedalSeriesCheckbox.getValue());
series.setStartsWithZeroScore(startsWithZeroScoreCheckbox.getValue());
List<FleetDTO> fleets = new ArrayList<FleetDTO>(); List<FleetDTO> fleets = new ArrayList<FleetDTO>();
int fleetsCount = fleetNameEntryFields.size(); int fleetsCount = fleetNameEntryFields.size();
for(int i = 0; i < fleetsCount; i++) { for(int i = 0; i < fleetsCount; i++) {
@@ -178,6 +195,7 @@ public class SeriesWithFleetsCreateDialog extends DataEntryDialog<SeriesDTO> {
fleets.add(fleetDTO); fleets.add(fleetDTO);
} }
series.setFleets(fleets); series.setFleets(fleets);
series.setDiscardThresholds(useSeriesResultDiscardingThresholdsCheckbox.getValue() ? discardThresholdBoxes.getDiscardThresholds() : null);
return series; return series;
} }
@@ -204,14 +222,17 @@ public class SeriesWithFleetsCreateDialog extends DataEntryDialog<SeriesDTO> {
if (additionalWidget != null) { if (additionalWidget != null) {
panel.add(additionalWidget); panel.add(additionalWidget);
} }
Grid formGrid = new Grid(2, 2); Grid formGrid = new Grid(5, 2);
panel.add(formGrid); panel.add(formGrid);
formGrid.setWidget(0, 0, new Label(stringConstants.name() + ":")); formGrid.setWidget(0, 0, new Label(stringMessages.name() + ":"));
formGrid.setWidget(0, 1, nameEntryField); formGrid.setWidget(0, 1, nameEntryField);
formGrid.setWidget(1, 1, isMedalSeriesCheckbox); formGrid.setWidget(1, 1, isMedalSeriesCheckbox);
panel.add(createHeadlineLabel(stringConstants.fleets())); formGrid.setWidget(2, 1, startsWithZeroScoreCheckbox);
formGrid.setWidget(3, 1, useSeriesResultDiscardingThresholdsCheckbox);
formGrid.setWidget(4, 1, discardThresholdBoxes.getWidget());
panel.add(createHeadlineLabel(stringMessages.fleets()));
panel.add(fleetsGrid); panel.add(fleetsGrid);
Button addFleetButton = new Button(stringConstants.addFleet()); Button addFleetButton = new Button(stringMessages.addFleet());
addFleetButton.addClickHandler(new ClickHandler() { addFleetButton.addClickHandler(new ClickHandler() {
@Override @Override
public void onClick(ClickEvent event) { public void onClick(ClickEvent event) {
@@ -236,9 +257,9 @@ public class SeriesWithFleetsCreateDialog extends DataEntryDialog<SeriesDTO> {
int fleetCount = fleetNameEntryFields.size(); int fleetCount = fleetNameEntryFields.size();
fleetsGrid = new Grid(fleetCount + 1, 3); fleetsGrid = new Grid(fleetCount + 1, 3);
fleetsGrid.setCellSpacing(4); fleetsGrid.setCellSpacing(4);
fleetsGrid.setHTML(0, 0, stringConstants.color()); fleetsGrid.setHTML(0, 0, stringMessages.color());
fleetsGrid.setHTML(0, 1, stringConstants.name()); fleetsGrid.setHTML(0, 1, stringMessages.name());
fleetsGrid.setHTML(0, 2, stringConstants.rank()); fleetsGrid.setHTML(0, 2, stringMessages.rank());
for(int i = 0; i < fleetCount; i++) { for(int i = 0; i < fleetCount; i++) {
fleetsGrid.setWidget(i+1, 0, fleetColorEntryFields.get(i)); fleetsGrid.setWidget(i+1, 0, fleetColorEntryFields.get(i));
fleetsGrid.setWidget(i+1, 1, fleetNameEntryFields.get(i)); fleetsGrid.setWidget(i+1, 1, fleetNameEntryFields.get(i));
@@ -384,7 +384,7 @@ public abstract class DataEntryDialog<T> {
} }
public void alignAllPanelWidgetsVertically(HorizontalPanel panel, HasVerticalAlignment.VerticalAlignmentConstant alignment) { public void alignAllPanelWidgetsVertically(HorizontalPanel panel, HasVerticalAlignment.VerticalAlignmentConstant alignment) {
for(int i = 0; i < panel.getWidgetCount(); i++) { for (int i = 0; i < panel.getWidgetCount(); i++) {
panel.setCellVerticalAlignment(panel.getWidget(i), alignment); panel.setCellVerticalAlignment(panel.getWidget(i), alignment);
} }
} }
@@ -143,7 +143,7 @@ public interface SailingService extends RemoteService {
void moveLeaderboardColumnDown(String leaderboardName, String columnName); void moveLeaderboardColumnDown(String leaderboardName, String columnName);
RegattaDTO createRegatta(String regattaName, String boatClassName, RegattaDTO createRegatta(String regattaName, String boatClassName,
LinkedHashMap<String, Pair<List<Triple<String, Integer, Color>>, Boolean>> seriesNamesWithFleetNamesAndFleetOrderingAndMedal, LinkedHashMap<String, Triple<List<Triple<String, Integer, Color>>, Pair<Boolean, Boolean>, int[]>> seriesNamesWithFleetNamesAndFleetOrderingAndMedal,
boolean persistent, ScoringSchemeType scoringSchemeType, String defaultCourseAreaId); boolean persistent, ScoringSchemeType scoringSchemeType, String defaultCourseAreaId);
void removeRegatta(RegattaIdentifier regattaIdentifier); void removeRegatta(RegattaIdentifier regattaIdentifier);
@@ -152,6 +152,8 @@ public interface SailingService extends RemoteService {
List<RaceColumnInSeriesDTO> addRaceColumnsToSeries(RegattaIdentifier regattaIdentifier, String seriesName, List<String> columnNames); List<RaceColumnInSeriesDTO> addRaceColumnsToSeries(RegattaIdentifier regattaIdentifier, String seriesName, List<String> columnNames);
void updateSeries(RegattaIdentifier regattaIdentifier, String seriesName, boolean isMedal, int[] resultDiscardingThresholds, boolean startsWithZeroScore);
RaceColumnInSeriesDTO addRaceColumnToSeries(RegattaIdentifier regattaIdentifier, String seriesName, String columnName); RaceColumnInSeriesDTO addRaceColumnToSeries(RegattaIdentifier regattaIdentifier, String seriesName, String columnName);
void removeRaceColumnsFromSeries(RegattaIdentifier regattaIdentifier, String seriesName, List<String> columnNames); void removeRaceColumnsFromSeries(RegattaIdentifier regattaIdentifier, String seriesName, List<String> columnNames);
@@ -364,11 +364,14 @@ public interface SailingServiceAsync {
AsyncCallback<Void> callback); AsyncCallback<Void> callback);
void createRegatta(String regattaName, String boatClassName, void createRegatta(String regattaName, String boatClassName,
LinkedHashMap<String, Pair<List<Triple<String, Integer, Color>>, Boolean>> seriesNamesWithFleetNamesAndFleetOrderingAndMedal, LinkedHashMap<String, Triple<List<Triple<String, Integer, Color>>, Pair<Boolean, Boolean>, int[]>> seriesNamesWithFleetNamesAndFleetOrderingAndMedal,
boolean persistent, ScoringSchemeType scoringSchemeType, String defaultCourseAreaId, AsyncCallback<RegattaDTO> callback); boolean persistent, ScoringSchemeType scoringSchemeType, String defaultCourseAreaId, AsyncCallback<RegattaDTO> callback);
void addRaceColumnsToSeries(RegattaIdentifier regattaIdentifier, String seriesName, List<String> columnNames, void addRaceColumnsToSeries(RegattaIdentifier regattaIdentifier, String seriesName, List<String> columnNames,
AsyncCallback<List<RaceColumnInSeriesDTO>> callback); AsyncCallback<List<RaceColumnInSeriesDTO>> callback);
void updateSeries(RegattaIdentifier regattaIdentifier, String seriesName, boolean isMedal, int[] resultDiscardingThresholds,
boolean startsWithZeroScore, AsyncCallback<Void> callback);
void removeRaceColumnsFromSeries(RegattaIdentifier regattaIdentifier, String seriesName, List<String> columnNames, void removeRaceColumnsFromSeries(RegattaIdentifier regattaIdentifier, String seriesName, List<String> columnNames,
AsyncCallback<Void> callback); AsyncCallback<Void> callback);
@@ -650,4 +650,8 @@ public interface StringMessages extends Messages {
String showFollowingRegattas(); String showFollowingRegattas();
String showOnlyRacesOfSameDay(); String showOnlyRacesOfSameDay();
String showOnlyCurrentlyRunningRaces(); String showOnlyCurrentlyRunningRaces();
String seriesDefinesResultDiscardingRule();
String addSeries();
String regattaDefinesResultDiscardingRules();
String startsWithZeroScore();
} }
@@ -643,3 +643,7 @@ showFollowingCourseAreas=Show following course areas
showFollowingRegattas=Show following regattas showFollowingRegattas=Show following regattas
showOnlyRacesOfSameDay=Show only races of same day showOnlyRacesOfSameDay=Show only races of same day
showOnlyCurrentlyRunningRaces=Show currently running races showOnlyCurrentlyRunningRaces=Show currently running races
seriesDefinesResultDiscardingRule=Series defines result discarding rules
addSeries=Add series
regattaDefinesResultDiscardingRules=Regatta defines result discarding rules
startsWithZeroScore=Series starts with zero score
@@ -641,3 +641,7 @@ showFollowingCourseAreas=Zeige folgende Rennbahnen
showFollowingRegattas=Zeige folgende Regatten showFollowingRegattas=Zeige folgende Regatten
showOnlyRacesOfSameDay=Zeige nur Rennen desselben Tags showOnlyRacesOfSameDay=Zeige nur Rennen desselben Tags
showOnlyCurrentlyRunningRaces=Zeige nur gerade laufende Rennen showOnlyCurrentlyRunningRaces=Zeige nur gerade laufende Rennen
seriesDefinesResultDiscardingRule=Serie legt Streichregel fest
addSeries=Serie hinzufügen
regattaDefinesResultDiscardingRules=Streichergebnisse werden von der Regatta festgelegt
startsWithZeroScore=Serie beginnt mit auf 0 gesetzten Ergebnissen
@@ -1201,7 +1201,7 @@ public class LeaderboardPanel extends FormPanel implements TimeListener, PlaySta
@Override @Override
public String getValue(LeaderboardRowDTO object) { public String getValue(LeaderboardRowDTO object) {
Double totalPoints = getLeaderboard().getTotalPoints(object); Double totalPoints = object.totalPoints;
return "" + (totalPoints == null ? "" : scoreFormat.format(totalPoints)); return "" + (totalPoints == null ? "" : scoreFormat.format(totalPoints));
} }
@@ -126,6 +126,7 @@ import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.leaderboard.LeaderboardGroup; import com.sap.sailing.domain.leaderboard.LeaderboardGroup;
import com.sap.sailing.domain.leaderboard.MetaLeaderboard; import com.sap.sailing.domain.leaderboard.MetaLeaderboard;
import com.sap.sailing.domain.leaderboard.RegattaLeaderboard; import com.sap.sailing.domain.leaderboard.RegattaLeaderboard;
import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule;
import com.sap.sailing.domain.leaderboard.caching.LiveLeaderboardUpdater; import com.sap.sailing.domain.leaderboard.caching.LiveLeaderboardUpdater;
import com.sap.sailing.domain.persistence.DomainObjectFactory; import com.sap.sailing.domain.persistence.DomainObjectFactory;
import com.sap.sailing.domain.persistence.MongoFactory; import com.sap.sailing.domain.persistence.MongoFactory;
@@ -260,6 +261,7 @@ import com.sap.sailing.server.operationaltransformation.UpdateLeaderboardMaxPoin
import com.sap.sailing.server.operationaltransformation.UpdateLeaderboardScoreCorrection; import com.sap.sailing.server.operationaltransformation.UpdateLeaderboardScoreCorrection;
import com.sap.sailing.server.operationaltransformation.UpdateLeaderboardScoreCorrectionMetadata; import com.sap.sailing.server.operationaltransformation.UpdateLeaderboardScoreCorrectionMetadata;
import com.sap.sailing.server.operationaltransformation.UpdateRaceDelayToLive; import com.sap.sailing.server.operationaltransformation.UpdateRaceDelayToLive;
import com.sap.sailing.server.operationaltransformation.UpdateSeries;
import com.sap.sailing.server.operationaltransformation.UpdateSpecificRegatta; import com.sap.sailing.server.operationaltransformation.UpdateSpecificRegatta;
import com.sap.sailing.server.replication.ReplicaDescriptor; import com.sap.sailing.server.replication.ReplicaDescriptor;
import com.sap.sailing.server.replication.ReplicationFactory; import com.sap.sailing.server.replication.ReplicationFactory;
@@ -486,7 +488,9 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
raceColumnDTO.setExplicitFactor(raceColumn.getExplicitFactor()); raceColumnDTO.setExplicitFactor(raceColumn.getExplicitFactor());
raceColumns.add(raceColumnDTO); raceColumns.add(raceColumnDTO);
} }
SeriesDTO result = new SeriesDTO(series.getName(), fleets, raceColumns, series.isMedal()); SeriesDTO result = new SeriesDTO(series.getName(), fleets, raceColumns, series.isMedal(),
series.getResultDiscardingRule() == null ? null : series.getResultDiscardingRule().getDiscardIndexResultsStartingWithHowManyRaces(),
series.isStartsWithZeroScore());
return result; return result;
} }
@@ -1596,7 +1600,11 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
} }
leaderboardDTO.setDelayToLiveInMillisForLatestRace(delayToLiveInMillisForLatestRace); leaderboardDTO.setDelayToLiveInMillisForLatestRace(delayToLiveInMillisForLatestRace);
leaderboardDTO.hasCarriedPoints = leaderboard.hasCarriedPoints(); leaderboardDTO.hasCarriedPoints = leaderboard.hasCarriedPoints();
leaderboardDTO.discardThresholds = leaderboard.getResultDiscardingRule().getDiscardIndexResultsStartingWithHowManyRaces(); if (leaderboard.getResultDiscardingRule() instanceof ThresholdBasedResultDiscardingRule) {
leaderboardDTO.discardThresholds = ((ThresholdBasedResultDiscardingRule) leaderboard.getResultDiscardingRule()).getDiscardIndexResultsStartingWithHowManyRaces();
} else {
leaderboardDTO.discardThresholds = null;
}
for (RaceColumn raceColumn : leaderboard.getRaceColumns()) { for (RaceColumn raceColumn : leaderboard.getRaceColumns()) {
for (Fleet fleet : raceColumn.getFleets()) { for (Fleet fleet : raceColumn.getFleets()) {
TimePoint latestTimePointAfterQueryTimePointWhenATrackedRaceWasLive = null; TimePoint latestTimePointAfterQueryTimePointWhenATrackedRaceWasLive = null;
@@ -2245,7 +2253,10 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
} }
Leaderboard overallLeaderboard = leaderboardGroup.getOverallLeaderboard(); Leaderboard overallLeaderboard = leaderboardGroup.getOverallLeaderboard();
if (overallLeaderboard != null) { if (overallLeaderboard != null) {
groupDTO.setOverallLeaderboardDiscardThresholds(overallLeaderboard.getResultDiscardingRule().getDiscardIndexResultsStartingWithHowManyRaces()); if (overallLeaderboard.getResultDiscardingRule() instanceof ThresholdBasedResultDiscardingRule) {
groupDTO.setOverallLeaderboardDiscardThresholds(((ThresholdBasedResultDiscardingRule) overallLeaderboard
.getResultDiscardingRule()).getDiscardIndexResultsStartingWithHowManyRaces());
}
groupDTO.setOverallLeaderboardScoringSchemeType(overallLeaderboard.getScoringScheme().getType()); groupDTO.setOverallLeaderboardScoringSchemeType(overallLeaderboard.getScoringScheme().getType());
} }
return groupDTO; return groupDTO;
@@ -2485,6 +2496,12 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
} }
return result; return result;
} }
@Override
public void updateSeries(RegattaIdentifier regattaIdentifier, String seriesName, boolean isMedal,
int[] resultDiscardingThresholds, boolean startsWithZeroScore) {
getService().apply(new UpdateSeries(regattaIdentifier, seriesName, isMedal, resultDiscardingThresholds, startsWithZeroScore));
}
@Override @Override
public RaceColumnInSeriesDTO addRaceColumnToSeries(RegattaIdentifier regattaIdentifier, String seriesName, String columnName) { public RaceColumnInSeriesDTO addRaceColumnToSeries(RegattaIdentifier regattaIdentifier, String seriesName, String columnName) {
@@ -2520,7 +2537,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
@Override @Override
public RegattaDTO createRegatta(String regattaName, String boatClassName, public RegattaDTO createRegatta(String regattaName, String boatClassName,
LinkedHashMap<String, Pair<List<Triple<String, Integer, Color>>, Boolean>> seriesNamesWithFleetNamesAndFleetOrderingAndMedal, LinkedHashMap<String, Triple<List<Triple<String, Integer, Color>>, Pair<Boolean, Boolean>, int[]>> seriesNamesWithFleetNamesAndFleetOrderingAndMedal,
boolean persistent, ScoringSchemeType scoringSchemeType, String defaultCourseAreaId) { boolean persistent, ScoringSchemeType scoringSchemeType, String defaultCourseAreaId) {
UUID courseAreaUuid = convertIdentifierStringToUuid(defaultCourseAreaId); UUID courseAreaUuid = convertIdentifierStringToUuid(defaultCourseAreaId);
Regatta regatta = getService().apply( Regatta regatta = getService().apply(
@@ -53,6 +53,20 @@ public class RegattaDTO extends NamedDTO {
} }
return tracked; return tracked;
} }
/**
* @return whether this regatta defines its local per-series result discarding rules; if so, any leaderboard based
* on the regatta has to respect this and has to use a result discarding rule implementation that
* keeps discards local to each series rather than spreading them across the entire leaderboard.
*/
public boolean definesSeriesDiscardThresholds() {
for (SeriesDTO s : series) {
if (s.definesSeriesDiscardThresholds()) {
return true;
}
}
return false;
}
@Override @Override
public int hashCode() { public int hashCode() {
@@ -11,14 +11,18 @@ public class SeriesDTO extends NamedDTO {
private List<FleetDTO> fleets; private List<FleetDTO> fleets;
private List<RaceColumnDTO> raceColumns; private List<RaceColumnDTO> raceColumns;
private boolean isMedal; private boolean isMedal;
private int[] discardThresholds;
private Boolean startsWithZeroScore;
public SeriesDTO() {} public SeriesDTO() {}
public SeriesDTO(String name, List<FleetDTO> fleets, List<RaceColumnDTO> raceColumns, boolean isMedal) { public SeriesDTO(String name, List<FleetDTO> fleets, List<RaceColumnDTO> raceColumns, boolean isMedal, int[] discardThresholds, boolean startsWithZeroScore) {
super(name); super(name);
this.fleets = fleets; this.fleets = fleets;
this.raceColumns = raceColumns; this.raceColumns = raceColumns;
this.isMedal = isMedal; this.isMedal = isMedal;
this.startsWithZeroScore = startsWithZeroScore;
this.discardThresholds = discardThresholds;
} }
public boolean isMedal() { public boolean isMedal() {
@@ -44,4 +48,29 @@ public class SeriesDTO extends NamedDTO {
public void setRaceColumns(List<RaceColumnDTO> raceColumns) { public void setRaceColumns(List<RaceColumnDTO> raceColumns) {
this.raceColumns = raceColumns; this.raceColumns = raceColumns;
} }
/**
* @return whether this series defines its local result discarding rule; if so, any leaderboard based on the
* enclosing regatta has to respect this and has to use a result discarding rule implementation that keeps
* discards local to each series rather than spreading them across the entire leaderboard.
*/
public boolean definesSeriesDiscardThresholds() {
return getDiscardThresholds() != null;
}
public int[] getDiscardThresholds() {
return discardThresholds;
}
public void setDiscardThresholds(int[] discardThresholds) {
this.discardThresholds = discardThresholds;
}
public Boolean isStartsWithZeroScore() {
return startsWithZeroScore;
}
public void setStartsWithZeroScore(Boolean startsWithZeroScore) {
this.startsWithZeroScore = startsWithZeroScore;
}
} }
@@ -1,6 +1,7 @@
package com.sap.sailing.mongodb.test; package com.sap.sailing.mongodb.test;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
@@ -53,8 +54,10 @@ import com.sap.sailing.domain.common.impl.Util;
import com.sap.sailing.domain.leaderboard.Leaderboard; import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.leaderboard.RegattaLeaderboard; import com.sap.sailing.domain.leaderboard.RegattaLeaderboard;
import com.sap.sailing.domain.leaderboard.ScoringScheme; import com.sap.sailing.domain.leaderboard.ScoringScheme;
import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule;
import com.sap.sailing.domain.leaderboard.impl.HighPoint; import com.sap.sailing.domain.leaderboard.impl.HighPoint;
import com.sap.sailing.domain.leaderboard.impl.LowPoint; import com.sap.sailing.domain.leaderboard.impl.LowPoint;
import com.sap.sailing.domain.leaderboard.impl.ThresholdBasedResultDiscardingRuleImpl;
import com.sap.sailing.domain.persistence.DomainObjectFactory; import com.sap.sailing.domain.persistence.DomainObjectFactory;
import com.sap.sailing.domain.persistence.MongoFactory; import com.sap.sailing.domain.persistence.MongoFactory;
import com.sap.sailing.domain.persistence.MongoObjectFactory; import com.sap.sailing.domain.persistence.MongoObjectFactory;
@@ -208,10 +211,10 @@ public class TestStoringAndLoadingEventsAndRegattas extends AbstractMongoDBTest
assertEquals(Util.size(regatta.getSeries()), Util.size(loadedRegatta.getSeries())); assertEquals(Util.size(regatta.getSeries()), Util.size(loadedRegatta.getSeries()));
Leaderboard loadedLeaderboard = resForLoading.getLeaderboardByName(loadedRegatta.getName()); Leaderboard loadedLeaderboard = resForLoading.getLeaderboardByName(loadedRegatta.getName());
assertNotNull(loadedLeaderboard); assertNotNull(loadedLeaderboard);
assertEquals(regattaLeaderboard.getResultDiscardingRule().getDiscardIndexResultsStartingWithHowManyRaces().length, assertEquals(((ThresholdBasedResultDiscardingRule) regattaLeaderboard.getResultDiscardingRule()).getDiscardIndexResultsStartingWithHowManyRaces().length,
loadedLeaderboard.getResultDiscardingRule().getDiscardIndexResultsStartingWithHowManyRaces().length); ((ThresholdBasedResultDiscardingRule) loadedLeaderboard.getResultDiscardingRule()).getDiscardIndexResultsStartingWithHowManyRaces().length);
assertTrue(Arrays.equals(regattaLeaderboard.getResultDiscardingRule().getDiscardIndexResultsStartingWithHowManyRaces(), assertTrue(Arrays.equals(((ThresholdBasedResultDiscardingRule) regattaLeaderboard.getResultDiscardingRule()).getDiscardIndexResultsStartingWithHowManyRaces(),
loadedLeaderboard.getResultDiscardingRule().getDiscardIndexResultsStartingWithHowManyRaces())); ((ThresholdBasedResultDiscardingRule) loadedLeaderboard.getResultDiscardingRule()).getDiscardIndexResultsStartingWithHowManyRaces()));
assertTrue(loadedLeaderboard instanceof RegattaLeaderboard); assertTrue(loadedLeaderboard instanceof RegattaLeaderboard);
RegattaLeaderboard loadedRegattaLeaderboard = (RegattaLeaderboard) loadedLeaderboard; RegattaLeaderboard loadedRegattaLeaderboard = (RegattaLeaderboard) loadedLeaderboard;
assertSame(loadedRegatta, loadedRegattaLeaderboard.getRegatta()); assertSame(loadedRegatta, loadedRegattaLeaderboard.getRegatta());
@@ -283,6 +286,42 @@ public class TestStoringAndLoadingEventsAndRegattas extends AbstractMongoDBTest
assertEquals(1, Util.size(loadedMedalSeries.getRaceColumns())); assertEquals(1, Util.size(loadedMedalSeries.getRaceColumns()));
} }
@Test
public void testLoadStoreSimpleRegattaWithSeriesScoringScheme() {
final int numberOfQualifyingRaces = 5;
final int numberOfFinalRaces = 7;
final String regattaBaseName = "Kieler Woche";
BoatClass boatClass = DomainFactory.INSTANCE.getOrCreateBoatClass("29erXX", /* typicallyStartsUpwind */ true);
Regatta regatta = createRegattaAndAddRaceColumns(numberOfQualifyingRaces, numberOfFinalRaces, regattaBaseName,
boatClass, /* persistent */false, DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT));
regatta.getSeriesByName("Qualifying").setResultDiscardingRule(new ThresholdBasedResultDiscardingRuleImpl(new int[] { 1, 2, 3 }));
MongoObjectFactory mof = MongoFactory.INSTANCE.getMongoObjectFactory(getMongoService());
mof.storeRegatta(regatta);
DomainObjectFactory dof = MongoFactory.INSTANCE.getDomainObjectFactory(getMongoService());
Regatta loadedRegatta = dof.loadRegatta(regatta.getName(), /* trackedRegattaRegistry */ null);
assertTrue(Arrays.equals(new int[] { 1, 2, 3 },
loadedRegatta.getSeriesByName("Qualifying").getResultDiscardingRule().getDiscardIndexResultsStartingWithHowManyRaces()));
}
@Test
public void testLoadStoreSimpleRegattaWithScoreForMedalStartingWithZero() {
final int numberOfQualifyingRaces = 5;
final int numberOfFinalRaces = 7;
final String regattaBaseName = "Kieler Woche";
BoatClass boatClass = DomainFactory.INSTANCE.getOrCreateBoatClass("29erXX", /* typicallyStartsUpwind */ true);
Regatta regatta = createRegattaAndAddRaceColumns(numberOfQualifyingRaces, numberOfFinalRaces, regattaBaseName,
boatClass, /* persistent */false, DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT));
regatta.getSeriesByName("Medal").setStartsWithZeroScore(true);
MongoObjectFactory mof = MongoFactory.INSTANCE.getMongoObjectFactory(getMongoService());
mof.storeRegatta(regatta);
DomainObjectFactory dof = MongoFactory.INSTANCE.getDomainObjectFactory(getMongoService());
Regatta loadedRegatta = dof.loadRegatta(regatta.getName(), /* trackedRegattaRegistry */ null);
assertFalse(loadedRegatta.getSeriesByName("Qualifying").isStartsWithZeroScore());
assertTrue(loadedRegatta.getSeriesByName("Medal").isStartsWithZeroScore());
}
@Test @Test
public void testLoadStoreSimpleRegattaWithHighPointScoringScheme() { public void testLoadStoreSimpleRegattaWithHighPointScoringScheme() {
final int numberOfQualifyingRaces = 5; final int numberOfQualifyingRaces = 5;
@@ -39,7 +39,7 @@ import com.sap.sailing.domain.leaderboard.impl.FlexibleLeaderboardImpl;
import com.sap.sailing.domain.leaderboard.impl.HighPointExtremeSailingSeriesOverall; import com.sap.sailing.domain.leaderboard.impl.HighPointExtremeSailingSeriesOverall;
import com.sap.sailing.domain.leaderboard.impl.LeaderboardGroupImpl; import com.sap.sailing.domain.leaderboard.impl.LeaderboardGroupImpl;
import com.sap.sailing.domain.leaderboard.impl.LowPoint; import com.sap.sailing.domain.leaderboard.impl.LowPoint;
import com.sap.sailing.domain.leaderboard.impl.ResultDiscardingRuleImpl; import com.sap.sailing.domain.leaderboard.impl.ThresholdBasedResultDiscardingRuleImpl;
import com.sap.sailing.domain.leaderboard.impl.ScoreCorrectionImpl; import com.sap.sailing.domain.leaderboard.impl.ScoreCorrectionImpl;
import com.sap.sailing.domain.leaderboard.meta.LeaderboardGroupMetaLeaderboard; import com.sap.sailing.domain.leaderboard.meta.LeaderboardGroupMetaLeaderboard;
import com.sap.sailing.domain.persistence.DomainObjectFactory; import com.sap.sailing.domain.persistence.DomainObjectFactory;
@@ -77,21 +77,21 @@ public class TestStoringAndRetrievingLeaderboardGroups extends AbstractMongoDBTe
final ArrayList<Leaderboard> leaderboards = new ArrayList<>(); final ArrayList<Leaderboard> leaderboards = new ArrayList<>();
Leaderboard leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[0], new ScoreCorrectionImpl(), Leaderboard leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[0], new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null); new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
leaderboards.add(leaderboard); leaderboards.add(leaderboard);
leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[1], new ScoreCorrectionImpl(), leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[1], new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null); new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
leaderboards.add(leaderboard); leaderboards.add(leaderboard);
leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[2], new ScoreCorrectionImpl(), leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[2], new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null); new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
leaderboards.add(leaderboard); leaderboards.add(leaderboard);
leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[3], new ScoreCorrectionImpl(), leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[3], new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null); new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
leaderboards.add(leaderboard); leaderboards.add(leaderboard);
final LeaderboardGroup leaderboardGroup = new LeaderboardGroupImpl(groupName, groupDescription, false, leaderboards); final LeaderboardGroup leaderboardGroup = new LeaderboardGroupImpl(groupName, groupDescription, false, leaderboards);
final Leaderboard overallLeaderboard = new LeaderboardGroupMetaLeaderboard(leaderboardGroup, final Leaderboard overallLeaderboard = new LeaderboardGroupMetaLeaderboard(leaderboardGroup,
new HighPointExtremeSailingSeriesOverall(), new ResultDiscardingRuleImpl(new int[0])); new HighPointExtremeSailingSeriesOverall(), new ThresholdBasedResultDiscardingRuleImpl(new int[0]));
leaderboardGroup.setOverallLeaderboard(overallLeaderboard); leaderboardGroup.setOverallLeaderboard(overallLeaderboard);
mongoObjectFactory.storeLeaderboardGroup(leaderboardGroup); mongoObjectFactory.storeLeaderboardGroup(leaderboardGroup);
@@ -125,18 +125,18 @@ public class TestStoringAndRetrievingLeaderboardGroups extends AbstractMongoDBTe
final String groupDescription = "A leaderboard group"; final String groupDescription = "A leaderboard group";
FlexibleLeaderboard leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[0], new ScoreCorrectionImpl(), FlexibleLeaderboard leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[0], new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null); new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
racingEventService.addLeaderboard(leaderboard); racingEventService.addLeaderboard(leaderboard);
leaderboard.addRace(raceWithTwoCompetitors, raceColumnName1, /* medalRace */ false, leaderboard.addRace(raceWithTwoCompetitors, raceColumnName1, /* medalRace */ false,
leaderboard.getFleet(null)); leaderboard.getFleet(null));
leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[1], new ScoreCorrectionImpl(), leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[1], new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null); new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
racingEventService.addLeaderboard(leaderboard); racingEventService.addLeaderboard(leaderboard);
leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[2], new ScoreCorrectionImpl(), leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[2], new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null); new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
racingEventService.addLeaderboard(leaderboard); racingEventService.addLeaderboard(leaderboard);
leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[3], new ScoreCorrectionImpl(), leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[3], new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null); new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
racingEventService.addLeaderboard(leaderboard); racingEventService.addLeaderboard(leaderboard);
LeaderboardGroup leaderboardGroup = racingEventService.addLeaderboardGroup(groupName, groupDescription, false, Arrays.asList(leaderboardNames), new int[0], LeaderboardGroup leaderboardGroup = racingEventService.addLeaderboardGroup(groupName, groupDescription, false, Arrays.asList(leaderboardNames), new int[0],
@@ -182,23 +182,23 @@ public class TestStoringAndRetrievingLeaderboardGroups extends AbstractMongoDBTe
final ArrayList<Leaderboard> leaderboards = new ArrayList<>(); final ArrayList<Leaderboard> leaderboards = new ArrayList<>();
FlexibleLeaderboard leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[0], new ScoreCorrectionImpl(), FlexibleLeaderboard leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[0], new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null); new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
leaderboards.add(leaderboard); leaderboards.add(leaderboard);
leaderboard.addRace(raceWithTwoCompetitors, raceColumnName1, /* medalRace */ false, leaderboard.addRace(raceWithTwoCompetitors, raceColumnName1, /* medalRace */ false,
leaderboard.getFleet(null)); leaderboard.getFleet(null));
leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[1], new ScoreCorrectionImpl(), leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[1], new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null); new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
leaderboards.add(leaderboard); leaderboards.add(leaderboard);
leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[2], new ScoreCorrectionImpl(), leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[2], new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null); new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
leaderboards.add(leaderboard); leaderboards.add(leaderboard);
leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[3], new ScoreCorrectionImpl(), leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[3], new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null); new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
leaderboards.add(leaderboard); leaderboards.add(leaderboard);
final LeaderboardGroup leaderboardGroup = new LeaderboardGroupImpl(groupName, groupDescription, false, leaderboards); final LeaderboardGroup leaderboardGroup = new LeaderboardGroupImpl(groupName, groupDescription, false, leaderboards);
final Leaderboard overallLeaderboard = new LeaderboardGroupMetaLeaderboard(leaderboardGroup, final Leaderboard overallLeaderboard = new LeaderboardGroupMetaLeaderboard(leaderboardGroup,
new HighPointExtremeSailingSeriesOverall(), new ResultDiscardingRuleImpl(new int[0])); new HighPointExtremeSailingSeriesOverall(), new ThresholdBasedResultDiscardingRuleImpl(new int[0]));
leaderboardGroup.setOverallLeaderboard(overallLeaderboard); leaderboardGroup.setOverallLeaderboard(overallLeaderboard);
overallLeaderboard.setSuppressed(wolfgang, true); overallLeaderboard.setSuppressed(wolfgang, true);
mongoObjectFactory.storeLeaderboardGroup(leaderboardGroup); mongoObjectFactory.storeLeaderboardGroup(leaderboardGroup);
@@ -239,23 +239,23 @@ public class TestStoringAndRetrievingLeaderboardGroups extends AbstractMongoDBTe
final ArrayList<Leaderboard> leaderboards = new ArrayList<>(); final ArrayList<Leaderboard> leaderboards = new ArrayList<>();
FlexibleLeaderboard leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[0], new ScoreCorrectionImpl(), FlexibleLeaderboard leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[0], new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null); new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
leaderboards.add(leaderboard); leaderboards.add(leaderboard);
leaderboard.addRace(raceWithTwoCompetitors, raceColumnName1, /* medalRace */ false, leaderboard.addRace(raceWithTwoCompetitors, raceColumnName1, /* medalRace */ false,
leaderboard.getFleet(null)); leaderboard.getFleet(null));
leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[1], new ScoreCorrectionImpl(), leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[1], new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null); new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
leaderboards.add(leaderboard); leaderboards.add(leaderboard);
leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[2], new ScoreCorrectionImpl(), leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[2], new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null); new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
leaderboards.add(leaderboard); leaderboards.add(leaderboard);
leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[3], new ScoreCorrectionImpl(), leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[3], new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null); new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
leaderboards.add(leaderboard); leaderboards.add(leaderboard);
final LeaderboardGroup leaderboardGroup = new LeaderboardGroupImpl(groupName, groupDescription, false, leaderboards); final LeaderboardGroup leaderboardGroup = new LeaderboardGroupImpl(groupName, groupDescription, false, leaderboards);
final Leaderboard overallLeaderboard = new LeaderboardGroupMetaLeaderboard(leaderboardGroup, final Leaderboard overallLeaderboard = new LeaderboardGroupMetaLeaderboard(leaderboardGroup,
new HighPointExtremeSailingSeriesOverall(), new ResultDiscardingRuleImpl(new int[0])); new HighPointExtremeSailingSeriesOverall(), new ThresholdBasedResultDiscardingRuleImpl(new int[0]));
leaderboardGroup.setOverallLeaderboard(overallLeaderboard); leaderboardGroup.setOverallLeaderboard(overallLeaderboard);
overallLeaderboard.setSuppressed(wolfgang, true); overallLeaderboard.setSuppressed(wolfgang, true);
overallLeaderboard.setSuppressed(hasso, true); overallLeaderboard.setSuppressed(hasso, true);
@@ -287,13 +287,13 @@ public class TestStoringAndRetrievingLeaderboardGroups extends AbstractMongoDBTe
final String groupDescription1 = "A leaderboard group 1"; final String groupDescription1 = "A leaderboard group 1";
final ArrayList<Leaderboard> leaderboards1 = new ArrayList<>(); final ArrayList<Leaderboard> leaderboards1 = new ArrayList<>();
Leaderboard leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[0], new ScoreCorrectionImpl(), Leaderboard leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[0], new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null); new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
leaderboards1.add(leaderboard); leaderboards1.add(leaderboard);
leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[1], new ScoreCorrectionImpl(), leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[1], new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null); new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
leaderboards1.add(leaderboard); leaderboards1.add(leaderboard);
leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[2], new ScoreCorrectionImpl(), leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[2], new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null); new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
leaderboards1.add(leaderboard); leaderboards1.add(leaderboard);
final LeaderboardGroup leaderboardGroup1 = new LeaderboardGroupImpl(groupName1, groupDescription1, false, leaderboards1); final LeaderboardGroup leaderboardGroup1 = new LeaderboardGroupImpl(groupName1, groupDescription1, false, leaderboards1);
mongoObjectFactory.storeLeaderboardGroup(leaderboardGroup1); mongoObjectFactory.storeLeaderboardGroup(leaderboardGroup1);
@@ -302,10 +302,10 @@ public class TestStoringAndRetrievingLeaderboardGroups extends AbstractMongoDBTe
final String groupDescription2= "A leaderboard group 2"; final String groupDescription2= "A leaderboard group 2";
final ArrayList<Leaderboard> leaderboards2 = new ArrayList<>(); final ArrayList<Leaderboard> leaderboards2 = new ArrayList<>();
leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[2], new ScoreCorrectionImpl(), leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[2], new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null); new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
leaderboards2.add(leaderboard); leaderboards2.add(leaderboard);
leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[3], new ScoreCorrectionImpl(), leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[3], new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null); new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
leaderboards2.add(leaderboard); leaderboards2.add(leaderboard);
final LeaderboardGroup leaderboardGroup2 = new LeaderboardGroupImpl(groupName2, groupDescription2, false, leaderboards2); final LeaderboardGroup leaderboardGroup2 = new LeaderboardGroupImpl(groupName2, groupDescription2, false, leaderboards2);
mongoObjectFactory.storeLeaderboardGroup(leaderboardGroup2); mongoObjectFactory.storeLeaderboardGroup(leaderboardGroup2);
@@ -332,16 +332,16 @@ public class TestStoringAndRetrievingLeaderboardGroups extends AbstractMongoDBTe
final ArrayList<Leaderboard> leaderboards = new ArrayList<>(); final ArrayList<Leaderboard> leaderboards = new ArrayList<>();
Leaderboard leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[0], new ScoreCorrectionImpl(), Leaderboard leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[0], new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null); new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
leaderboards.add(leaderboard); leaderboards.add(leaderboard);
leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[1], new ScoreCorrectionImpl(), leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[1], new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null); new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
leaderboards.add(leaderboard); leaderboards.add(leaderboard);
leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[2], new ScoreCorrectionImpl(), leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[2], new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null); new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
leaderboards.add(leaderboard); leaderboards.add(leaderboard);
leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[3], new ScoreCorrectionImpl(), leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[3], new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null); new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
leaderboards.add(leaderboard); leaderboards.add(leaderboard);
final LeaderboardGroup leaderboardGroup = new LeaderboardGroupImpl(groupName, groupDescription, false, leaderboards); final LeaderboardGroup leaderboardGroup = new LeaderboardGroupImpl(groupName, groupDescription, false, leaderboards);
@@ -370,10 +370,10 @@ public class TestStoringAndRetrievingLeaderboardGroups extends AbstractMongoDBTe
final ArrayList<Leaderboard> leaderboards = new ArrayList<>(); final ArrayList<Leaderboard> leaderboards = new ArrayList<>();
Leaderboard leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[0], new ScoreCorrectionImpl(), Leaderboard leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[0], new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null); new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
leaderboards.add(leaderboard); leaderboards.add(leaderboard);
leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[1], new ScoreCorrectionImpl(), leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[1], new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null); new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
leaderboards.add(leaderboard); leaderboards.add(leaderboard);
final LeaderboardGroup leaderboardGroup = new LeaderboardGroupImpl(groupName, groupDescription, false, leaderboards); final LeaderboardGroup leaderboardGroup = new LeaderboardGroupImpl(groupName, groupDescription, false, leaderboards);
@@ -383,11 +383,11 @@ public class TestStoringAndRetrievingLeaderboardGroups extends AbstractMongoDBTe
final FlexibleLeaderboard[] ungroupedLeaderboards = { final FlexibleLeaderboard[] ungroupedLeaderboards = {
new FlexibleLeaderboardImpl(ungroupedLeaderboardNames[0], new ScoreCorrectionImpl(), new FlexibleLeaderboardImpl(ungroupedLeaderboardNames[0], new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null), new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null),
new FlexibleLeaderboardImpl(ungroupedLeaderboardNames[1], new ScoreCorrectionImpl(), new FlexibleLeaderboardImpl(ungroupedLeaderboardNames[1], new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null), new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null),
new FlexibleLeaderboardImpl(ungroupedLeaderboardNames[2], new ScoreCorrectionImpl(), new FlexibleLeaderboardImpl(ungroupedLeaderboardNames[2], new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null) }; new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null) };
mongoObjectFactory.storeLeaderboard(ungroupedLeaderboards[0]); mongoObjectFactory.storeLeaderboard(ungroupedLeaderboards[0]);
mongoObjectFactory.storeLeaderboard(ungroupedLeaderboards[1]); mongoObjectFactory.storeLeaderboard(ungroupedLeaderboards[1]);
mongoObjectFactory.storeLeaderboard(ungroupedLeaderboards[2]); mongoObjectFactory.storeLeaderboard(ungroupedLeaderboards[2]);
@@ -424,7 +424,7 @@ public class TestStoringAndRetrievingLeaderboardGroups extends AbstractMongoDBTe
final String groupDescription = "A leaderboard group"; final String groupDescription = "A leaderboard group";
final ArrayList<Leaderboard> leaderboards = new ArrayList<>(); final ArrayList<Leaderboard> leaderboards = new ArrayList<>();
final FlexibleLeaderboard leaderboard = new FlexibleLeaderboardImpl(leaderboardName, new ScoreCorrectionImpl(), new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null); final FlexibleLeaderboard leaderboard = new FlexibleLeaderboardImpl(leaderboardName, new ScoreCorrectionImpl(), new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
final Fleet fleet = leaderboard.getFleet(null); final Fleet fleet = leaderboard.getFleet(null);
final RaceColumn race = leaderboard.addRaceColumn(columnName, false, fleet); final RaceColumn race = leaderboard.addRaceColumn(columnName, false, fleet);
leaderboards.add(leaderboard); leaderboards.add(leaderboard);
@@ -27,10 +27,11 @@ import com.sap.sailing.domain.common.impl.Util;
import com.sap.sailing.domain.leaderboard.FlexibleLeaderboard; import com.sap.sailing.domain.leaderboard.FlexibleLeaderboard;
import com.sap.sailing.domain.leaderboard.Leaderboard; import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.leaderboard.SettableScoreCorrection; import com.sap.sailing.domain.leaderboard.SettableScoreCorrection;
import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule;
import com.sap.sailing.domain.leaderboard.impl.FlexibleLeaderboardImpl; import com.sap.sailing.domain.leaderboard.impl.FlexibleLeaderboardImpl;
import com.sap.sailing.domain.leaderboard.impl.HighPoint; import com.sap.sailing.domain.leaderboard.impl.HighPoint;
import com.sap.sailing.domain.leaderboard.impl.LowPoint; import com.sap.sailing.domain.leaderboard.impl.LowPoint;
import com.sap.sailing.domain.leaderboard.impl.ResultDiscardingRuleImpl; import com.sap.sailing.domain.leaderboard.impl.ThresholdBasedResultDiscardingRuleImpl;
import com.sap.sailing.domain.leaderboard.impl.ScoreCorrectionImpl; import com.sap.sailing.domain.leaderboard.impl.ScoreCorrectionImpl;
import com.sap.sailing.domain.persistence.impl.DomainObjectFactoryImpl; import com.sap.sailing.domain.persistence.impl.DomainObjectFactoryImpl;
import com.sap.sailing.domain.persistence.impl.MongoObjectFactoryImpl; import com.sap.sailing.domain.persistence.impl.MongoObjectFactoryImpl;
@@ -48,7 +49,7 @@ public class TestStoringAndRetrievingLeaderboards extends AbstractMongoDBTest {
final String leaderboardName = "TestLeaderboard"; final String leaderboardName = "TestLeaderboard";
final int[] discardIndexResultsStartingWithHowManyRaces = new int[] { 5, 8 }; final int[] discardIndexResultsStartingWithHowManyRaces = new int[] { 5, 8 };
FlexibleLeaderboardImpl leaderboard = new FlexibleLeaderboardImpl(leaderboardName, new ScoreCorrectionImpl(), FlexibleLeaderboardImpl leaderboard = new FlexibleLeaderboardImpl(leaderboardName, new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null); new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
leaderboard.addRaceColumn("R1", /* medalRace */ false); leaderboard.addRaceColumn("R1", /* medalRace */ false);
RaceColumn r2 = leaderboard.addRaceColumn("R2", /* medalRace */ false); RaceColumn r2 = leaderboard.addRaceColumn("R2", /* medalRace */ false);
r2.setFactor(1.5); r2.setFactor(1.5);
@@ -66,7 +67,7 @@ public class TestStoringAndRetrievingLeaderboards extends AbstractMongoDBTest {
final String leaderboardName = "TestLeaderboard"; final String leaderboardName = "TestLeaderboard";
final int[] discardIndexResultsStartingWithHowManyRaces = new int[] { 5, 8 }; final int[] discardIndexResultsStartingWithHowManyRaces = new int[] { 5, 8 };
FlexibleLeaderboardImpl leaderboard = new FlexibleLeaderboardImpl(leaderboardName, new ScoreCorrectionImpl(), FlexibleLeaderboardImpl leaderboard = new FlexibleLeaderboardImpl(leaderboardName, new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null); new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
Competitor wolfgang = createCompetitor(); Competitor wolfgang = createCompetitor();
Competitor hasso = new CompetitorImpl(234, "Hasso Plattner", new TeamImpl("STG", Collections.singleton( Competitor hasso = new CompetitorImpl(234, "Hasso Plattner", new TeamImpl("STG", Collections.singleton(
new PersonImpl("Hasso Plattner", new NationalityImpl("GER"), new PersonImpl("Hasso Plattner", new NationalityImpl("GER"),
@@ -95,7 +96,7 @@ public class TestStoringAndRetrievingLeaderboards extends AbstractMongoDBTest {
final int[] discardIndexResultsStartingWithHowManyRaces = new int[] { 5, 8 }; final int[] discardIndexResultsStartingWithHowManyRaces = new int[] { 5, 8 };
SettableScoreCorrection scoreCorrection = new ScoreCorrectionImpl(); SettableScoreCorrection scoreCorrection = new ScoreCorrectionImpl();
FlexibleLeaderboardImpl leaderboard = new FlexibleLeaderboardImpl(leaderboardName, scoreCorrection, FlexibleLeaderboardImpl leaderboard = new FlexibleLeaderboardImpl(leaderboardName, scoreCorrection,
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null); new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
scoreCorrection.setComment("Humba"); scoreCorrection.setComment("Humba");
MillisecondsTimePoint now = MillisecondsTimePoint.now(); MillisecondsTimePoint now = MillisecondsTimePoint.now();
scoreCorrection.setTimePointOfLastCorrectionsValidity(now); scoreCorrection.setTimePointOfLastCorrectionsValidity(now);
@@ -111,7 +112,7 @@ public class TestStoringAndRetrievingLeaderboards extends AbstractMongoDBTest {
final int[] discardIndexResultsStartingWithHowManyRaces = new int[] { 5, 8 }; final int[] discardIndexResultsStartingWithHowManyRaces = new int[] { 5, 8 };
SettableScoreCorrection scoreCorrection = new ScoreCorrectionImpl(); SettableScoreCorrection scoreCorrection = new ScoreCorrectionImpl();
FlexibleLeaderboardImpl leaderboard = new FlexibleLeaderboardImpl(leaderboardName, scoreCorrection, FlexibleLeaderboardImpl leaderboard = new FlexibleLeaderboardImpl(leaderboardName, scoreCorrection,
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null); new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
Competitor competitor = createCompetitor(); Competitor competitor = createCompetitor();
TrackedRace raceWithOneCompetitor1 = new MockedTrackedRaceWithFixedRank(competitor, /* rank */ 1, /* started */ true); TrackedRace raceWithOneCompetitor1 = new MockedTrackedRaceWithFixedRank(competitor, /* rank */ 1, /* started */ true);
final String raceColumnName1 = "My First Race 1"; final String raceColumnName1 = "My First Race 1";
@@ -132,12 +133,12 @@ public class TestStoringAndRetrievingLeaderboards extends AbstractMongoDBTest {
final String leaderboardName = "TestLeaderboard"; final String leaderboardName = "TestLeaderboard";
final int[] discardIndexResultsStartingWithHowManyRaces = new int[] { 5, 8 }; final int[] discardIndexResultsStartingWithHowManyRaces = new int[] { 5, 8 };
FlexibleLeaderboardImpl leaderboard = new FlexibleLeaderboardImpl(leaderboardName, new ScoreCorrectionImpl(), FlexibleLeaderboardImpl leaderboard = new FlexibleLeaderboardImpl(leaderboardName, new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null); new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
new MongoObjectFactoryImpl(db).storeLeaderboard(leaderboard); new MongoObjectFactoryImpl(db).storeLeaderboard(leaderboard);
Leaderboard loadedLeaderboard = new DomainObjectFactoryImpl(db).loadLeaderboard(leaderboardName, /* regattaRegistry */ null); Leaderboard loadedLeaderboard = new DomainObjectFactoryImpl(db).loadLeaderboard(leaderboardName, /* regattaRegistry */ null);
assertEquals(leaderboardName, loadedLeaderboard.getName()); assertEquals(leaderboardName, loadedLeaderboard.getName());
assertTrue(Arrays.equals(discardIndexResultsStartingWithHowManyRaces, loadedLeaderboard.getResultDiscardingRule() assertTrue(Arrays.equals(discardIndexResultsStartingWithHowManyRaces,
.getDiscardIndexResultsStartingWithHowManyRaces())); ((ThresholdBasedResultDiscardingRule) loadedLeaderboard.getResultDiscardingRule()).getDiscardIndexResultsStartingWithHowManyRaces()));
} }
@Test @Test
@@ -145,7 +146,7 @@ public class TestStoringAndRetrievingLeaderboards extends AbstractMongoDBTest {
final String leaderboardName = "TestLeaderboard"; final String leaderboardName = "TestLeaderboard";
final int[] discardIndexResultsStartingWithHowManyRaces = new int[] { 5, 8 }; final int[] discardIndexResultsStartingWithHowManyRaces = new int[] { 5, 8 };
FlexibleLeaderboardImpl leaderboard = new FlexibleLeaderboardImpl(leaderboardName, new ScoreCorrectionImpl(), FlexibleLeaderboardImpl leaderboard = new FlexibleLeaderboardImpl(leaderboardName, new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new HighPoint(), null); new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new HighPoint(), null);
new MongoObjectFactoryImpl(db).storeLeaderboard(leaderboard); new MongoObjectFactoryImpl(db).storeLeaderboard(leaderboard);
Leaderboard loadedLeaderboard = new DomainObjectFactoryImpl(db).loadLeaderboard(leaderboardName, /* regattaRegistry */ null); Leaderboard loadedLeaderboard = new DomainObjectFactoryImpl(db).loadLeaderboard(leaderboardName, /* regattaRegistry */ null);
assertSame(HighPoint.class, loadedLeaderboard.getScoringScheme().getClass()); assertSame(HighPoint.class, loadedLeaderboard.getScoringScheme().getClass());
@@ -159,7 +160,7 @@ public class TestStoringAndRetrievingLeaderboards extends AbstractMongoDBTest {
final double carriedPointsForWolfgangHunger = 3.7; final double carriedPointsForWolfgangHunger = 3.7;
final int[] discardIndexResultsStartingWithHowManyRaces = new int[] { 5, 8 }; final int[] discardIndexResultsStartingWithHowManyRaces = new int[] { 5, 8 };
FlexibleLeaderboardImpl leaderboard = new FlexibleLeaderboardImpl(leaderboardName, new ScoreCorrectionImpl(), FlexibleLeaderboardImpl leaderboard = new FlexibleLeaderboardImpl(leaderboardName, new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null); new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
Competitor competitor = createCompetitor(); Competitor competitor = createCompetitor();
TrackedRace raceWithOneCompetitor = new MockedTrackedRaceWithFixedRank(competitor, /* rank */ 1, /* started */ true); TrackedRace raceWithOneCompetitor = new MockedTrackedRaceWithFixedRank(competitor, /* rank */ 1, /* started */ true);
leaderboard.addRace(raceWithOneCompetitor, raceColumnName, /* medalRace */ false, leaderboard.getFleet(null)); leaderboard.addRace(raceWithOneCompetitor, raceColumnName, /* medalRace */ false, leaderboard.getFleet(null));
@@ -169,8 +170,8 @@ public class TestStoringAndRetrievingLeaderboards extends AbstractMongoDBTest {
// attach tracked race to leaderboard to ensure that competitor object is assigned properly // attach tracked race to leaderboard to ensure that competitor object is assigned properly
loadedLeaderboard.addRace(raceWithOneCompetitor, raceColumnName, /* medalRace, ignored */ false, leaderboard.getFleet(null)); loadedLeaderboard.addRace(raceWithOneCompetitor, raceColumnName, /* medalRace, ignored */ false, leaderboard.getFleet(null));
assertEquals(leaderboardName, loadedLeaderboard.getName()); assertEquals(leaderboardName, loadedLeaderboard.getName());
assertTrue(Arrays.equals(discardIndexResultsStartingWithHowManyRaces, loadedLeaderboard.getResultDiscardingRule() assertTrue(Arrays.equals(discardIndexResultsStartingWithHowManyRaces,
.getDiscardIndexResultsStartingWithHowManyRaces())); ((ThresholdBasedResultDiscardingRule) loadedLeaderboard.getResultDiscardingRule()).getDiscardIndexResultsStartingWithHowManyRaces()));
assertEquals(1, Util.size(loadedLeaderboard.getCompetitors())); assertEquals(1, Util.size(loadedLeaderboard.getCompetitors()));
assertEquals(competitor, loadedLeaderboard.getCompetitors().iterator().next()); assertEquals(competitor, loadedLeaderboard.getCompetitors().iterator().next());
assertEquals(carriedPointsForWolfgangHunger, loadedLeaderboard.getCarriedPoints(competitor), 0.000000001); assertEquals(carriedPointsForWolfgangHunger, loadedLeaderboard.getCarriedPoints(competitor), 0.000000001);
@@ -184,7 +185,7 @@ public class TestStoringAndRetrievingLeaderboards extends AbstractMongoDBTest {
final double correctedPoints = 2.75; final double correctedPoints = 2.75;
final int[] discardIndexResultsStartingWithHowManyRaces = new int[] { 5, 8 }; final int[] discardIndexResultsStartingWithHowManyRaces = new int[] { 5, 8 };
FlexibleLeaderboardImpl leaderboard = new FlexibleLeaderboardImpl(leaderboardName, new ScoreCorrectionImpl(), FlexibleLeaderboardImpl leaderboard = new FlexibleLeaderboardImpl(leaderboardName, new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null); new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
Competitor competitor = createCompetitor(); Competitor competitor = createCompetitor();
TrackedRace raceWithOneCompetitor1 = new MockedTrackedRaceWithFixedRank(competitor, /* rank */ 1, /* started */ true); TrackedRace raceWithOneCompetitor1 = new MockedTrackedRaceWithFixedRank(competitor, /* rank */ 1, /* started */ true);
TrackedRace raceWithOneCompetitor2 = new MockedTrackedRaceWithFixedRank(competitor, /* rank */ 2, /* started */ true); TrackedRace raceWithOneCompetitor2 = new MockedTrackedRaceWithFixedRank(competitor, /* rank */ 2, /* started */ true);
@@ -212,7 +213,7 @@ public class TestStoringAndRetrievingLeaderboards extends AbstractMongoDBTest {
final double correctedPoints = 2.75; final double correctedPoints = 2.75;
final int[] discardIndexResultsStartingWithHowManyRaces = new int[] { 5, 8 }; final int[] discardIndexResultsStartingWithHowManyRaces = new int[] { 5, 8 };
FlexibleLeaderboardImpl leaderboard = new FlexibleLeaderboardImpl(leaderboardName, new ScoreCorrectionImpl(), FlexibleLeaderboardImpl leaderboard = new FlexibleLeaderboardImpl(leaderboardName, new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null); new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
Competitor competitor = createCompetitor(); Competitor competitor = createCompetitor();
TrackedRace raceWithOneCompetitor1 = new MockedTrackedRaceWithFixedRank(competitor, /* rank */ 1, /* started */ true); TrackedRace raceWithOneCompetitor1 = new MockedTrackedRaceWithFixedRank(competitor, /* rank */ 1, /* started */ true);
TrackedRace raceWithOneCompetitor2 = new MockedTrackedRaceWithFixedRank(competitor, /* rank */ 2, /* started */ true); TrackedRace raceWithOneCompetitor2 = new MockedTrackedRaceWithFixedRank(competitor, /* rank */ 2, /* started */ true);
@@ -238,7 +239,7 @@ public class TestStoringAndRetrievingLeaderboards extends AbstractMongoDBTest {
final MaxPointsReason maxPointsReason = MaxPointsReason.DNF; final MaxPointsReason maxPointsReason = MaxPointsReason.DNF;
final int[] discardIndexResultsStartingWithHowManyRaces = new int[] { 5, 8 }; final int[] discardIndexResultsStartingWithHowManyRaces = new int[] { 5, 8 };
FlexibleLeaderboardImpl leaderboard = new FlexibleLeaderboardImpl(leaderboardName, new ScoreCorrectionImpl(), FlexibleLeaderboardImpl leaderboard = new FlexibleLeaderboardImpl(leaderboardName, new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null); new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
Competitor competitor = createCompetitor(); Competitor competitor = createCompetitor();
TrackedRace raceWithOneCompetitor1 = new MockedTrackedRaceWithFixedRank(competitor, /* rank */ 1, /* started */ true); TrackedRace raceWithOneCompetitor1 = new MockedTrackedRaceWithFixedRank(competitor, /* rank */ 1, /* started */ true);
TrackedRace raceWithOneCompetitor2 = new MockedTrackedRaceWithFixedRank(competitor, /* rank */ 2, /* started */ true); TrackedRace raceWithOneCompetitor2 = new MockedTrackedRaceWithFixedRank(competitor, /* rank */ 2, /* started */ true);
@@ -267,7 +268,7 @@ public class TestStoringAndRetrievingLeaderboards extends AbstractMongoDBTest {
final MaxPointsReason maxPointsReason = MaxPointsReason.DNF; final MaxPointsReason maxPointsReason = MaxPointsReason.DNF;
final int[] discardIndexResultsStartingWithHowManyRaces = new int[] { 5, 8 }; final int[] discardIndexResultsStartingWithHowManyRaces = new int[] { 5, 8 };
FlexibleLeaderboardImpl leaderboard = new FlexibleLeaderboardImpl(leaderboardName, new ScoreCorrectionImpl(), FlexibleLeaderboardImpl leaderboard = new FlexibleLeaderboardImpl(leaderboardName, new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null); new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
Competitor competitor = createCompetitor(); Competitor competitor = createCompetitor();
TrackedRace raceWithOneCompetitor1 = new MockedTrackedRaceWithFixedRank(competitor, /* rank */ 1, /* started */ true); TrackedRace raceWithOneCompetitor1 = new MockedTrackedRaceWithFixedRank(competitor, /* rank */ 1, /* started */ true);
TrackedRace raceWithOneCompetitor2 = new MockedTrackedRaceWithFixedRank(competitor, /* rank */ 2, /* started */ true); TrackedRace raceWithOneCompetitor2 = new MockedTrackedRaceWithFixedRank(competitor, /* rank */ 2, /* started */ true);
@@ -28,7 +28,7 @@ import com.sap.sailing.domain.leaderboard.FlexibleLeaderboard;
import com.sap.sailing.domain.leaderboard.Leaderboard; import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.leaderboard.impl.FlexibleLeaderboardImpl; import com.sap.sailing.domain.leaderboard.impl.FlexibleLeaderboardImpl;
import com.sap.sailing.domain.leaderboard.impl.LowPoint; import com.sap.sailing.domain.leaderboard.impl.LowPoint;
import com.sap.sailing.domain.leaderboard.impl.ResultDiscardingRuleImpl; import com.sap.sailing.domain.leaderboard.impl.ThresholdBasedResultDiscardingRuleImpl;
import com.sap.sailing.domain.leaderboard.impl.ScoreCorrectionImpl; import com.sap.sailing.domain.leaderboard.impl.ScoreCorrectionImpl;
import com.sap.sailing.domain.persistence.MongoFactory; import com.sap.sailing.domain.persistence.MongoFactory;
import com.sap.sailing.domain.persistence.MongoRaceLogStoreFactory; import com.sap.sailing.domain.persistence.MongoRaceLogStoreFactory;
@@ -68,7 +68,7 @@ public class TestStoringAndRetrievingRaceLogInLeaderboards extends RaceLogMongoD
RaceLogStore raceLogStore = MongoRaceLogStoreFactory.INSTANCE.getMongoRaceLogStore(mongoObjectFactory, domainObjectFactory); RaceLogStore raceLogStore = MongoRaceLogStoreFactory.INSTANCE.getMongoRaceLogStore(mongoObjectFactory, domainObjectFactory);
leaderboard = new FlexibleLeaderboardImpl(raceLogStore, leaderboardName, new ScoreCorrectionImpl(), leaderboard = new FlexibleLeaderboardImpl(raceLogStore, leaderboardName, new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null); new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
Fleet defaultFleet = leaderboard.getFleet(null); Fleet defaultFleet = leaderboard.getFleet(null);
leaderboard.addRaceColumn(raceColumnName, /* medalRace */ false, defaultFleet); leaderboard.addRaceColumn(raceColumnName, /* medalRace */ false, defaultFleet);
@@ -1,5 +1,7 @@
package com.sap.sailing.server.gateway.deserialization.racelog.impl; package com.sap.sailing.server.gateway.deserialization.racelog.impl;
import java.io.Serializable;
import org.json.simple.JSONArray; import org.json.simple.JSONArray;
import org.json.simple.JSONObject; import org.json.simple.JSONObject;
@@ -20,7 +22,8 @@ public class RaceLogDeserializer implements JsonDeserializer<RaceLog> {
} }
public RaceLog deserialize(JSONObject object) throws JsonDeserializationException { public RaceLog deserialize(JSONObject object) throws JsonDeserializationException {
RaceLog result = new RaceLogImpl(); Serializable id = (String)object.get(RaceLogSerializer.FIELD_RACELOG_IDENTIFIER);
RaceLog result = new RaceLogImpl(id);
if (object.get(RaceLogSerializer.FIELD_EVENTS) == null) { if (object.get(RaceLogSerializer.FIELD_EVENTS) == null) {
return result; return result;
@@ -10,6 +10,7 @@ import com.sap.sailing.server.gateway.serialization.JsonSerializer;
public class RaceLogSerializer implements JsonSerializer<RaceLog> { public class RaceLogSerializer implements JsonSerializer<RaceLog> {
public static final String FIELD_EVENTS = "events"; public static final String FIELD_EVENTS = "events";
public static final String FIELD_RACELOG_IDENTIFIER = "racelog_id";
private final JsonSerializer<RaceLogEvent> itemSerializer; private final JsonSerializer<RaceLogEvent> itemSerializer;
@@ -27,6 +28,7 @@ public class RaceLogSerializer implements JsonSerializer<RaceLog> {
events.add(itemSerializer.serialize(event)); events.add(itemSerializer.serialize(event));
} }
result.put(FIELD_EVENTS, events); result.put(FIELD_EVENTS, events);
result.put(FIELD_RACELOG_IDENTIFIER, object.getId());
} finally { } finally {
object.unlockAfterRead(); object.unlockAfterRead();
} }
@@ -38,6 +38,8 @@ public class EventRaceStatesJsonGetServlet extends AbstractJsonHttpServlet {
private final static Logger logger = Logger.getLogger(EventRaceStatesJsonGetServlet.class.getName()); private final static Logger logger = Logger.getLogger(EventRaceStatesJsonGetServlet.class.getName());
private static final String PARAM_NAME_EVENTID = "eventId"; private static final String PARAM_NAME_EVENTID = "eventId";
// private static final String PARAM_NAME_FILTER_BY_REGATTA = "filterByRegatta";
private static final String PARAM_NAME_FILTER_BY_LEADERBOARD = "filterByLeaderboard";
private static final String PARAM_NAME_FILTER_BY_COURSEAREA = "filterByCourseArea"; private static final String PARAM_NAME_FILTER_BY_COURSEAREA = "filterByCourseArea";
private static final String PARAM_NAME_FILTER_BY_DAYOFFSET = "filterByDayOffset"; private static final String PARAM_NAME_FILTER_BY_DAYOFFSET = "filterByDayOffset";
@@ -48,7 +50,8 @@ public class EventRaceStatesJsonGetServlet extends AbstractJsonHttpServlet {
@Override @Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String eventIdParam = request.getParameter(PARAM_NAME_EVENTID); String eventIdParam = request.getParameter(PARAM_NAME_EVENTID);
String courseAreaFilterParam = request.getParameter(PARAM_NAME_FILTER_BY_COURSEAREA); String filterByCourseAreaParam = request.getParameter(PARAM_NAME_FILTER_BY_COURSEAREA);
String filterByLeaderboardParam = request.getParameter(PARAM_NAME_FILTER_BY_LEADERBOARD);
String filterByDayOffsetParam = request.getParameter(PARAM_NAME_FILTER_BY_DAYOFFSET); String filterByDayOffsetParam = request.getParameter(PARAM_NAME_FILTER_BY_DAYOFFSET);
boolean filterByDayOffset = false; boolean filterByDayOffset = false;
Calendar dayToCheck = Calendar.getInstance(); Calendar dayToCheck = Calendar.getInstance();
@@ -76,17 +79,21 @@ public class EventRaceStatesJsonGetServlet extends AbstractJsonHttpServlet {
JSONArray raceStatesLogEntriesJson = new JSONArray(); JSONArray raceStatesLogEntriesJson = new JSONArray();
result.put(FIELD_RACE_STATES, raceStatesLogEntriesJson); result.put(FIELD_RACE_STATES, raceStatesLogEntriesJson);
for (CourseArea courseArea : event.getVenue().getCourseAreas()) { for (CourseArea courseArea : event.getVenue().getCourseAreas()) {
if(courseAreaFilterParam == null || courseArea.getName().equals(courseAreaFilterParam)) { if(filterByCourseAreaParam == null || courseArea.getName().equals(filterByCourseAreaParam)) {
for (Leaderboard leaderboard : getService().getLeaderboards().values()) { for (Leaderboard leaderboard : getService().getLeaderboards().values()) {
if (leaderboard.getDefaultCourseArea() != null && leaderboard.getDefaultCourseArea().equals(courseArea)) { if (filterByLeaderboardParam == null || leaderboard.getName().equals(filterByLeaderboardParam)) {
String regattaDisplayName = leaderboard.getDisplayName(); if (leaderboard.getDefaultCourseArea() != null && leaderboard.getDefaultCourseArea().equals(courseArea)) {
for (RaceColumn raceColumn : leaderboard.getRaceColumns()) { String leaderboardName = leaderboard.getName();
for (Fleet fleet : raceColumn.getFleets()) { String leaderboardDisplayName = leaderboard.getDisplayName();
if(!filterByDayOffset || isRaceStateOfSameDay(raceColumn, fleet, dayToCheck)) { for (RaceColumn raceColumn : leaderboard.getRaceColumns()) {
JSONObject raceStateJson = createRaceStateJsonObject(raceColumn, fleet); for (Fleet fleet : raceColumn.getFleets()) {
raceStateJson.put("courseAreaName", courseArea.getName()); if(!filterByDayOffset || isRaceStateOfSameDay(raceColumn, fleet, dayToCheck)) {
raceStateJson.put("regattaName", regattaDisplayName); JSONObject raceStateJson = createRaceStateJsonObject(raceColumn, fleet);
raceStatesLogEntriesJson.add(raceStateJson); raceStateJson.put("courseAreaName", courseArea.getName());
raceStateJson.put("leaderboardName", leaderboardName);
raceStateJson.put("leaderboardDisplayName", leaderboardDisplayName);
raceStatesLogEntriesJson.add(raceStateJson);
}
} }
} }
} }
@@ -25,6 +25,7 @@ import com.sap.sailing.domain.common.LeaderboardNameConstants;
import com.sap.sailing.domain.common.RegattaName; import com.sap.sailing.domain.common.RegattaName;
import com.sap.sailing.domain.common.impl.Util; import com.sap.sailing.domain.common.impl.Util;
import com.sap.sailing.domain.leaderboard.Leaderboard; import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule;
import com.sap.sailing.domain.leaderboard.impl.LowPoint; import com.sap.sailing.domain.leaderboard.impl.LowPoint;
import com.sap.sailing.server.operationaltransformation.AddDefaultRegatta; import com.sap.sailing.server.operationaltransformation.AddDefaultRegatta;
import com.sap.sailing.server.operationaltransformation.AddRaceDefinition; import com.sap.sailing.server.operationaltransformation.AddRaceDefinition;
@@ -55,8 +56,8 @@ public class ServerReplicationTest extends AbstractServerReplicationTest {
Thread.sleep(1000); // wait 1s for JMS to deliver the message and the message to be applied Thread.sleep(1000); // wait 1s for JMS to deliver the message and the message to be applied
final Leaderboard replicaLeaderboard = replica.getLeaderboardByName(leaderboardName); final Leaderboard replicaLeaderboard = replica.getLeaderboardByName(leaderboardName);
assertNotNull(replicaLeaderboard); assertNotNull(replicaLeaderboard);
assertTrue(Arrays.equals(masterLeaderboard.getResultDiscardingRule().getDiscardIndexResultsStartingWithHowManyRaces(), assertTrue(Arrays.equals(((ThresholdBasedResultDiscardingRule) masterLeaderboard.getResultDiscardingRule()).getDiscardIndexResultsStartingWithHowManyRaces(),
replicaLeaderboard.getResultDiscardingRule().getDiscardIndexResultsStartingWithHowManyRaces())); ((ThresholdBasedResultDiscardingRule) replicaLeaderboard.getResultDiscardingRule()).getDiscardIndexResultsStartingWithHowManyRaces()));
} }
@Test @Test
@@ -20,9 +20,10 @@ import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.common.impl.Util; import com.sap.sailing.domain.common.impl.Util;
import com.sap.sailing.domain.leaderboard.FlexibleLeaderboard; import com.sap.sailing.domain.leaderboard.FlexibleLeaderboard;
import com.sap.sailing.domain.leaderboard.Leaderboard; import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule;
import com.sap.sailing.domain.leaderboard.impl.HighPoint; import com.sap.sailing.domain.leaderboard.impl.HighPoint;
import com.sap.sailing.domain.leaderboard.impl.LowPoint; import com.sap.sailing.domain.leaderboard.impl.LowPoint;
import com.sap.sailing.domain.leaderboard.impl.ResultDiscardingRuleImpl; import com.sap.sailing.domain.leaderboard.impl.ThresholdBasedResultDiscardingRuleImpl;
import com.sap.sailing.domain.test.AbstractLeaderboardTest; import com.sap.sailing.domain.test.AbstractLeaderboardTest;
import com.sap.sailing.domain.test.mock.MockedTrackedRaceWithFixedRank; import com.sap.sailing.domain.test.mock.MockedTrackedRaceWithFixedRank;
import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.domain.tracking.TrackedRace;
@@ -47,11 +48,11 @@ public class LeaderboardDiscardingRulesTest {
FlexibleLeaderboard leaderboard = (FlexibleLeaderboard) racingEventService.getLeaderboardByName(LEADERBOARDNAME); FlexibleLeaderboard leaderboard = (FlexibleLeaderboard) racingEventService.getLeaderboardByName(LEADERBOARDNAME);
assertNotNull(leaderboard); assertNotNull(leaderboard);
int[] discardingRulesNew = new int[] { 1, 5 }; int[] discardingRulesNew = new int[] { 1, 5 };
leaderboard.setResultDiscardingRule(new ResultDiscardingRuleImpl(discardingRulesNew)); leaderboard.setCrossLeaderboardResultDiscardingRule(new ThresholdBasedResultDiscardingRuleImpl(discardingRulesNew));
racingEventService.updateStoredLeaderboard(leaderboard); racingEventService.updateStoredLeaderboard(leaderboard);
Leaderboard leaderboardNew = racingEventService.getLeaderboardByName(LEADERBOARDNAME); Leaderboard leaderboardNew = racingEventService.getLeaderboardByName(LEADERBOARDNAME);
assertNotNull(leaderboardNew); assertNotNull(leaderboardNew);
int[] result = leaderboardNew.getResultDiscardingRule().getDiscardIndexResultsStartingWithHowManyRaces(); int[] result = ((ThresholdBasedResultDiscardingRule) leaderboardNew.getResultDiscardingRule()).getDiscardIndexResultsStartingWithHowManyRaces();
assertArrayEquals(discardingRulesNew, result); assertArrayEquals(discardingRulesNew, result);
} }
@@ -4,7 +4,7 @@ import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Fleet; import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.RaceColumn; import com.sap.sailing.domain.base.RaceColumn;
import com.sap.sailing.domain.base.RaceColumnListener; import com.sap.sailing.domain.base.RaceColumnListener;
import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule; import com.sap.sailing.domain.leaderboard.ResultDiscardingRule;
import com.sap.sailing.domain.racelog.RaceLogEvent; import com.sap.sailing.domain.racelog.RaceLogEvent;
import com.sap.sailing.domain.racelog.RaceLogIdentifier; import com.sap.sailing.domain.racelog.RaceLogIdentifier;
import com.sap.sailing.domain.racelog.RaceLogIdentifierTemplateResolver; import com.sap.sailing.domain.racelog.RaceLogIdentifierTemplateResolver;
@@ -68,6 +68,10 @@ public class RaceLogReplicator implements RaceColumnListener {
public void isMedalRaceChanged(RaceColumn raceColumn, boolean newIsMedalRace) { public void isMedalRaceChanged(RaceColumn raceColumn, boolean newIsMedalRace) {
} }
@Override
public void isStartsWithZeroScoreChanged(RaceColumn raceColumn, boolean newIsStartsWithZeroScore) {
}
@Override @Override
public boolean canAddRaceColumnToContainer(RaceColumn raceColumn) { public boolean canAddRaceColumnToContainer(RaceColumn raceColumn) {
return true; return true;
@@ -94,8 +98,7 @@ public class RaceLogReplicator implements RaceColumnListener {
} }
@Override @Override
public void resultDiscardingRuleChanged(ThresholdBasedResultDiscardingRule oldDiscardingRule, public void resultDiscardingRuleChanged(ResultDiscardingRule oldDiscardingRule, ResultDiscardingRule newDiscardingRule) {
ThresholdBasedResultDiscardingRule newDiscardingRule) {
} }
@@ -14,7 +14,7 @@ import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.common.impl.Util; import com.sap.sailing.domain.common.impl.Util;
import com.sap.sailing.domain.common.impl.Util.Triple; import com.sap.sailing.domain.common.impl.Util.Triple;
import com.sap.sailing.domain.leaderboard.Leaderboard; import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule; import com.sap.sailing.domain.leaderboard.ResultDiscardingRule;
import com.sap.sailing.domain.racelog.RaceLog; import com.sap.sailing.domain.racelog.RaceLog;
import com.sap.sailing.domain.racelog.RaceLogEvent; import com.sap.sailing.domain.racelog.RaceLogEvent;
import com.sap.sailing.domain.racelog.RaceLogFinishPositioningConfirmedEvent; import com.sap.sailing.domain.racelog.RaceLogFinishPositioningConfirmedEvent;
@@ -50,6 +50,10 @@ public class RaceLogScoringReplicator implements RaceColumnListener {
public void isMedalRaceChanged(RaceColumn raceColumn, boolean newIsMedalRace) { public void isMedalRaceChanged(RaceColumn raceColumn, boolean newIsMedalRace) {
} }
@Override
public void isStartsWithZeroScoreChanged(RaceColumn raceColumn, boolean newIsStartsWithZeroScore) {
}
@Override @Override
public boolean canAddRaceColumnToContainer(RaceColumn raceColumn) { public boolean canAddRaceColumnToContainer(RaceColumn raceColumn) {
return true; return true;
@@ -76,8 +80,7 @@ public class RaceLogScoringReplicator implements RaceColumnListener {
} }
@Override @Override
public void resultDiscardingRuleChanged(ThresholdBasedResultDiscardingRule oldDiscardingRule, public void resultDiscardingRuleChanged(ResultDiscardingRule oldDiscardingRule, ResultDiscardingRule newDiscardingRule) {
ThresholdBasedResultDiscardingRule newDiscardingRule) {
} }
@Override @Override
@@ -69,7 +69,7 @@ import com.sap.sailing.domain.leaderboard.ScoringScheme;
import com.sap.sailing.domain.leaderboard.impl.FlexibleLeaderboardImpl; import com.sap.sailing.domain.leaderboard.impl.FlexibleLeaderboardImpl;
import com.sap.sailing.domain.leaderboard.impl.LeaderboardGroupImpl; import com.sap.sailing.domain.leaderboard.impl.LeaderboardGroupImpl;
import com.sap.sailing.domain.leaderboard.impl.RegattaLeaderboardImpl; import com.sap.sailing.domain.leaderboard.impl.RegattaLeaderboardImpl;
import com.sap.sailing.domain.leaderboard.impl.ResultDiscardingRuleImpl; import com.sap.sailing.domain.leaderboard.impl.ThresholdBasedResultDiscardingRuleImpl;
import com.sap.sailing.domain.leaderboard.impl.ScoreCorrectionImpl; import com.sap.sailing.domain.leaderboard.impl.ScoreCorrectionImpl;
import com.sap.sailing.domain.leaderboard.meta.LeaderboardGroupMetaLeaderboard; import com.sap.sailing.domain.leaderboard.meta.LeaderboardGroupMetaLeaderboard;
import com.sap.sailing.domain.persistence.DomainObjectFactory; import com.sap.sailing.domain.persistence.DomainObjectFactory;
@@ -366,7 +366,7 @@ public class RacingEventServiceImpl implements RacingEventService, RegattaListen
mongoObjectFactory, mongoObjectFactory,
domainObjectFactory); domainObjectFactory);
CourseArea courseArea = getCourseArea(courseAreaId); CourseArea courseArea = getCourseArea(courseAreaId);
FlexibleLeaderboard result = new FlexibleLeaderboardImpl(raceLogStore, leaderboardName, new ScoreCorrectionImpl(), new ResultDiscardingRuleImpl( FlexibleLeaderboard result = new FlexibleLeaderboardImpl(raceLogStore, leaderboardName, new ScoreCorrectionImpl(), new ThresholdBasedResultDiscardingRuleImpl(
discardThresholds), scoringScheme, courseArea); discardThresholds), scoringScheme, courseArea);
result.setDisplayName(leaderboardDisplayName); result.setDisplayName(leaderboardDisplayName);
synchronized (leaderboardsByName) { synchronized (leaderboardsByName) {
@@ -398,7 +398,7 @@ public class RacingEventServiceImpl implements RacingEventService, RegattaListen
+ (regatta == null ? "null" : (regatta.getName() + " (" + regatta.hashCode() + ")")) + " to " + this); + (regatta == null ? "null" : (regatta.getName() + " (" + regatta.hashCode() + ")")) + " to " + this);
RegattaLeaderboard result = null; RegattaLeaderboard result = null;
if (regatta != null) { if (regatta != null) {
result = new RegattaLeaderboardImpl(regatta, new ScoreCorrectionImpl(), new ResultDiscardingRuleImpl( result = new RegattaLeaderboardImpl(regatta, new ScoreCorrectionImpl(), new ThresholdBasedResultDiscardingRuleImpl(
discardThresholds)); discardThresholds));
result.setDisplayName(leaderboardDisplayName); result.setDisplayName(leaderboardDisplayName);
synchronized (leaderboardsByName) { synchronized (leaderboardsByName) {
@@ -860,15 +860,19 @@ public class RacingEventServiceImpl implements RacingEventService, RegattaListen
} }
} }
private Map<String, Pair<List<Triple<String, Integer, Color>>, Boolean>> getSeriesWithoutRaceColumnsConstructionParametersAsMap(Regatta regatta) { private Map<String, Triple<List<Triple<String, Integer, Color>>, Pair<Boolean, Boolean>, int[]>> getSeriesWithoutRaceColumnsConstructionParametersAsMap(
Map<String, Pair<List<Triple<String, Integer, Color>>, Boolean>> result = new HashMap<String, Pair<List<Triple<String, Integer, Color>>, Boolean>>(); Regatta regatta) {
Map<String, Triple<List<Triple<String, Integer, Color>>, Pair<Boolean, Boolean>, int[]>> result =
new HashMap<String, Triple<List<Triple<String, Integer, Color>>, Pair<Boolean, Boolean>, int[]>>();
for (Series s : regatta.getSeries()) { for (Series s : regatta.getSeries()) {
assert Util.isEmpty(s.getRaceColumns()); assert Util.isEmpty(s.getRaceColumns());
List<Triple<String, Integer, Color>> fleetNamesAndOrdering = new ArrayList<Triple<String, Integer, Color>>(); List<Triple<String, Integer, Color>> fleetNamesAndOrdering = new ArrayList<Triple<String, Integer, Color>>();
for (Fleet f : s.getFleets()) { for (Fleet f : s.getFleets()) {
fleetNamesAndOrdering.add(new Triple<String, Integer, Color>(f.getName(), f.getOrdering(), f.getColor())); fleetNamesAndOrdering.add(new Triple<String, Integer, Color>(f.getName(), f.getOrdering(), f.getColor()));
} }
result.put(s.getName(), new Pair<List<Triple<String, Integer, Color>>, Boolean>(fleetNamesAndOrdering, s.isMedal())); result.put(s.getName(), new Triple<List<Triple<String, Integer, Color>>, Pair<Boolean, Boolean>, int[]>(fleetNamesAndOrdering,
new Pair<Boolean, Boolean>(s.isMedal(), s.isStartsWithZeroScore()),
s.getResultDiscardingRule() == null ? null : s.getResultDiscardingRule().getDiscardIndexResultsStartingWithHowManyRaces()));
} }
return result; return result;
} }
@@ -1549,7 +1553,7 @@ public class RacingEventServiceImpl implements RacingEventService, RegattaListen
removeLeaderboard(overallLeaderboard.getName()); removeLeaderboard(overallLeaderboard.getName());
} else { } else {
// update existing overall leaderboard's discards settings; scoring scheme cannot be updated in-place // update existing overall leaderboard's discards settings; scoring scheme cannot be updated in-place
overallLeaderboard.setResultDiscardingRule(new ResultDiscardingRuleImpl(overallLeaderboardDiscardThresholds)); overallLeaderboard.setCrossLeaderboardResultDiscardingRule(new ThresholdBasedResultDiscardingRuleImpl(overallLeaderboardDiscardThresholds));
updateStoredLeaderboard(overallLeaderboard); updateStoredLeaderboard(overallLeaderboard);
} }
} else if (overallLeaderboard == null && overallLeaderboardScoringSchemeType != null) { } else if (overallLeaderboard == null && overallLeaderboardScoringSchemeType != null) {
@@ -1563,7 +1567,7 @@ public class RacingEventServiceImpl implements RacingEventService, RegattaListen
private void addOverallLeaderboardToLeaderboardGroup(LeaderboardGroup leaderboardGroup, private void addOverallLeaderboardToLeaderboardGroup(LeaderboardGroup leaderboardGroup,
ScoringScheme scoringScheme, int[] discardThresholds) { ScoringScheme scoringScheme, int[] discardThresholds) {
Leaderboard overallLeaderboard = new LeaderboardGroupMetaLeaderboard(leaderboardGroup, scoringScheme, Leaderboard overallLeaderboard = new LeaderboardGroupMetaLeaderboard(leaderboardGroup, scoringScheme,
new ResultDiscardingRuleImpl(discardThresholds)); new ThresholdBasedResultDiscardingRuleImpl(discardThresholds));
leaderboardGroup.setOverallLeaderboard(overallLeaderboard); leaderboardGroup.setOverallLeaderboard(overallLeaderboard);
addLeaderboard(overallLeaderboard); addLeaderboard(overallLeaderboard);
updateStoredLeaderboard(overallLeaderboard); updateStoredLeaderboard(overallLeaderboard);
@@ -1,15 +1,9 @@
package com.sap.sailing.server.operationaltransformation; package com.sap.sailing.server.operationaltransformation;
import java.util.logging.Logger;
import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.base.Series;
import com.sap.sailing.domain.common.RegattaIdentifier; import com.sap.sailing.domain.common.RegattaIdentifier;
import com.sap.sailing.server.RacingEventService;
public abstract class AbstractColumnInSeriesOperation<ResultType> extends AbstractSeriesOperation<ResultType> { public abstract class AbstractColumnInSeriesOperation<ResultType> extends AbstractSeriesOperation<ResultType> {
private static final long serialVersionUID = -8985474505327170991L; private static final long serialVersionUID = -8985474505327170991L;
private static final Logger logger = Logger.getLogger(AbstractColumnInSeriesOperation.class.getName());
private final String columnName; private final String columnName;
public AbstractColumnInSeriesOperation(RegattaIdentifier regattaIdentifier, String seriesName, String columnName) { public AbstractColumnInSeriesOperation(RegattaIdentifier regattaIdentifier, String seriesName, String columnName) {
@@ -20,18 +14,4 @@ public abstract class AbstractColumnInSeriesOperation<ResultType> extends Abstra
protected String getColumnName() { protected String getColumnName() {
return columnName; return columnName;
} }
protected Series getSeries(RacingEventService toState) {
Series series = null;
Regatta regatta = toState.getRegatta(getRegattaIdentifier());
if (regatta != null) {
series = regatta.getSeriesByName(getSeriesName());
if (series == null) {
logger.warning("series "+getSeriesName()+" not found in regatta "+regatta.getName());
}
} else {
logger.warning("regatta "+getRegattaIdentifier()+" not found");
}
return series;
}
} }
@@ -1,9 +1,15 @@
package com.sap.sailing.server.operationaltransformation; package com.sap.sailing.server.operationaltransformation;
import java.util.logging.Logger;
import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.base.Series;
import com.sap.sailing.domain.common.RegattaIdentifier; import com.sap.sailing.domain.common.RegattaIdentifier;
import com.sap.sailing.server.RacingEventService;
public abstract class AbstractSeriesOperation<ResultType> extends AbstractRacingEventServiceOperation<ResultType> { public abstract class AbstractSeriesOperation<ResultType> extends AbstractRacingEventServiceOperation<ResultType> {
private static final long serialVersionUID = -7786133902346491275L; private static final long serialVersionUID = -7786133902346491275L;
private Logger logger = Logger.getLogger(AbstractSeriesOperation.class.getName());
private final RegattaIdentifier regattaIdentifier; private final RegattaIdentifier regattaIdentifier;
private final String seriesName; private final String seriesName;
@@ -21,4 +27,17 @@ public abstract class AbstractSeriesOperation<ResultType> extends AbstractRacing
return seriesName; return seriesName;
} }
protected Series getSeries(RacingEventService toState) {
Series series = null;
Regatta regatta = toState.getRegatta(getRegattaIdentifier());
if (regatta != null) {
series = regatta.getSeriesByName(getSeriesName());
if (series == null) {
logger.warning("series "+getSeriesName()+" not found in regatta "+regatta.getName());
}
} else {
logger.warning("regatta "+getRegattaIdentifier()+" not found");
}
return series;
}
} }
@@ -19,7 +19,7 @@ public class AddColumnToSeries extends AbstractColumnInSeriesOperation<RaceColum
Series series = getSeries(toState); Series series = getSeries(toState);
if (series != null) { if (series != null) {
result = series.addRaceColumn(getColumnName(), toState); result = series.addRaceColumn(getColumnName(), toState);
if(result != null && series.getRegatta().isPersistent()) { if (result != null && series.getRegatta().isPersistent()) {
toState.updateStoredRegatta(series.getRegatta()); toState.updateStoredRegatta(series.getRegatta());
} }
} }
@@ -15,22 +15,23 @@ import com.sap.sailing.domain.common.Color;
import com.sap.sailing.domain.common.impl.Util.Pair; import com.sap.sailing.domain.common.impl.Util.Pair;
import com.sap.sailing.domain.common.impl.Util.Triple; import com.sap.sailing.domain.common.impl.Util.Triple;
import com.sap.sailing.domain.leaderboard.ScoringScheme; import com.sap.sailing.domain.leaderboard.ScoringScheme;
import com.sap.sailing.domain.leaderboard.impl.ThresholdBasedResultDiscardingRuleImpl;
import com.sap.sailing.domain.tracking.TrackedRegattaRegistry; import com.sap.sailing.domain.tracking.TrackedRegattaRegistry;
import com.sap.sailing.server.RacingEventService; import com.sap.sailing.server.RacingEventService;
import com.sap.sailing.server.RacingEventServiceOperation; import com.sap.sailing.server.RacingEventServiceOperation;
public class AddSpecificRegatta extends AbstractAddRegattaOperation { public class AddSpecificRegatta extends AbstractAddRegattaOperation {
private static final long serialVersionUID = -8018855620167669352L; private static final long serialVersionUID = -8018855620167669352L;
private final Map<String, Pair<List<Triple<String, Integer, Color>>, Boolean>> seriesNamesWithFleetNamesAndFleetOrderingAndMedal; private final Map<String, Triple<List<Triple<String, Integer, Color>>, Pair<Boolean, Boolean>, int[]>> seriesNamesWithFleetNamesAndFleetOrderingAndMedalAndStartsWithZeroScoreAndDiscardingThresholds;
private final boolean persistent; private final boolean persistent;
private final ScoringScheme scoringScheme; private final ScoringScheme scoringScheme;
private final Serializable defaultCourseAreaId; private final Serializable defaultCourseAreaId;
public AddSpecificRegatta(String regattaName, String boatClassName, Serializable id, public AddSpecificRegatta(String regattaName, String boatClassName, Serializable id,
Map<String, Pair<List<Triple<String, Integer, Color>>, Boolean>> seriesNamesWithFleetNamesAndFleetOrdering, Map<String, Triple<List<Triple<String, Integer, Color>>, Pair<Boolean, Boolean>, int[]>> seriesNamesWithFleetNamesAndFleetOrderingAndMedalAndDiscardingThresholds,
boolean persistent, ScoringScheme scoringScheme, Serializable defaultCourseAreaId) { boolean persistent, ScoringScheme scoringScheme, Serializable defaultCourseAreaId) {
super(regattaName, boatClassName, id); super(regattaName, boatClassName, id);
this.seriesNamesWithFleetNamesAndFleetOrderingAndMedal = seriesNamesWithFleetNamesAndFleetOrdering; this.seriesNamesWithFleetNamesAndFleetOrderingAndMedalAndStartsWithZeroScoreAndDiscardingThresholds = seriesNamesWithFleetNamesAndFleetOrderingAndMedalAndDiscardingThresholds;
this.persistent = persistent; this.persistent = persistent;
this.scoringScheme = scoringScheme; this.scoringScheme = scoringScheme;
this.defaultCourseAreaId = defaultCourseAreaId; this.defaultCourseAreaId = defaultCourseAreaId;
@@ -44,10 +45,14 @@ public class AddSpecificRegatta extends AbstractAddRegattaOperation {
private Iterable<? extends Series> createSeries(TrackedRegattaRegistry trackedRegattaRegistry) { private Iterable<? extends Series> createSeries(TrackedRegattaRegistry trackedRegattaRegistry) {
List<Series> result = new ArrayList<Series>(); List<Series> result = new ArrayList<Series>();
for (Map.Entry<String, Pair<List<Triple<String, Integer, Color>>, Boolean>> e : seriesNamesWithFleetNamesAndFleetOrderingAndMedal.entrySet()) { for (Map.Entry<String, Triple<List<Triple<String, Integer, Color>>, Pair<Boolean, Boolean>, int[]>> e : seriesNamesWithFleetNamesAndFleetOrderingAndMedalAndStartsWithZeroScoreAndDiscardingThresholds.entrySet()) {
final List<String> emptyRaceColumnNamesList = Collections.emptyList(); final List<String> emptyRaceColumnNamesList = Collections.emptyList();
Series s = new SeriesImpl(e.getKey(), /* isMedal */e.getValue().getB(), createFleets(e.getValue().getA()), Series s = new SeriesImpl(e.getKey(), /* isMedal */e.getValue().getB().getA(), createFleets(e.getValue().getA()),
emptyRaceColumnNamesList, trackedRegattaRegistry); emptyRaceColumnNamesList, trackedRegattaRegistry);
if (e.getValue().getC() != null) {
s.setResultDiscardingRule(new ThresholdBasedResultDiscardingRuleImpl(e.getValue().getC()));
}
s.setStartsWithZeroScore(e.getValue().getB().getB());
result.add(s); result.add(s);
} }
return result; return result;
@@ -6,7 +6,8 @@ import java.util.Arrays;
import com.sap.sailing.domain.base.CourseArea; import com.sap.sailing.domain.base.CourseArea;
import com.sap.sailing.domain.leaderboard.FlexibleLeaderboard; import com.sap.sailing.domain.leaderboard.FlexibleLeaderboard;
import com.sap.sailing.domain.leaderboard.Leaderboard; import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.leaderboard.impl.ResultDiscardingRuleImpl; import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule;
import com.sap.sailing.domain.leaderboard.impl.ThresholdBasedResultDiscardingRuleImpl;
import com.sap.sailing.server.RacingEventService; import com.sap.sailing.server.RacingEventService;
import com.sap.sailing.server.RacingEventServiceOperation; import com.sap.sailing.server.RacingEventServiceOperation;
@@ -43,8 +44,15 @@ public class UpdateLeaderboard extends AbstractLeaderboardOperation<Leaderboard>
toState.renameLeaderboard(getLeaderboardName(), newLeaderboardName); toState.renameLeaderboard(getLeaderboardName(), newLeaderboardName);
} }
Leaderboard leaderboard = toState.getLeaderboardByName(newLeaderboardName); Leaderboard leaderboard = toState.getLeaderboardByName(newLeaderboardName);
if (!Arrays.equals(leaderboard.getResultDiscardingRule().getDiscardIndexResultsStartingWithHowManyRaces(), newDiscardingThresholds)) { // If the new thresholds are null this means that the leaderboard is expected to obtain its result discarding
leaderboard.setResultDiscardingRule(new ResultDiscardingRuleImpl(newDiscardingThresholds)); // configuration from somewhere else implicitly, e.g., an underlying regatta, and we'll leave it alone;
// Otherwise, a new threshold-based result discarding rule will be set based on the newDiscardingThresholds
// unless the leaderboard already has an equal definition.
if (newDiscardingThresholds != null
&& (!(leaderboard.getResultDiscardingRule() instanceof ThresholdBasedResultDiscardingRule) || !Arrays
.equals(((ThresholdBasedResultDiscardingRule) leaderboard.getResultDiscardingRule())
.getDiscardIndexResultsStartingWithHowManyRaces(), newDiscardingThresholds))) {
leaderboard.setCrossLeaderboardResultDiscardingRule(new ThresholdBasedResultDiscardingRuleImpl(newDiscardingThresholds));
} }
leaderboard.setDisplayName(newLeaderboardDisplayName); leaderboard.setDisplayName(newLeaderboardDisplayName);
@@ -0,0 +1,47 @@
package com.sap.sailing.server.operationaltransformation;
import com.sap.sailing.domain.base.Series;
import com.sap.sailing.domain.common.RegattaIdentifier;
import com.sap.sailing.domain.leaderboard.impl.ThresholdBasedResultDiscardingRuleImpl;
import com.sap.sailing.server.RacingEventService;
import com.sap.sailing.server.RacingEventServiceOperation;
public class UpdateSeries extends AbstractSeriesOperation<Void> {
private static final long serialVersionUID = 6356089749049112710L;
private final boolean isMedal;
private final int[] resultDiscardingThresholds;
private final boolean startsWithZeroScore;
public UpdateSeries(RegattaIdentifier regattaIdentifier, String seriesName, boolean isMedal, int[] resultDiscardingThresholds, boolean startsWithZeroScore) {
super(regattaIdentifier, seriesName);
this.isMedal = isMedal;
this.resultDiscardingThresholds = resultDiscardingThresholds;
this.startsWithZeroScore = startsWithZeroScore;
}
@Override
public Void internalApplyTo(RacingEventService toState) throws Exception {
Series series = getSeries(toState);
series.setIsMedal(isMedal);
series.setResultDiscardingRule(resultDiscardingThresholds == null ?
null : new ThresholdBasedResultDiscardingRuleImpl(resultDiscardingThresholds));
series.setStartsWithZeroScore(startsWithZeroScore);
if (series.getRegatta().isPersistent()) {
toState.updateStoredRegatta(series.getRegatta());
}
return null;
}
@Override
public RacingEventServiceOperation<?> transformClientOp(RacingEventServiceOperation<?> serverOp) {
// TODO Auto-generated method stub
return null;
}
@Override
public RacingEventServiceOperation<?> transformServerOp(RacingEventServiceOperation<?> clientOp) {
// TODO Auto-generated method stub
return null;
}
}
+18
View File
@@ -0,0 +1,18 @@
#!/bin/sh
SERVER_NAME=MASTER
MEMORY="1024m"
REPLICATION_CHANNEL=sapsailinganalytics-master
TELNET_PORT=14888
SERVER_PORT=8888
MONGODB_PORT=27017
EXPEDITION_PORT=2010
if [[ "$JAVA_HOME" == "" ]]; then
JAVA_HOME=$HOME/jdk1.7.0_02
echo "Setting JAVA_HOME to $JAVA_HOME"
fi
+5 -5
View File
@@ -1,11 +1,11 @@
#!/bin/sh #!/bin/sh
export TELNET_PORT=14886 # source variables
export SERVER_NAME=MASTER . `pwd`/env.sh
export JAVA_HOME=/home/trac/jdk1.7.0_02
scriptdir=`dirname "$0"` scriptdir=`dirname "$0"`
LIB_DIR=`pwd`/native-libraries LIB_DIR=`pwd`/native-libraries
echo scriptdir is $scriptdir and LIB_DIR is $LIB_DIR echo Script directory is $scriptdir and LIB_DIR is $LIB_DIR
nohup "$JAVA_HOME/bin/java" -XX:+UseMembar -D$SERVER_NAME -Dcom.sap.sailing.server.name=$SERVER_NAME -Djava.io.tmpdir=`pwd`/tmp -Dfile.encoding=cp1252 -Djetty.home=$scriptdir/configuration/jetty -Djava.util.logging.config.file=$scriptdir/configuration/logging.properties -Djava.library.path=$LIB_DIR -Xmx8192m -jar plugins/org.eclipse.equinox.launcher_1.2.0.v20110502.jar -console $TELNET_PORT -configuration ./ -clean &
nohup "$JAVA_HOME/bin/java" -XX:+UseMembar -D$SERVER_NAME -Dcom.sap.sailing.server.name=$SERVER_NAME -Djava.io.tmpdir=`pwd`/tmp -Dfile.encoding=cp1252 -Djetty.home=$scriptdir/configuration/jetty -Djava.util.logging.config.file=$scriptdir/configuration/logging.properties -Djava.library.path=$LIB_DIR -Xmx$MEMORY -jar plugins/org.eclipse.equinox.launcher_1.2.0.v20110502.jar -console $TELNET_PORT -configuration ./ -clean &
echo "Started $SERVER_NAME on port $TELNET_PORT - use telnet localhost $TELNET_PORT to connect to the OSGi console" echo "Started $SERVER_NAME on port $TELNET_PORT - use telnet localhost $TELNET_PORT to connect to the OSGi console"
+7 -6
View File
@@ -1,13 +1,14 @@
#!/bin/bash #!/bin/bash
OSGI_TELNET_PORT=14886 . `pwd`/env.sh
TELNET_ACTIVE=`netstat -tlnp 2>/dev/null | grep ":$OSGI_TELNET_PORT"`
if [[ $OSGI_TELNET_PORT == "" ]] || [[ $TELNET_ACTIVE == "" ]]; then TELNET_ACTIVE=`netstat -tlnp 2>/dev/null | grep ":$TELNET_PORT"`
echo "Could not find server on telnet port $OSGI_TELNET_PORT. Perhaps server is not running?" if [[ $TELNET_PORT == "" ]] || [[ $TELNET_ACTIVE == "" ]]; then
echo "Could not find server on telnet port $TELNET_PORT. Perhaps server is not running?"
exit exit
fi fi
NC_CMD="nc -t 127.0.0.1 $OSGI_TELNET_PORT" NC_CMD="nc -t 127.0.0.1 $TELNET_PORT"
echo -n exit | $NC_CMD > /dev/null echo -n exit | $NC_CMD > /dev/null
echo "Stopped server MASTER" echo "Stopped server $SERVER_NAME"
@@ -73,7 +73,7 @@ public class OfflineDataManager extends DataManager {
competitors.add(new CompetitorImpl(UUID.randomUUID(), "Realteam", null, null)); competitors.add(new CompetitorImpl(UUID.randomUUID(), "Realteam", null, null));
RaceLogEventFactory factory = new RaceLogEventFactoryImpl(); RaceLogEventFactory factory = new RaceLogEventFactoryImpl();
RaceLog log = new RaceLogImpl(); RaceLog log = new RaceLogImpl(UUID.randomUUID());
log.add(factory.createStartTimeEvent( log.add(factory.createStartTimeEvent(
new MillisecondsTimePoint(new Date().getTime() - 2000), new MillisecondsTimePoint(new Date().getTime() - 2000),
1, 1,
@@ -93,7 +93,7 @@ public class OfflineDataManager extends DataManager {
StartProcedureType.ESS, StartProcedureType.ESS,
log); log);
log = new RaceLogImpl(); log = new RaceLogImpl(UUID.randomUUID());
/*log.add(factory.createStartTimeEvent( /*log.add(factory.createStartTimeEvent(
new MillisecondsTimePoint(new Date()), new MillisecondsTimePoint(new Date()),
1, 1,
@@ -110,7 +110,7 @@ public class OfflineDataManager extends DataManager {
StartProcedureType.ESS, StartProcedureType.ESS,
log); log);
log = new RaceLogImpl(); log = new RaceLogImpl(UUID.randomUUID());
/*log.add(factory.createRaceStatusEvent( /*log.add(factory.createRaceStatusEvent(
new MillisecondsTimePoint(new Date()), new MillisecondsTimePoint(new Date()),
5, 5,
+5
View File
@@ -0,0 +1,5 @@
# Amazon EC2 for SAP
- URL: https://skyesapicl.hana.ondemand.com/skye/
- IO: 135676 - Project Travel Exp. Technology Sponsors.
- Instance: RHEL
+10 -1
View File
@@ -85,4 +85,13 @@ One can find more information about the Jetty architecture here: http://docs.cod
### Ignore GIT line endings during merge ### Ignore GIT line endings during merge
Following http://stackoverflow.com/questions/861995/is-it-possible-for-git-merge-to-ignore-line-ending-differences one can use `git config merge.renormalize true` Following http://stackoverflow.com/questions/861995/is-it-possible-for-git-merge-to-ignore-line-ending-differences one can use `git config merge.renormalize true`
### Remove association between local and remote branch in GIT
To remove the association between the local and remote branch, and delete the local branch, run:
<pre>
git config --unset branch.&lt;branch&gt;.remote
git config --unset branch.&lt;branch&gt;.merge
</pre>
Loaded 100 of 105 files, more files were not shown because too many files have changed in this diff. Show more