Merge remote-tracking branch 'origin/master' into ttcm_with_metadata

This commit is contained in:
fmittag committed 2012-11-12 10:43:16 +01:00
commit 05b81d7646
243 files changed
+11257 -3320

No files matched your search

Binary file not shown.
+11
View File
@@ -0,0 +1,11 @@
Exporting swisstiming 'Store and forward' messages
------------------------------------------------------
27012: local MongoDB "winddb" instance
/opt/mongodb/bin/mongoexport --port 27017 -d winddb -c SWISSTIMING_CONFIGURATIONS > /tmp/swisstiming_configurations.json
/opt/mongodb/bin/mongoexport --port 27017 -d winddb -c LAST_MESSAGE_COUNT > /tmp/swisstiming_last_message_count.json
/opt/mongodb/bin/mongoexport --port 27017 -d winddb -c RACES_MASTERDATA > /tmp/swisstiming_races_masterdata.json
/opt/mongodb/bin/mongoexport --port 27017 -d winddb -c RACES_MESSAGES > /tmp/swisstiming_races_massages.json
/opt/mongodb/bin/mongoexport --port 27017 -d winddb -c COMMAND_MESSAGES > /tmp/swisstiming_command_messages.json
Binary file not shown.
@@ -2,4 +2,5 @@
<!DOCTYPE module PUBLIC "-//Google Inc.//DTD Google Web Toolkit 2.4.0//EN" "http://google-web-toolkit.googlecode.com/svn/tags/2.4.0/distro-source/core/src/gwt-module.dtd">
<module>
<source path='common'/>
<source path='common/impl'/>
</module>
@@ -24,6 +24,8 @@ public enum DetailType implements Serializable {
TACK(0, ASCENDING), JIBE(0, ASCENDING), PENALTY_CIRCLE(0, ASCENDING), MARK_PASSING(0, ASCENDING), CURRENT_LEG(0, ASCENDING),
DISPLAY_LEGS(0, NONE), TIME_TRAVELED(0, ASCENDING),
TOTAL_TIME_SAILED_DOWNWIND_IN_SECONDS(1, ASCENDING),
TOTAL_TIME_SAILED_UPWIND_IN_SECONDS(1, ASCENDING),
TOTAL_TIME_SAILED_REACHING_IN_SECONDS(1, ASCENDING),
MAXIMUM_SPEED_OVER_GROUND_IN_KNOTS(1, DESCENDING),
TOTAL_TIME_SAILED_IN_SECONDS(1, ASCENDING), RACE_CURRENT_SPEED_OVER_GROUND_IN_KNOTS(2, DESCENDING);
@@ -1,4 +1,4 @@
package com.sap.sailing.domain.base.impl;
package com.sap.sailing.domain.common.impl;
import com.sap.sailing.domain.common.AbstractDistance;
import com.sap.sailing.domain.common.Distance;
@@ -39,8 +39,9 @@ public interface MongoObjectFactory {
void renameLeaderboard(String oldName, String newName);
/**
* Stores the group, if it doesn't exist or updates it.<br />
* Leaderboards in the group, which aren't stored in the database, will be stored.
* Stores the group, if it doesn't exist or updates it. Leaderboards in the group, which aren't stored in the
* database, will be stored. If the leaderboard group has an {@link LeaderboardGroup#getOverallLeaderboard() overall
* leaderboard}, it will be stored / updated as well.
*/
void storeLeaderboardGroup(LeaderboardGroup leaderboardGroup);
@@ -198,6 +198,14 @@ public class DelayedLeaderboardCorrectionsImpl implements DelayedLeaderboardCorr
public void raceColumnRemovedFromContainer(RaceColumn raceColumn) {
}
@Override
public void raceColumnMoved(RaceColumn raceColumn, int newIndex) {
}
@Override
public void factorChanged(RaceColumn raceColumn, Double oldFactor, Double newFactor) {
}
@Override
public boolean isTransient() {
return false;
@@ -220,6 +220,7 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
DelayedLeaderboardCorrections loadedLeaderboardCorrections = new DelayedLeaderboardCorrectionsImpl(result);
loadLeaderboardCorrections(dbLeaderboard, loadedLeaderboardCorrections, scoreCorrection);
loadSuppressedCompetitors(dbLeaderboard, loadedLeaderboardCorrections);
loadColumnFactors(dbLeaderboard, result);
// add the leaderboard to the registry
if (leaderboardRegistry != null) {
leaderboardRegistry.addLeaderboard(result);
@@ -230,6 +231,17 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
return result;
}
private void loadColumnFactors(DBObject dbLeaderboard, Leaderboard result) {
DBObject dbColumnFactors = (DBObject) dbLeaderboard.get(FieldNames.LEADERBOARD_COLUMN_FACTORS.name());
if (dbColumnFactors != null) {
for (String encodedRaceColumnName : dbColumnFactors.keySet()) {
double factor = ((Number) dbColumnFactors.get(encodedRaceColumnName)).doubleValue();
String raceColumnName = MongoUtils.unescapeDollarAndDot(encodedRaceColumnName);
result.getRaceColumnByName(raceColumnName).setFactor(factor);
}
}
}
private void loadSuppressedCompetitors(DBObject dbLeaderboard,
DelayedLeaderboardCorrections loadedLeaderboardCorrections) {
BasicDBList dbSuppressedCompetitorNames = (BasicDBList) dbLeaderboard.get(FieldNames.LEADERBOARD_SUPPRESSED_COMPETITORS.name());
@@ -432,6 +444,11 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
DBCollection leaderboardCollection = database.getCollection(CollectionNames.LEADERBOARDS.name());
String name = (String) o.get(FieldNames.LEADERBOARD_GROUP_NAME.name());
String description = (String) o.get(FieldNames.LEADERBOARD_GROUP_DESCRIPTION.name());
boolean displayGroupsInReverseOrder = false; // default value
Object displayGroupsInReverseOrderObj = o.get(FieldNames.LEADERBOARD_GROUP_DISPLAY_IN_REVERSE_ORDER.name());
if(displayGroupsInReverseOrderObj != null) {
displayGroupsInReverseOrder = (Boolean) displayGroupsInReverseOrderObj;
}
ArrayList<Leaderboard> leaderboards = new ArrayList<Leaderboard>();
BasicDBList dbLeaderboardIds = (BasicDBList) o.get(FieldNames.LEADERBOARD_GROUP_LEADERBOARDS.name());
for (Object object : dbLeaderboardIds) {
@@ -443,7 +460,7 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
}
}
logger.info("loaded leaderboard group "+name);
LeaderboardGroupImpl result = new LeaderboardGroupImpl(name, description, leaderboards);
LeaderboardGroupImpl result = new LeaderboardGroupImpl(name, description, displayGroupsInReverseOrder, leaderboards);
Object overallLeaderboardIdOrName = o.get(FieldNames.LEADERBOARD_GROUP_OVERALL_LEADERBOARD.name());
if (overallLeaderboardIdOrName != null) {
final DBObject dbOverallLeaderboard;
@@ -47,10 +47,11 @@ public enum FieldNames {
LEADERBOARD_NAME, LEADERBOARD_SUPPRESSED_COMPETITORS, LEADERBOARD_COLUMNS, LEADERBOARD_COLUMN_NAME, LEADERBOARD_COMPETITOR_DISPLAY_NAMES,
LEADERBOARD_IS_MEDAL_RACE_COLUMN, LEADERBOARD_CARRIED_POINTS, LEADERBOARD_SCORE_CORRECTIONS, LEADERBOARD_DISCARDING_THRESHOLDS,
LEADERBOARD_SCORE_CORRECTION_MAX_POINTS_REASON, LEADERBOARD_CORRECTED_SCORE, LEADERBOARD_SCORE_CORRECTION_TIMESTAMP,
LEADERBOARD_SCORE_CORRECTION_COMMENT,
LEADERBOARD_SCORE_CORRECTION_COMMENT, LEADERBOARD_COLUMN_FACTORS,
// leaderboard group
LEADERBOARD_GROUP_NAME, LEADERBOARD_GROUP_DESCRIPTION, LEADERBOARD_GROUP_LEADERBOARDS, LEADERBOARD_GROUP_OVERALL_LEADERBOARD,
LEADERBOARD_GROUP_DISPLAY_IN_REVERSE_ORDER,
// race ID to regatta name
RACE_ID_AS_STRING;
@@ -152,10 +152,22 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
// at least store the scoring scheme
dbLeaderboard.put(FieldNames.SCORING_SCHEME_TYPE.name(), leaderboard.getScoringScheme().getType().name());
}
storeColumnFactors(leaderboard, dbLeaderboard);
storeLeaderboardCorrectionsAndDiscards(leaderboard, dbLeaderboard);
leaderboardCollection.update(query, dbLeaderboard, /* upsrt */ true, /* multi */ false);
}
private void storeColumnFactors(Leaderboard leaderboard, BasicDBObject dbLeaderboard) {
DBObject raceColumnFactors = new BasicDBObject();
for (RaceColumn raceColumn : leaderboard.getRaceColumns()) {
Double explicitFactor = raceColumn.getExplicitFactor();
if (explicitFactor != null) {
raceColumnFactors.put(MongoUtils.escapeDollarAndDot(raceColumn.getName()), explicitFactor);
}
}
dbLeaderboard.put(FieldNames.LEADERBOARD_COLUMN_FACTORS.name(), raceColumnFactors);
}
private void storeRegattaLeaderboard(RegattaLeaderboard leaderboard, DBObject dbLeaderboard) {
dbLeaderboard.put(FieldNames.REGATTA_NAME.name(), leaderboard.getRegatta().getName());
}
@@ -234,6 +246,8 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
}
}
if (!dbCorrectionForRace.isEmpty()) {
// using the column name as the key for the score corrections requires re-writing the score corrections
// of a meta-leaderboard if the name of one of its leaderboards changes
dbScoreCorrections.put(raceColumn.getName(), dbCorrectionForRace);
}
}
@@ -276,6 +290,7 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
BasicDBObject dbLeaderboardGroup = new BasicDBObject();
dbLeaderboardGroup.put(FieldNames.LEADERBOARD_GROUP_NAME.name(), leaderboardGroup.getName());
dbLeaderboardGroup.put(FieldNames.LEADERBOARD_GROUP_DESCRIPTION.name(), leaderboardGroup.getDescription());
dbLeaderboardGroup.put(FieldNames.LEADERBOARD_GROUP_DISPLAY_IN_REVERSE_ORDER.name(), leaderboardGroup.isDisplayGroupsInReverseOrder());
final Leaderboard overallLeaderboard = leaderboardGroup.getOverallLeaderboard();
if (overallLeaderboard != null) {
BasicDBObject overallLeaderboardQuery = new BasicDBObject(FieldNames.LEADERBOARD_NAME.name(), overallLeaderboard.getName());
@@ -157,7 +157,7 @@ public class StoreAndForward implements Runnable {
if (lastMessageCountRecord == null) {
lastMessageCountCollection.insert(new BasicDBObject().append(FieldNames.LAST_MESSAGE_COUNT.name(), 0l));
}
return lastMessageCountRecord == null ? 0 : (Long) lastMessageCountRecord.get(FieldNames.LAST_MESSAGE_COUNT.name());
return lastMessageCountRecord == null ? 0 : ((Number) lastMessageCountRecord.get(FieldNames.LAST_MESSAGE_COUNT.name())).longValue();
}
/**
@@ -241,13 +241,23 @@ public class StoreAndForward implements Runnable {
}
public static void main(String[] args) throws InterruptedException, IOException {
int listenPort = Integer.valueOf(args[0]);
int clientPort = Integer.valueOf(args[1]);
String hostname = null;
int i=0;
if (args[i].equals("-h")) {
hostname = args[++i];
i++;
}
int sailMasterPort = Integer.valueOf(args[i++]);
int clientPort = Integer.valueOf(args[i++]);
MongoDBService mongoDBService = MongoDBService.INSTANCE;
mongoDBService.setConfiguration(MongoDBConfiguration.getDefaultConfiguration());
SwissTimingAdapterPersistence swissTimingAdapterPersistence = SwissTimingAdapterPersistence.INSTANCE;
new StoreAndForward(listenPort, clientPort, SwissTimingFactory.INSTANCE, swissTimingAdapterPersistence, mongoDBService);
if (hostname == null) {
new StoreAndForward(sailMasterPort, clientPort, SwissTimingFactory.INSTANCE, swissTimingAdapterPersistence, mongoDBService);
} else {
new StoreAndForward(hostname, sailMasterPort, clientPort, SwissTimingFactory.INSTANCE, swissTimingAdapterPersistence, mongoDBService);
}
}
public void run() {
@@ -125,7 +125,7 @@ public class SwissTimingAdapterPersistenceImpl implements SwissTimingAdapterPers
List<SailMasterMessage> result = new ArrayList<SailMasterMessage>();
for (DBObject o : results) {
SailMasterMessage msg = swissTimingFactory.createMessage((String) o.get(FieldNames.MESSAGE_CONTENT.name()),
(Long) o.get(FieldNames.MESSAGE_SEQUENCE_NUMBER.name()));
((Number) o.get(FieldNames.MESSAGE_SEQUENCE_NUMBER.name())).longValue());
result.add(msg);
}
return result;
@@ -0,0 +1 @@
RAC|1|W4702;Women 470 Race 2STL|W4702|46|ARG 8;ARG;ARG 8|AUS 7;AUS;AUS 7|AUT 431;AUT;AUT 431|BRA 177;BRA;BRA 177|BRA 1187;BRA;BRA 1187|CAN 11;CAN;CAN 11|CAN 610;CAN;CAN 610|CHN 1211;CHN;CHN 1211|CHN 1261;CHN;CHN 1261|CHN 221;CHN;CHN 221|CHN 616;CHN;CHN 616|CRO 111;CRO;CRO 111|DEN 143;DEN;DEN 143|ESP 133;ESP;ESP 133|ESP 696;ESP;ESP 696|EST 20;EST;EST 20|FRA 12;FRA;FRA 12|FRA 4;FRA;FRA 4|FRA 9;FRA;FRA 9|GBR 812;GBR;GBR 812|GBR 831;GBR;GBR 831|GBR 841;GBR;GBR 841|GBR 847;GBR;GBR 847|GBR 850;GBR;GBR 850|GBR 855;GBR;GBR 855|GER 12;GER;GER 12|GER 21;GER;GER 21|GER 61;GER;GER 61|GER 66;GER;GER 66|GER 72;GER;GER 72|ISR 311;ISR;ISR 311|ITA 23;ITA;ITA 23|JPN 4151;JPN;JPN 4151|JPN 4321;JPN;JPN 4321|NED 11;NED;NED 11|NED 24;NED;NED 24|NZL 75;NZL;NZL 75|RUS 12;RUS;RUS 12|RUS 700;RUS;RUS 700|SLO 64;SLO;SLO 64|SUI 14;SUI;SUI 14|SWE 342;SWE;SWE 342|SWE 344;SWE;SWE 344|USA 1712;USA;USA 1712|USA 1736;USA;USA 1736|USA 1757;USA;USA 1757RPD|W4702|0|2011-06-06T17:54:19+02:00|||||61|SB;2;0;50.621933;-2.366140;0.2;;;90;;;;;|MB4;4;0;50.605008;-2.373398;0.2;;;17;;;;;|MB3;4;0;50.607335;-2.388345;0.8;;;69;;;;;|MB2;4;0;50.617526;-2.352416;0.2;;;220;;;;;|MB1;4;0;50.619644;-2.363547;0.4;;;81;;;;;|M7;5;0;50.621848;-2.366225;0.0;;;90;;;;;|M6;5;0;50.607561;-2.387959;0.8;;;108;;;;;|M5;5;0;50.605096;-2.373590;0.4;;;222;;;;;|M4;5;0;50.617094;-2.352122;1.0;;;32;;;;;|M3;5;0;50.620254;-2.366982;1.0;;;167;;;;;|M2;5;0;50.619848;-2.366420;0.6;;;311;;;;;|M1;5;0;50.617360;-2.352805;0.6;;;134;;;;;|FP;4;0;50.617521;-2.352463;0.2;;;180;;;;;|FB;4;0;50.619268;-2.354392;0.6;;;27;;;;;|SP;4;0;50.619847;-2.363828;0.4;;;322;;;;;|ARG 8;1;0;50.621380;-2.365032;0.8;;;225;;;;;|AUS 7;1;0;50.621263;-2.364862;0.8;;;243;;;;;|AUT 431;1;0;50.621965;-2.366063;2.1;;;90;;;;;|BRA 177;1;0;50.622014;-2.366027;0.4;;;180;;;;;|BRA 1187;1;0;50.621852;-2.365759;0.6;;;153;;;;;|CAN 11;1;0;50.621128;-2.364736;0.6;;;180;;;;;|CAN 610;1;0;50.621951;-2.365865;1.6;;;243;;;;;|CHN 1211;1;0;50.621690;-2.365462;0.8;;;225;;;;;|CHN 1261;1;0;50.621501;-2.365166;1.7;;;169;;;;;|CHN 221;1;0;50.620430;-2.363818;0.2;;;225;;;;;|CHN 616;1;0;50.620251;-2.363536;3.3;;;219;;;;;|CRO 111;1;0;50.621173;-2.364813;0.0;;;225;;;;;|DEN 143;1;0;50.620934;-2.364467;0.8;;;243;;;;;|ESP 133;1;0;50.620624;-2.364079;0.6;;;180;;;;;|ESP 696;1;0;50.621312;-2.364947;0.6;;;162;;;;;|EST 20;1;0;50.621785;-2.365646;1.6;;;108;;;;;|FRA 12;1;0;50.621659;-2.365364;1.9;;;180;;;;;|FRA 4;1;0;50.621713;-2.365547;0.8;;;225;;;;;|FRA 9;1;0;50.620723;-2.364178;0.8;;;225;;;;;|GBR 812;1;0;50.621868;-2.365812;0.6;;;132;;;;;|GBR 831;1;;;;;;;;;;;;|GBR 841;1;0;50.620964;-2.364544;1.7;;;174;;;;;|GBR 847;1;0;50.621773;-2.365637;3.5;;;191;;;;;|GBR 850;1;0;50.621475;-2.365135;2.3;;;273;;;;;|GBR 855;1;0;50.619879;-2.363592;1.0;;;296;;;;;|GER 12;1;0;50.621562;-2.365258;0.6;;;179;;;;;|GER 21;1;0;50.620871;-2.364329;1.4;;;193;;;;;|GER 61;1;0;50.620787;-2.364242;1.0;;;183;;;;;|GER 66;1;0;50.620484;-2.363859;1.7;;;200;;;;;|GER 72;1;0;50.621653;-2.365376;1.9;;;174;;;;;|ISR 311;1;0;50.622183;-2.366012;1.4;;;211;;;;;|ITA 23;1;0;50.620513;-2.363913;2.1;;;278;;;;;|JPN 4151;1;0;50.620337;-2.363733;1.9;;;208;;;;;|JPN 4321;1;0;50.621128;-2.364648;1.0;;;157;;;;;|NED 11;1;0;50.620606;-2.364038;0.4;;;272;;;;;|NED 24;1;;;;;;;;;;;;|NZL 75;1;0;50.621411;-2.365085;0.6;;;332;;;;;|RUS 12;1;0;50.622181;-2.366163;1.9;;;161;;;;;|RUS 700;1;0;50.622005;-2.365919;0.8;;;217;;;;;|SLO 64;1;0;50.621017;-2.364512;3.9;;;259;;;;;|SUI 14;1;0;50.621708;-2.365600;0.2;;;270;;;;;|SWE 342;1;0;50.620647;-2.364130;0.6;;;274;;;;;|SWE 344;1;0;50.621326;-2.364849;5.6;;;142;;;;;|USA 1712;1;0;50.621901;-2.365836;0.0;;;155;;;;;|USA 1736;1;0;50.620603;-2.363976;0.6;;;207;;;;;|USA 1757;1;0;50.621044;-2.364583;1.6;;;196;;;;;RPD|W4702|0|2011-06-06T17:54:20+02:00|||||61|SB;2;0;50.621933;-2.366140;0.2;;;90;;;;;|MB4;4;0;50.605010;-2.373399;0.2;;;0;;;;;|MB3;4;0;50.607337;-2.388341;0.8;;;63;;;;;|MB2;4;0;50.617524;-2.352420;0.2;;;225;;;;;|MB1;4;0;50.619644;-2.363544;0.4;;;90;;;;;|M7;5;0;50.621848;-2.366225;0.0;;;90;;;;;|M6;5;0;50.607561;-2.387960;0.8;;;90;;;;;|M5;5;0;50.605096;-2.373589;0.4;;;90;;;;;|M4;5;0;50.617096;-2.352124;1.0;;;0;;;;;|M3;5;0;50.620253;-2.366984;1.0;;;180;;;;;|M2;5;0;50.619848;-2.366419;0.6;;;90;;;;;|M1;5;0;50.617358;-2.352802;0.6;;;135;;;;;|FP;4;0;50.617519;-2.352463;0.2;;;180;;;;;|FB;4;0;50.619273;-2.354388;0.6;;;27;;;;;|SP;4;0;50.619846;-2.363826;0.4;;;90;;;;;|ARG 8;1;0;50.621375;-2.365036;0.8;;;213;;;;;|AUS 7;1;0;50.621261;-2.364871;0.6;;;250;;;;;|AUT 431;1;0;50.621959;-2.366061;1.0;;;170;;;;;|BRA 177;1;0;50.622012;-2.366027;0.6;;;181;;;;;|BRA 1187;1;0;50.621850;-2.365762;1.0;;;220;;;;;|CAN 11;1;0;50.621125;-2.364739;0.8;;;219;;;;;|CAN 610;1;0;50.621948;-2.365866;1.4;;;194;;;;;|CHN 1211;1;0;50.621688;-2.365462;0.6;;;175;;;;;|CHN 1261;1;0;50.621496;-2.365174;1.7;;;228;;;;;|CHN 221;1;0;50.620427;-2.363819;0.4;;;191;;;;;|CHN 616;1;0;50.620246;-2.363549;2.1;;;239;;;;;|CRO 111;1;0;50.621171;-2.364817;0.0;;;225;;;;;|DEN 143;1;0;50.620934;-2.364478;0.8;;;268;;;;;|ESP 133;1;0;50.62062Line truncated
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,3 @@
RAC|1|W4702;Women 470 Race 2
STL|W4702|46|ARG 8;ARG;ARG 8|AUS 7;AUS;AUS 7|AUT 431;AUT;AUT 431|BRA 177;BRA;BRA 177|BRA 1187;BRA;BRA 1187|CAN 11;CAN;CAN 11|CAN 610;CAN;CAN 610|CHN 1211;CHN;CHN 1211|CHN 1261;CHN;CHN 1261|CHN 221;CHN;CHN 221|CHN 616;CHN;CHN 616|CRO 111;CRO;CRO 111|DEN 143;DEN;DEN 143|ESP 133;ESP;ESP 133|ESP 696;ESP;ESP 696|EST 20;EST;EST 20|FRA 12;FRA;FRA 12|FRA 4;FRA;FRA 4|FRA 9;FRA;FRA 9|GBR 812;GBR;GBR 812|GBR 831;GBR;GBR 831|GBR 841;GBR;GBR 841|GBR 847;GBR;GBR 847|GBR 850;GBR;GBR 850|GBR 855;GBR;GBR 855|GER 12;GER;GER 12|GER 21;GER;GER 21|GER 61;GER;GER 61|GER 66;GER;GER 66|GER 72;GER;GER 72|ISR 311;ISR;ISR 311|ITA 23;ITA;ITA 23|JPN 4151;JPN;JPN 4151|JPN 4321;JPN;JPN 4321|NED 11;NED;NED 11|NED 24;NED;NED 24|NZL 75;NZL;NZL 75|RUS 12;RUS;RUS 12|RUS 700;RUS;RUS 700|SLO 64;SLO;SLO 64|SUI 14;SUI;SUI 14|SWE 342;SWE;SWE 342|SWE 344;SWE;SWE 344|USA 1712;USA;USA 1712|USA 1736;USA;USA 1736|USA 1757;USA;USA 1757
CCG|W4702|7|0;StartGate;M1;M7|1;Buoy6;M6|2;Buoy5;M5|3;LeeGate;M2;M3|4;Buoy6;M6|5;Buoy5;M5|6;FinishGate;M1;M4
@@ -17,14 +17,15 @@ import java.util.List;
import com.sap.sailing.domain.base.SpeedWithBearing;
import com.sap.sailing.domain.base.impl.KnotSpeedImpl;
import com.sap.sailing.domain.base.impl.KnotSpeedWithBearingImpl;
import com.sap.sailing.domain.base.impl.MeterDistance;
import com.sap.sailing.domain.common.Distance;
import com.sap.sailing.domain.common.Speed;
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
import com.sap.sailing.domain.common.impl.MeterDistance;
import com.sap.sailing.domain.swisstimingadapter.Competitor;
import com.sap.sailing.domain.swisstimingadapter.Mark;
import com.sap.sailing.domain.swisstimingadapter.Race;
import com.sap.sailing.domain.swisstimingadapter.SailMasterMessage;
import com.sap.sailing.domain.swisstimingadapter.Mark.MarkType;
import com.sap.sailing.domain.swisstimingadapter.classes.messages.CAMMessage;
import com.sap.sailing.domain.swisstimingadapter.classes.messages.CCGMessage;
import com.sap.sailing.domain.swisstimingadapter.classes.messages.ClockAtMarkElement;
@@ -193,8 +194,20 @@ public class MessageFileServiceImpl implements MessageFileService {
List<Mark> marks = new ArrayList<Mark>();
for (int i = 0; i < count; i++) {
String[] markDetails = message.getSections()[3 + i].split(";");
marks.add(new MarkImpl(markDetails[1], Integer.valueOf(markDetails[0]), Arrays.asList(markDetails).subList(
2, markDetails.length)));
MarkType markType = null;
final int devicesNamesStartIndex;
if (message.getSections()[3+i].split(";", -1).length == 5) {
// this is the SailMaster protocol version 1.0 (May 2012) or later (see bug 1000), containing
// a MarkType specification before the two tracker IDs:
int markTypeIndex = Integer.valueOf(markDetails[2]);
markType = MarkType.values()[markTypeIndex];
devicesNamesStartIndex = 3;
} else {
devicesNamesStartIndex = 2;
}
marks.add(new MarkImpl(markDetails[1], Integer.valueOf(markDetails[0]),
Arrays.asList(markDetails).subList(devicesNamesStartIndex, markDetails.length),
markType));
}
return new CCGMessage(raceId, marks);
}
@@ -89,9 +89,10 @@ public class EndToEndListeningStoreAndFowardTest {
new BasicDBObject().append(FieldNames.LAST_MESSAGE_COUNT.name(), 0l),
/* upsert */true, /* multi */false);
// important: construct a new domain factory each time to make sure the competitor cache starts out empty
final com.sap.sailing.domain.base.impl.DomainFactoryImpl baseDomainFactory = new com.sap.sailing.domain.base.impl.DomainFactoryImpl();
racingEventService = new RacingEventServiceImpl(mongoDBService, SwissTimingFactory.INSTANCE,
new DomainFactoryImpl(new com.sap.sailing.domain.base.impl.DomainFactoryImpl()),
com.sap.sailing.domain.tractracadapter.DomainFactory.INSTANCE);
new DomainFactoryImpl(baseDomainFactory),
new com.sap.sailing.domain.tractracadapter.impl.DomainFactoryImpl(baseDomainFactory));
raceHandles = new ArrayList<RacesHandle>();
}
@@ -140,7 +141,19 @@ public class EndToEndListeningStoreAndFowardTest {
String scriptName1 = "/SailMasterDataInterfaceRACandSTL.txt";
String scriptName2 = "/SailMasterDataInterface-ExampleAsText.txt";
setUpUsingScript(racesToTrack, scriptName1, scriptName2);
coreOfTestLongRaceLog();
}
@Test
public void testLongRaceLogNewVersion() throws Exception {
String[] racesToTrack = new String[] { "W4702" };
String scriptName1 = "/W4702RACandSTLandCCG.txt";
String scriptName2 = "/W4702AsText.txt";
setUpUsingScript(racesToTrack, scriptName1, scriptName2);
coreOfTestLongRaceLog();
}
private void coreOfTestLongRaceLog() {
Set<TrackedRace> allTrackedRaces = new HashSet<TrackedRace>();
Iterable<Regatta> allRegattas = racingEventService.getAllRegattas();
for (Regatta regatta : allRegattas) {
@@ -163,7 +176,8 @@ public class EndToEndListeningStoreAndFowardTest {
assertEquals(7, Util.size(race.getCourse().getWaypoints()));
assertEquals(6, Util.size(race.getCourse().getLegs()));
for (Competitor competitor : race.getCompetitors()) {
if (!competitor.getName().equals("Competitor 35") && !competitor.getName().equals("Competitor 20")) {
if (!competitor.getName().equals("Competitor 35") && !competitor.getName().equals("Competitor 20")
&& !competitor.getName().equals("GBR 831") && !competitor.getName().equals("NED 24")) {
final GPSFixTrack<Competitor, GPSFixMoving> track = trackedRace.getTrack(competitor);
track.lockForRead();
try {
@@ -227,7 +241,7 @@ public class EndToEndListeningStoreAndFowardTest {
assertEquals(2, Util.size(race.getCourse().getLegs()));
for (Competitor competitor : race.getCompetitors()) {
if (!competitor.getName().equals("NED 24") && !competitor.getName().equals("Competitor 35")
&& !competitor.getName().equals("Competitor 20")) {
&& !competitor.getName().equals("Competitor 20") && !competitor.getName().equals("GBR 831")) {
final GPSFixTrack<Competitor, GPSFixMoving> track = trackedRace.getTrack(competitor);
track.lockForRead();
try {
@@ -1,6 +1,7 @@
package com.sap.sailing.domain.swisstimingadapter.test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import java.util.ArrayList;
import java.util.Arrays;
@@ -14,6 +15,7 @@ import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.base.Waypoint;
import com.sap.sailing.domain.base.impl.RegattaImpl;
import com.sap.sailing.domain.common.ScoringSchemeType;
import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.common.impl.Util;
import com.sap.sailing.domain.swisstimingadapter.Competitor;
import com.sap.sailing.domain.swisstimingadapter.Course;
@@ -30,6 +32,62 @@ import com.sap.sailing.server.impl.RacingEventServiceImpl;
import difflib.PatchFailedException;
public class SimpleDomainFactoryTest {
/**
* Tests that an unknown boat class encoded in a regular "SA" format race ID is returned as the unknown boat class
* instead of null.
*/
@Test
public void testUnknownBoatClassInValidRaceID() {
DomainFactory domainFactory = DomainFactory.INSTANCE;
final String raceID = "SAX920103";
RaceDefinition raceDefinition = domainFactory.createRaceDefinition(domainFactory.getOrCreateRegatta(raceID,
new RacingEventServiceImpl()),
new Race() {
@Override
public String getDescription() {
return null;
}
@Override
public String getRaceID() {
return raceID;
}
@Override
public TimePoint getStartTime() {
return null;
}
@Override
public void setStartTime(TimePoint timePoint) {
}
}, new StartList() {
@Override
public String getRaceID() {
return raceID;
}
@Override
public Iterable<Competitor> getCompetitors() {
return Collections.emptyList();
}
}, new Course() {
@Override
public String getRaceID() {
return raceID;
}
@Override
public Iterable<Mark> getMarks() {
return Collections.emptyList();
}
});
assertNotNull(raceDefinition.getBoatClass());
assertEquals("Unknown", raceDefinition.getBoatClass().getName());
}
@Test
public void testCourseConfigForBuoy() throws PatchFailedException {
DomainFactory domainFactory = DomainFactory.INSTANCE;
@@ -38,8 +96,8 @@ public class SimpleDomainFactoryTest {
Race race = new RaceImpl("1234", "Race 1234");
Iterable<Competitor> competitors = Collections.emptyList();
StartList startList = new StartListImpl("1234", competitors);
Mark mark1 = new MarkImpl("M1", 0, Arrays.asList("D1", "D2"));
Mark mark2 = new MarkImpl("M1", 0, Arrays.asList("D3", "D4"));
Mark mark1 = new MarkImpl("M1", 0, Arrays.asList("D1", "D2"), /* markType */ null);
Mark mark2 = new MarkImpl("M1", 0, Arrays.asList("D3", "D4"), /* markType */ null);
List<Mark> marks = Arrays.asList(mark1, mark2);
Course course = new CourseImpl("1234", marks);
RaceDefinition raceDefinition = domainFactory.createRaceDefinition(regatta, race, startList, course);
@@ -63,8 +121,8 @@ public class SimpleDomainFactoryTest {
Race race = new RaceImpl("1234", "Race 1234");
Iterable<Competitor> competitors = Collections.emptyList();
StartList startList = new StartListImpl("1234", competitors);
Mark mark1 = new MarkImpl("M1", 0, Arrays.asList("D1", "D2"));
Mark mark2 = new MarkImpl("M1", 0, Arrays.asList("D3", "D4"));
Mark mark1 = new MarkImpl("M1", 0, Arrays.asList("D1", "D2"), /* markType */ null);
Mark mark2 = new MarkImpl("M1", 0, Arrays.asList("D3", "D4"), /* markType */ null);
List<Mark> marks = Arrays.asList(mark1, mark2);
Course course = new CourseImpl("1234", marks);
RaceDefinition raceDefinition = domainFactory.createRaceDefinition(regatta, race, startList, course);
@@ -269,8 +269,8 @@ public class EditCCG extends javax.swing.JDialog {
private class MarkWithDesc extends MarkImpl {
public MarkWithDesc(String description, int index, Iterable<String> devices ){
super(description, index, devices);
public MarkWithDesc(String description, int index, Iterable<String> devices) {
super(description, index, devices, /* markType */ null);
}
@Override
@@ -34,4 +34,10 @@ public interface Fix {
Distance getDistanceToLeader();
Distance getDistanceToNextMark();
/**
* Obtains a disqualification reason which is one of { None, BFD, DNS, DNF, DNC, DSQ, OCS, RAF, SCP, ZFP }. May be
* <code>null</code>.
*/
String getBoatIRM();
}
@@ -1,7 +1,11 @@
package com.sap.sailing.domain.swisstimingadapter;
public interface Mark extends WithDescription {
public enum MarkType { MANUAL, LINE_CROSSING, BUOY_ROUNDING };
int getIndex();
Iterable<String> getDevices();
MarkType getMarkType();
}
@@ -54,12 +54,12 @@ public enum MessageType {
/**
* Weather Information
*/
WEA(/* isRaceSpecific */ false),
WEA(/* isRaceSpecific */ true),
/**
* Wind Data
*/
WND(/* isRaceSpecific */ false),
WND(/* isRaceSpecific */ true),
/**
* Version
@@ -70,15 +70,15 @@ public class DomainFactoryImpl implements DomainFactory {
controlPointCache = new HashMap<Iterable<String>, ControlPoint>();
olympicClassesByID = new HashMap<String, BoatClass>();
/*
SAM102000 Men's Windsurfer = Windsufer Männer RS:X
SAM102000 Men's Windsurfer = Windsufer M�nner RS:X
SAW102000 Women's Windsurfer = Windsurfer Damen RS:X
SAM004000 Men's One Person Dinghy = Laser Männer
SAM004000 Men's One Person Dinghy = Laser M�nner
SAW103000 Women's One Person Dinghy = Laser Damen Laser Radial
SAM002000 Men's One Person Dinghy Heavy = Finn Dinghy Männer
SAM005000 Men's Two Person Dinghy = 470er Männer
SAM002000 Men's One Person Dinghy Heavy = Finn Dinghy M�nner
SAM005000 Men's Two Person Dinghy = 470er M�nner
SAW005000 Women's Two Person Dinghy = 470er Damen
SAM009000 Men's Skiff = 49er Männer
SAM007000 Men's Keelboat = Starboot Männer
SAM009000 Men's Skiff = 49er M�nner
SAM007000 Men's Keelboat = Starboot M�nner
SAW010000 Women's Match Racing = Matchrace Damen Elliott 6M (modified)
*/
olympicClassesByID.put("102", baseDomainFactory.getOrCreateBoatClass("RS:X", /* typicallyStartsUpwind */ true));
@@ -148,20 +148,24 @@ public class DomainFactoryImpl implements DomainFactory {
private BoatClass getOrCreateBoatClassFromRaceID(String raceID) {
BoatClass result;
/*
SAM102000 Men's Windsurfer = Windsufer Männer RS:X
SAM102000 Men's Windsurfer = Windsufer M�nner RS:X
SAW102000 Women's Windsurfer = Windsurfer Damen RS:X
SAM004000 Men's One Person Dinghy = Laser Männer
SAM004000 Men's One Person Dinghy = Laser M�nner
SAW103000 Women's One Person Dinghy = Laser Damen Laser Radial
SAM002000 Men's One Person Dinghy Heavy = Finn Dinghy Männer
SAM005000 Men's Two Person Dinghy = 470er Männer
SAM002000 Men's One Person Dinghy Heavy = Finn Dinghy M�nner
SAM005000 Men's Two Person Dinghy = 470er M�nner
SAW005000 Women's Two Person Dinghy = 470er Damen
SAM009000 Men's Skiff = 49er Männer
SAM007000 Men's Keelboat = Starboot Männer
SAM009000 Men's Skiff = 49er M�nner
SAM007000 Men's Keelboat = Starboot M�nner
SAW010000 Women's Match Racing = Matchrace Damen Elliott 6M (modified)
*/
if (raceID.startsWith("SA") && raceID.length() == 9) {
String classID = raceID.substring(3, 6);
result = olympicClassesByID.get(classID);
if (result == null) {
// unknown code
result = unknownBoatClass;
}
} else {
result = unknownBoatClass;
}
@@ -20,10 +20,14 @@ public class FixImpl implements Fix {
private final Distance distanceToLeader;
private final Distance distanceToNextMark;
/**
* A disqualification reason, such as "DNC" or "DNS". May be <code>null</code>.
*/
private final String boatIRM;
public FixImpl(String boatID, TrackerType trackerType, Long ageOfDataInMilliseconds, Position position,
SpeedWithBearing speed, Integer nextMarkIndex, Integer rank, Speed averageSpeedOverGroundPerLeg,
Speed velocityMadeGood, Distance distanceToLeader, Distance distanceToNextMark) {
Speed velocityMadeGood, Distance distanceToLeader, Distance distanceToNextMark, String boatIRM) {
super();
this.boatID = boatID;
this.trackerType = trackerType;
@@ -36,6 +40,12 @@ public class FixImpl implements Fix {
this.velocityMadeGood = velocityMadeGood;
this.distanceToLeader = distanceToLeader;
this.distanceToNextMark = distanceToNextMark;
this.boatIRM = boatIRM;
}
@Override
public String getBoatIRM() {
return boatIRM;
}
@Override
@@ -8,12 +8,13 @@ public class MarkImpl implements Mark {
private final String description;
private final int index;
private final Iterable<String> devices;
private final MarkType markType;
public MarkImpl(String description, int index, Iterable<String> devices) {
public MarkImpl(String description, int index, Iterable<String> devices, MarkType markType) {
super();
this.description = description;
this.index = index;
this.markType = markType;
ArrayList<String> l = new ArrayList<String>();
for (String device : devices) {
l.add(device);
@@ -21,6 +22,11 @@ public class MarkImpl implements Mark {
this.devices = l;
}
@Override
public MarkType getMarkType() {
return markType;
}
@Override
public String getDescription() {
return description;
@@ -27,7 +27,6 @@ import java.util.logging.Logger;
import com.sap.sailing.domain.base.SpeedWithBearing;
import com.sap.sailing.domain.base.impl.KnotSpeedImpl;
import com.sap.sailing.domain.base.impl.KnotSpeedWithBearingImpl;
import com.sap.sailing.domain.base.impl.MeterDistance;
import com.sap.sailing.domain.base.impl.MillisecondsTimePoint;
import com.sap.sailing.domain.common.Distance;
import com.sap.sailing.domain.common.Position;
@@ -36,12 +35,14 @@ import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
import com.sap.sailing.domain.common.impl.DegreePosition;
import com.sap.sailing.domain.common.impl.KilometersPerHourSpeedImpl;
import com.sap.sailing.domain.common.impl.MeterDistance;
import com.sap.sailing.domain.common.impl.Util.Pair;
import com.sap.sailing.domain.common.impl.Util.Triple;
import com.sap.sailing.domain.swisstimingadapter.Competitor;
import com.sap.sailing.domain.swisstimingadapter.Course;
import com.sap.sailing.domain.swisstimingadapter.Fix;
import com.sap.sailing.domain.swisstimingadapter.Mark;
import com.sap.sailing.domain.swisstimingadapter.Mark.MarkType;
import com.sap.sailing.domain.swisstimingadapter.MessageType;
import com.sap.sailing.domain.swisstimingadapter.Race;
import com.sap.sailing.domain.swisstimingadapter.RaceSpecificMessageLoader;
@@ -336,7 +337,7 @@ public class SailMasterConnectorImpl extends SailMasterTransceiverImpl implement
parseHHMMSSToMilliseconds(details[2]);
markIndicesRanksAndTimesSinceStartInMilliseconds.add(new Triple<Integer, Integer, Long>(markIndex, rank, timeSinceStartInMilliseconds));
}
for (SailMasterListener listener : listeners) {
for (SailMasterListener listener : getGeneralAndRaceSpecificListeners(message.getRaceID())) {
try {
listener.receivedTimingData(raceID, boatID, markIndicesRanksAndTimesSinceStartInMilliseconds);
} catch (Throwable t) {
@@ -344,22 +345,11 @@ public class SailMasterConnectorImpl extends SailMasterTransceiverImpl implement
logger.throwing(SailMasterConnectorImpl.class.getName(), "notifyListenersTMD", t);
}
}
Set<SailMasterListener> raceSpecificListenerSet = raceSpecificListeners.get(message.getRaceID());
if (raceSpecificListenerSet != null) {
for (SailMasterListener listener : raceSpecificListenerSet) {
try {
listener.receivedTimingData(raceID, boatID, markIndicesRanksAndTimesSinceStartInMilliseconds);
} catch (Throwable t) {
logger.info("Exception occurred trying to notify listener "+listener+" about "+message+": "+t.getMessage());
logger.throwing(SailMasterConnectorImpl.class.getName(), "notifyListenersTMD", t);
}
}
}
}
private void notifyListenersCAM(SailMasterMessage message) throws ParseException {
List<Triple<Integer, TimePoint, String>> clockAtMarkResults = parseClockAtMarkMessage(message);
for (SailMasterListener listener : listeners) {
for (SailMasterListener listener : getGeneralAndRaceSpecificListeners(message.getRaceID())) {
try {
listener.receivedClockAtMark(message.getSections()[1], clockAtMarkResults);
} catch (Throwable t) {
@@ -367,22 +357,11 @@ public class SailMasterConnectorImpl extends SailMasterTransceiverImpl implement
logger.throwing(SailMasterConnectorImpl.class.getName(), "notifyListenersCAM", t);
}
}
Set<SailMasterListener> raceSpecificListenerSet = raceSpecificListeners.get(message.getRaceID());
if (raceSpecificListenerSet != null) {
for (SailMasterListener listener : raceSpecificListenerSet) {
try {
listener.receivedClockAtMark(message.getSections()[1], clockAtMarkResults);
} catch (Throwable t) {
logger.info("Exception occurred trying to notify listener "+listener+" about "+message+": "+t.getMessage());
logger.throwing(SailMasterConnectorImpl.class.getName(), "notifyListenersCAM", t);
}
}
}
}
private void notifyListenersSTL(SailMasterMessage message) {
StartList startListMessage = parseStartListMessage(message);
for (SailMasterListener listener : listeners) {
for (SailMasterListener listener : getGeneralAndRaceSpecificListeners(message.getRaceID())) {
try {
listener.receivedStartList(message.getSections()[1], startListMessage);
} catch (Throwable t) {
@@ -390,22 +369,11 @@ public class SailMasterConnectorImpl extends SailMasterTransceiverImpl implement
logger.throwing(SailMasterConnectorImpl.class.getName(), "notifyListenersSTL", t);
}
}
Set<SailMasterListener> raceSpecificListenerSet = raceSpecificListeners.get(message.getRaceID());
if (raceSpecificListenerSet != null) {
for (SailMasterListener listener : raceSpecificListenerSet) {
try {
listener.receivedStartList(message.getSections()[1], startListMessage);
} catch (Throwable t) {
logger.info("Exception occurred trying to notify listener "+listener+" about "+message+": "+t.getMessage());
logger.throwing(SailMasterConnectorImpl.class.getName(), "notifyListenersSTL", t);
}
}
}
}
private void notifyListenersCCG(SailMasterMessage message) {
Course course = parseCourseConfigurationMessage(message);
for (SailMasterListener listener : listeners) {
for (SailMasterListener listener : getGeneralAndRaceSpecificListeners(message.getRaceID())) {
try {
listener.receivedCourseConfiguration(message.getSections()[1], course);
} catch (Throwable t) {
@@ -413,16 +381,6 @@ public class SailMasterConnectorImpl extends SailMasterTransceiverImpl implement
logger.throwing(SailMasterConnectorImpl.class.getName(), "notifyListenersCCG", t);
}
}
Set<SailMasterListener> raceSpecificListenerSet = raceSpecificListeners.get(message.getRaceID());
if (raceSpecificListenerSet != null) {
for (SailMasterListener listener : raceSpecificListenerSet) {
try {
listener.receivedCourseConfiguration(message.getSections()[1], course);
} catch (Throwable t) {
logger.throwing(SailMasterConnectorImpl.class.getName(), "notifyListenersCCG", t);
}
}
}
}
private void notifyListenersRAC(SailMasterMessage message) {
@@ -458,42 +416,52 @@ public class SailMasterConnectorImpl extends SailMasterTransceiverImpl implement
Collection<Fix> fixes = new ArrayList<Fix>();
for (int i=0; i<count; i++) {
int fixDetailIndex = 0;
String[] fixSections = sections[9+i].split(";");
final String[] fixSections = sections[9+i].split(";");
final boolean postVersion1_0 = sections[9+i].split(";", -1).length >= 14;
if (fixSections.length > 2) {
String boatID = fixSections[fixDetailIndex++];
TrackerType trackerType = TrackerType.values()[Integer.valueOf(fixSections[fixDetailIndex++])];
Long ageOfDataInMilliseconds = 1000l * Long.valueOf(fixSections[fixDetailIndex++]);
Position position = new DegreePosition(Double.valueOf(fixSections[fixDetailIndex++]),
final String boatID = fixSections[fixDetailIndex++];
final TrackerType trackerType = TrackerType.values()[Integer.valueOf(fixSections[fixDetailIndex++])];
final Long ageOfDataInMilliseconds = 1000l * Long.valueOf(fixSections[fixDetailIndex++]);
final Position position = new DegreePosition(Double.valueOf(fixSections[fixDetailIndex++]),
Double.valueOf(fixSections[fixDetailIndex++]));
Double speedOverGroundInKnots = Double.valueOf(fixSections[fixDetailIndex++]);
Speed averageSpeedOverGround = fixSections[fixDetailIndex].trim().length() == 0 ? null
: new KnotSpeedImpl(Double.valueOf(fixSections[fixDetailIndex]));
fixDetailIndex++;
Speed velocityMadeGood = fixSections[fixDetailIndex].trim().length() == 0 ? null : new KnotSpeedImpl(
Double.valueOf(fixSections[fixDetailIndex]));
fixDetailIndex++;
SpeedWithBearing speed = new KnotSpeedWithBearingImpl(speedOverGroundInKnots, new DegreeBearingImpl(
Double.valueOf(fixSections[fixDetailIndex++])));
Integer nextMarkIndex = fixSections.length <= fixDetailIndex
final Double speedOverGroundInKnots = Double.valueOf(fixSections[fixDetailIndex++]);
final int alsIndex = postVersion1_0 ? fixDetailIndex+1 : fixDetailIndex;
final int vmgIndex = postVersion1_0 ? fixDetailIndex : fixDetailIndex+1;
final Speed averageSpeedOverGround = fixSections[alsIndex].trim().length() == 0 ? null
: new KnotSpeedImpl(Double.valueOf(fixSections[alsIndex]));
final Speed velocityMadeGood = fixSections[vmgIndex].trim().length() == 0 ? null : new KnotSpeedImpl(
Double.valueOf(fixSections[vmgIndex]));
fixDetailIndex += 2;
final DegreeBearingImpl cog = new DegreeBearingImpl(
Double.valueOf(fixSections[fixDetailIndex++]));
final SpeedWithBearing speed = new KnotSpeedWithBearingImpl(speedOverGroundInKnots, cog);
final Integer nextMarkIndex = fixSections.length <= fixDetailIndex
|| fixSections[fixDetailIndex].trim().length() == 0 ? null : Integer
.valueOf(fixSections[fixDetailIndex]);
fixDetailIndex++;
Integer rank = fixSections.length <= fixDetailIndex || fixSections[fixDetailIndex].trim().length() == 0 ? null
final Integer rank = fixSections.length <= fixDetailIndex || fixSections[fixDetailIndex].trim().length() == 0 ? null
: Integer.valueOf(fixSections[fixDetailIndex]);
fixDetailIndex++;
Distance distanceToLeader = fixSections.length <= fixDetailIndex
final Distance distanceToLeader = fixSections.length <= fixDetailIndex
|| fixSections[fixDetailIndex].trim().length() == 0 ? null : new MeterDistance(
Double.valueOf(fixSections[fixDetailIndex]));
fixDetailIndex++;
Distance distanceToNextMark = fixSections.length <= fixDetailIndex
final Distance distanceToNextMark = fixSections.length <= fixDetailIndex
|| fixSections[fixDetailIndex].trim().length() == 0 ? null : new MeterDistance(
Double.valueOf(fixSections[fixDetailIndex]));
fixDetailIndex++;
final String boatIRM; // the "disqualification" or "MaxPointReason"
if (postVersion1_0 && fixSections.length > fixDetailIndex) {
boatIRM = fixSections[fixDetailIndex++];
} else {
boatIRM = null;
}
fixes.add(new FixImpl(boatID, trackerType, ageOfDataInMilliseconds, position, speed, nextMarkIndex,
rank, averageSpeedOverGround, velocityMadeGood, distanceToLeader, distanceToNextMark));
rank, averageSpeedOverGround, velocityMadeGood, distanceToLeader, distanceToNextMark, boatIRM));
}
}
for (SailMasterListener listener : listeners) {
Set<SailMasterListener> allListeners = getGeneralAndRaceSpecificListeners(message.getRaceID());
for (SailMasterListener listener : allListeners) {
try {
listener.receivedRacePositionData(raceID, status, timePoint, startTimeEstimatedStartTime, millisecondsSinceRaceStart,
nextMarkIndexForLeader, distanceToNextMarkForLeader, fixes);
@@ -502,18 +470,15 @@ public class SailMasterConnectorImpl extends SailMasterTransceiverImpl implement
logger.throwing(SailMasterConnectorImpl.class.getName(), "notifyListenersRPD", t);
}
}
Set<SailMasterListener> raceSpecificListenerSet = raceSpecificListeners.get(message.getRaceID());
if (raceSpecificListenerSet != null) {
for (SailMasterListener listener : raceSpecificListenerSet) {
try {
listener.receivedRacePositionData(raceID, status, timePoint, startTimeEstimatedStartTime, millisecondsSinceRaceStart,
nextMarkIndexForLeader, distanceToNextMarkForLeader, fixes);
} catch (Throwable t) {
logger.info("Exception occurred trying to notify listener "+listener+" about "+message+": "+t.getMessage());
logger.throwing(SailMasterConnectorImpl.class.getName(), "notifyListenersRPD", t);
}
}
}
private Set<SailMasterListener> getGeneralAndRaceSpecificListeners(String raceID) {
Set<SailMasterListener> allListeners = new HashSet<SailMasterListener>(listeners);
Set<SailMasterListener> listenersForThisRace = raceSpecificListeners.get(raceID);
if (listenersForThisRace != null) {
allListeners.addAll(listenersForThisRace);
}
return allListeners;
}
@Override
@@ -630,7 +595,20 @@ public class SailMasterConnectorImpl extends SailMasterTransceiverImpl implement
List<Mark> marks = new ArrayList<Mark>();
for (int i=0; i<count; i++) {
String[] markDetails = courseConfigurationMessage.getSections()[3+i].split(";");
marks.add(new MarkImpl(markDetails[1], Integer.valueOf(markDetails[0]), Arrays.asList(markDetails).subList(2, markDetails.length)));
MarkType markType = null;
final int devicesNamesStartIndex;
if (courseConfigurationMessage.getSections()[3+i].split(";", -1).length == 5) {
// this is the SailMaster protocol version 1.0 (May 2012) or later (see bug 1000), containing
// a MarkType specification before the two tracker IDs:
int markTypeIndex = Integer.valueOf(markDetails[2]);
markType = MarkType.values()[markTypeIndex];
devicesNamesStartIndex = 3;
} else {
devicesNamesStartIndex = 2;
}
marks.add(new MarkImpl(markDetails[1], Integer.valueOf(markDetails[0]),
Arrays.asList(markDetails).subList(devicesNamesStartIndex, markDetails.length),
markType));
}
return new CourseImpl(courseConfigurationMessage.getSections()[1], marks);
}
@@ -15,9 +15,7 @@ public class SailMasterLiveSimulatorConnectorImpl extends SailMasterConnectorImp
public SailMasterLiveSimulatorConnectorImpl(String host, int port, RaceSpecificMessageLoader messageLoader, boolean canSendRequests) throws InterruptedException {
super(host, port, messageLoader, canSendRequests);
bufferedMessageList = Collections.synchronizedList(new ArrayList<SailMasterMessage>());
Thread messageDeliveryThread = new Thread("SailMasterLiveSimulatorConnector") {
public void run() {
while(true) {
@@ -247,7 +247,7 @@ public class SwissTimingRaceTrackerImpl extends AbstractRaceTrackerImpl implemen
public void receivedClockAtMark(String raceID,
List<Triple<Integer, TimePoint, String>> markIndicesTimePointsAndBoatIDs) {
// Ignored because it's covered by TMD. Mail from Kai Hahndorf of 2011-11-15T12:42:00Z:
// "Die TMD werden immer gesendet. Das CAM Protokoll ist nur für unsere TV-Grafik wichtig, da damit die Rückstandsuhr gestartet wird."
// "Die TMD werden immer gesendet. Das CAM Protokoll ist nur fuer unsere TV-Grafik wichtig, da damit die Rueckstandsuhr gestartet wird."
}
@Override
@@ -17,10 +17,10 @@ import org.junit.Test;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.impl.DouglasPeucker;
import com.sap.sailing.domain.base.impl.KnotSpeedWithBearingImpl;
import com.sap.sailing.domain.base.impl.MeterDistance;
import com.sap.sailing.domain.base.impl.MillisecondsTimePoint;
import com.sap.sailing.domain.common.WindSourceType;
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
import com.sap.sailing.domain.common.impl.MeterDistance;
import com.sap.sailing.domain.common.impl.WindSourceImpl;
import com.sap.sailing.domain.tracking.DynamicGPSFixTrack;
import com.sap.sailing.domain.tracking.GPSFixMoving;
@@ -8,8 +8,11 @@ import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NavigableSet;
import org.junit.Test;
@@ -20,10 +23,14 @@ import com.sap.sailing.domain.base.Fleet;
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.base.Waypoint;
import com.sap.sailing.domain.base.impl.BuoyImpl;
import com.sap.sailing.domain.base.impl.FleetImpl;
import com.sap.sailing.domain.base.impl.GateImpl;
import com.sap.sailing.domain.base.impl.MillisecondsTimePoint;
import com.sap.sailing.domain.base.impl.RegattaImpl;
import com.sap.sailing.domain.base.impl.SeriesImpl;
import com.sap.sailing.domain.base.impl.WaypointImpl;
import com.sap.sailing.domain.common.MaxPointsReason;
import com.sap.sailing.domain.common.NoWindException;
import com.sap.sailing.domain.common.ScoringSchemeType;
@@ -41,7 +48,11 @@ import com.sap.sailing.domain.leaderboard.impl.ResultDiscardingRuleImpl;
import com.sap.sailing.domain.leaderboard.impl.ScoreCorrectionImpl;
import com.sap.sailing.domain.leaderboard.meta.LeaderboardGroupMetaLeaderboard;
import com.sap.sailing.domain.test.mock.MockedTrackedRaceWithStartTimeAndRanks;
import com.sap.sailing.domain.tracking.MarkPassing;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.impl.MarkPassingImpl;
import com.sap.sailing.domain.tracking.impl.TimedComparator;
import com.sap.sailing.util.impl.ArrayListNavigableSet;
public class LeaderboardScoringAndRankingTest extends AbstractLeaderboardTest {
private ArrayList<Series> series;
@@ -512,6 +523,48 @@ public class LeaderboardScoringAndRankingTest extends AbstractLeaderboardTest {
assertEquals(reversedCompetitors, leaderboard.getCompetitorsFromBestToWorst(later));
}
@Test
public void testTotalTimeNotCountedForRacesStartedLaterThanTimePointRequested() {
TimePoint now = MillisecondsTimePoint.now();
TimePoint earlier = now.minus(1000000);
TimePoint later = now.plus(1000000); // first race from "earlier" to "now", second from "now" to "later", third from "later" to "finish"
TimePoint finish = later.plus(1000000);
Competitor[] c = createCompetitors(3).toArray(new Competitor[0]);
Competitor[] f1 = new Competitor[] { c[0], c[1], c[2] };
Competitor[] f2 = new Competitor[] { c[0], c[1], c[2] };
Competitor[] f3 = new Competitor[] { c[1], c[2], c[0] };
Regatta regatta = createRegatta(/* qualifying */0, new String[] { "Default" }, /* final */3, new String[] { "Default" },
/* medal */ false, "testTotalTimeNotCountedForRacesStartedLaterThanTimePointReqeusted",
DomainFactory.INSTANCE.getOrCreateBoatClass("49er", /* typicallyStartsUpwind */true), DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT));
Leaderboard leaderboard = createLeaderboard(regatta, /* discarding thresholds */ new int[0]);
@SuppressWarnings("unchecked")
Map<Competitor, TimePoint>[] lastMarkPassingTimesForCompetitors = (Map<Competitor, TimePoint>[]) new HashMap<?, ?>[3];
lastMarkPassingTimesForCompetitors[0] = new HashMap<>();
lastMarkPassingTimesForCompetitors[0].put(c[0], now);
lastMarkPassingTimesForCompetitors[0].put(c[1], now);
lastMarkPassingTimesForCompetitors[0].put(c[2], now);
lastMarkPassingTimesForCompetitors[1] = new HashMap<>();
lastMarkPassingTimesForCompetitors[1].put(c[0], later);
lastMarkPassingTimesForCompetitors[1].put(c[1], later);
lastMarkPassingTimesForCompetitors[1].put(c[2], later);
lastMarkPassingTimesForCompetitors[2] = new HashMap<>();
lastMarkPassingTimesForCompetitors[2].put(c[0], finish);
lastMarkPassingTimesForCompetitors[2].put(c[1], finish);
lastMarkPassingTimesForCompetitors[2].put(c[2], finish);
createAndAttachTrackedRacesWithStartTimeAndLastMarkPassingTimes(series.get(1), "Default",
new Competitor[][] { f1, f2, f3 }, new TimePoint[] { earlier, now, later }, lastMarkPassingTimesForCompetitors);
long totalTimeSailedC0_InRace1 = leaderboard.getTotalTimeSailedInMilliseconds(c[0], earlier.plus(1000));
assertEquals(1000l, totalTimeSailedC0_InRace1);
long totalTimeSailedC0_InRace2 = leaderboard.getTotalTimeSailedInMilliseconds(c[0], now.plus(1000));
assertEquals(now.asMillis()-earlier.asMillis() + 1000, totalTimeSailedC0_InRace2);
long totalTimeSailedC0_InRace3 = leaderboard.getTotalTimeSailedInMilliseconds(c[0], later.plus(1000));
assertEquals(later.asMillis()-earlier.asMillis() + 1000, totalTimeSailedC0_InRace3);
long totalTimeSailedC0_AtEndOfRace3 = leaderboard.getTotalTimeSailedInMilliseconds(c[0], finish);
assertEquals(finish.asMillis()-earlier.asMillis(), totalTimeSailedC0_AtEndOfRace3);
long totalTimeSailedC0_AfterRace3 = leaderboard.getTotalTimeSailedInMilliseconds(c[0], finish.plus(1000));
assertEquals(finish.asMillis()-earlier.asMillis(), totalTimeSailedC0_AfterRace3);
}
@Test
public void testTieBreakWithTwoVersusOneWins() throws NoWindException {
Competitor[] c = createCompetitors(3).toArray(new Competitor[0]);
@@ -644,8 +697,8 @@ public class LeaderboardScoringAndRankingTest extends AbstractLeaderboardTest {
leaderboard1.getFleet(null));
assertTrue(leaderboard3.getCompetitorsFromBestToWorst(later).indexOf(c[3]) <
leaderboard3.getCompetitorsFromBestToWorst(later).indexOf(c[0])); // c3 better than c0; won last race
LeaderboardGroup leaderboardGroup = new LeaderboardGroupImpl("Leaderboard Group", "Leaderboard Group", Arrays.asList(leaderboard1,
leaderboard2, leaderboard3));
LeaderboardGroup leaderboardGroup = new LeaderboardGroupImpl("Leaderboard Group", "Leaderboard Group", false, Arrays.asList(leaderboard1,
leaderboard2, leaderboard3));
leaderboardGroup.setOverallLeaderboard(new LeaderboardGroupMetaLeaderboard(leaderboardGroup, new HighPointExtremeSailingSeriesOverall(),
new ResultDiscardingRuleImpl(new int[0])));
leaderboardGroup.getOverallLeaderboard().setSuppressed(c[1], true);
@@ -670,6 +723,32 @@ public class LeaderboardScoringAndRankingTest extends AbstractLeaderboardTest {
return later;
}
private void createAndAttachTrackedRacesWithStartTimeAndLastMarkPassingTimes(
Series theSeries, String fleetName, Competitor[][] competitorLists, TimePoint[] startTimes,
Map<Competitor, TimePoint>[] lastMarkPassingTimesForCompetitors) {
Iterator<? extends RaceColumn> columnIter = theSeries.getRaceColumns().iterator();
int i=0;
for (Competitor[] competitorList : competitorLists) {
RaceColumn raceColumn = columnIter.next();
final Map<Competitor, TimePoint> lastMarkPassingTimes = lastMarkPassingTimesForCompetitors[i];
final Waypoint start = new WaypointImpl(new GateImpl(new BuoyImpl("Left StartBuoy"), new BuoyImpl("Right StartBuoy"), "Start"));
final Waypoint finish = new WaypointImpl(new BuoyImpl("FinishBuoy"));
TrackedRace trackedRace = new MockedTrackedRaceWithStartTimeAndRanks(startTimes[i], Arrays.asList(competitorList)) {
private static final long serialVersionUID = 1L;
@Override
public NavigableSet<MarkPassing> getMarkPassings(Competitor competitor) {
ArrayListNavigableSet<MarkPassing> result = new ArrayListNavigableSet<>(new TimedComparator());
result.add(new MarkPassingImpl(lastMarkPassingTimes.get(competitor), finish, competitor));
return result;
}
};
trackedRace.getRace().getCourse().addWaypoint(0, start);
trackedRace.getRace().getCourse().addWaypoint(1, finish);
raceColumn.setTrackedRace(raceColumn.getFleetByName(fleetName), trackedRace);
i++;
}
}
private List<Competitor> createCompetitors(int numberOfCompetitorsToCreate) {
List<Competitor> result = new ArrayList<Competitor>();
for (int i=1; i<=numberOfCompetitorsToCreate; i++) {
@@ -20,7 +20,6 @@ import org.junit.Test;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.impl.DouglasPeucker;
import com.sap.sailing.domain.base.impl.KnotSpeedWithBearingImpl;
import com.sap.sailing.domain.base.impl.MeterDistance;
import com.sap.sailing.domain.base.impl.MillisecondsTimePoint;
import com.sap.sailing.domain.common.ManeuverType;
import com.sap.sailing.domain.common.NoWindException;
@@ -28,6 +27,7 @@ import com.sap.sailing.domain.common.Tack;
import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.common.WindSourceType;
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
import com.sap.sailing.domain.common.impl.MeterDistance;
import com.sap.sailing.domain.common.impl.WindSourceImpl;
import com.sap.sailing.domain.tracking.GPSFixMoving;
import com.sap.sailing.domain.tracking.GPSFixTrack;
@@ -78,8 +78,7 @@ public class MockedTrackedRaceWithStartTimeAndRanks implements TrackedRace {
@Override
public TimePoint getStartOfRace() {
// TODO Auto-generated method stub
return null;
return startTime;
}
@Override
@@ -10,7 +10,7 @@ Require-Bundle: com.sap.sailing.domain,
com.googlecode.java-diff-utils;bundle-version="1.3.0",
com.sap.sailing.domain.common
Export-Package: com.sap.sailing.domain.tractracadapter,
com.sap.sailing.domain.tractracadapter.impl;x-friends:="com.sap.sailing.domain.test"
com.sap.sailing.domain.tractracadapter.impl;x-friends:="com.sap.sailing.domain.test,com.sap.sailing.domain.swisstimingadapter.test"
Bundle-ActivationPolicy: lazy
Import-Package: org.json.simple,
org.json.simple.parser
@@ -108,11 +108,11 @@ public interface RaceColumn extends Named {
*/
Pair<Competitor, RaceColumn> getKey(Competitor competitor);
public abstract RaceColumnListeners getRaceColumnListeners();
RaceColumnListeners getRaceColumnListeners();
public abstract void removeRaceColumnListener(RaceColumnListener listener);
void removeRaceColumnListener(RaceColumnListener listener);
public abstract void addRaceColumnListener(RaceColumnListener listener);
void addRaceColumnListener(RaceColumnListener listener);
/**
* Releases the {@link TrackedRace} previously set by {@link #setTrackedRace(Fleet, TrackedRace)} but leaves the
@@ -123,4 +123,24 @@ public interface RaceColumn extends Named {
* the fleet for which to release its tracked race
*/
void releaseTrackedRace(Fleet fleet);
/**
* Usually, the scores in each leaderboard column count as they are for the overall score. However, if a column is
* a medal race column it usually counts double. Under certain circumstances, columns may also count with factors different
* from 1 or 2. For example, we've seen cases in the Extreme Sailing Series where the race committee defined that in the
* overall series leaderboard the last two columns each count 1.5 times their scores.
*/
double getFactor();
/**
* @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>.
*/
void setFactor(Double factor);
/**
* 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.
*/
Double getExplicitFactor();
}
@@ -17,6 +17,10 @@ public interface RaceColumnListener extends Serializable {
void raceColumnRemovedFromContainer(RaceColumn raceColumn);
void raceColumnMoved(RaceColumn raceColumn, int newIndex);
void factorChanged(RaceColumn raceColumn, Double oldFactor, Double newFactor);
/**
* A listener can use this to specify that it must not be serialized together with other listeners.
* Background: the collections holding race column listeners are usually non-transient. This will cause
@@ -3,6 +3,7 @@ package com.sap.sailing.domain.base.impl;
import com.sap.sailing.domain.base.BoatClass;
import com.sap.sailing.domain.base.DomainFactory;
import com.sap.sailing.domain.common.Distance;
import com.sap.sailing.domain.common.impl.MeterDistance;
import com.sap.sailing.domain.common.impl.NamedImpl;
public class BoatClassImpl extends NamedImpl implements BoatClass {
@@ -48,7 +48,7 @@ public class BuoyImpl extends NamedImpl implements Buoy {
@Override
public Buoy resolve(DomainFactory domainFactory) {
Buoy result = domainFactory.getOrCreateBuoy(getName());
Buoy result = domainFactory.getOrCreateBuoy(getName(), getDisplayColor());
return result;
}
@@ -230,6 +230,16 @@ public class RegattaImpl extends NamedImpl implements Regatta, RaceColumnListene
raceColumnListeners.notifyListenersAboutRaceColumnRemovedFromContainer(raceColumn);
}
@Override
public void raceColumnMoved(RaceColumn raceColumn, int newIndex) {
raceColumnListeners.notifyListenersAboutRaceColumnMoved(raceColumn, newIndex);
}
@Override
public void factorChanged(RaceColumn raceColumn, Double oldFactor, Double newFactor) {
raceColumnListeners.notifyListenersAboutFactorChanged(raceColumn, oldFactor, newFactor);
}
@Override
public boolean isTransient() {
return false;
@@ -223,6 +223,16 @@ public class SeriesImpl extends NamedImpl implements Series, RaceColumnListener
raceColumnListeners.notifyListenersAboutRaceColumnRemovedFromContainer(raceColumn);
}
@Override
public void raceColumnMoved(RaceColumn raceColumn, int newIndex) {
raceColumnListeners.notifyListenersAboutRaceColumnMoved(raceColumn, newIndex);
}
@Override
public void factorChanged(RaceColumn raceColumn, Double oldFactor, Double newFactor) {
raceColumnListeners.notifyListenersAboutFactorChanged(raceColumn, oldFactor, newFactor);
}
@Override
public boolean isTransient() {
return false;
@@ -13,6 +13,19 @@ public abstract class SimpleAbstractRaceColumn implements RaceColumn {
private static final long serialVersionUID = -3590156714385187908L;
private final RaceColumnListeners raceColumnListeners;
/**
* The factor by which a medal race score is multiplied by default in the overall point scheme.
*
* @see #getFactor()
*/
private static final double DEFAULT_MEDAL_RACE_FACTOR = 2.0;
/**
* 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.
*/
private Double explicitFactor;
public SimpleAbstractRaceColumn() {
raceColumnListeners = new RaceColumnListeners();
}
@@ -45,4 +58,21 @@ public abstract class SimpleAbstractRaceColumn implements RaceColumn {
}
return result;
}
@Override
public double getFactor() {
return explicitFactor == null ? (isMedalRace() ? DEFAULT_MEDAL_RACE_FACTOR : 1.) : explicitFactor;
}
@Override
public void setFactor(Double factor) {
Double oldExplicitFactor = getExplicitFactor();
explicitFactor = factor;
raceColumnListeners.notifyListenersAboutFactorChanged(this, oldExplicitFactor, factor);
}
@Override
public Double getExplicitFactor() {
return explicitFactor;
}
}
@@ -15,6 +15,8 @@ public interface LeaderboardGroup extends Renamable {
String getDescription();
void setDescriptiom(String description);
boolean isDisplayGroupsInReverseOrder();
Iterable<Leaderboard> getLeaderboards();
int getIndexOf(Leaderboard leaderboard);
void addLeaderboard(Leaderboard leaderboard);
@@ -47,11 +47,6 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
static final Double DOUBLE_0 = new Double(0);
/**
* The factor by which a medal race score is multiplied in the overall point scheme
*/
static final double MEDAL_RACE_FACTOR = 2.0;
private final SettableScoreCorrection scoreCorrection;
private ThresholdBasedResultDiscardingRule resultDiscardingRule;
@@ -232,7 +227,7 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
if (netPoints == null) {
result = null;
} else {
result = (raceColumn.isMedalRace() ? MEDAL_RACE_FACTOR : 1.0) * netPoints;
result = raceColumn.getFactor() * netPoints;
}
}
return result;
@@ -367,7 +362,7 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
final Double correctedScore = correctedResults.getCorrectedScore();
return new EntryImpl(trackedRankProvider, correctedScore, correctedResults.isCorrected(),
discarded ? DOUBLE_0
: correctedScore == null ? null : Double.valueOf(correctedScore * (race.isMedalRace() ? MEDAL_RACE_FACTOR : 1.0)),
: correctedScore == null ? null : Double.valueOf(correctedScore * race.getFactor()),
correctedResults.getMaxPointsReason(), discarded, race.getFleetOfCompetitor(competitor));
}
@@ -394,7 +389,7 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
final Double correctedScore = correctedResults.getCorrectedScore();
Entry entry = new EntryImpl(trackedRankProvider, correctedScore,
correctedResults.isCorrected(), discarded ? DOUBLE_0 : (correctedScore==null?null:
Double.valueOf((correctedScore * (raceColumn.isMedalRace() ? MEDAL_RACE_FACTOR : 1.0)))),
Double.valueOf((correctedScore * raceColumn.getFactor()))),
correctedResults.getMaxPointsReason(), discarded,
raceColumn.getFleetOfCompetitor(competitor));
result.put(new Pair<Competitor, RaceColumn>(competitor, raceColumn), entry);
@@ -418,6 +413,16 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
getRaceColumnListeners().notifyListenersAboutIsMedalRaceChanged(raceColumn, newIsMedalRace);
}
@Override
public void raceColumnMoved(RaceColumn raceColumn, int newIndex) {
getRaceColumnListeners().notifyListenersAboutRaceColumnMoved(raceColumn, newIndex);
}
@Override
public void factorChanged(RaceColumn raceColumn, Double oldFactor, Double newFactor) {
getRaceColumnListeners().notifyListenersAboutFactorChanged(raceColumn, oldFactor, newFactor);
}
/**
* A leaderboard will only accept the addition of a race column if the column's name is unique across the leaderboard.
*/
@@ -566,24 +571,29 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
NavigableSet<MarkPassing> markPassings = trackedRace.getMarkPassings(competitor);
if (!markPassings.isEmpty()) {
TimePoint from = trackedRace.getStartOfRace(); // start counting at race start, not when the competitor passed the line
TimePoint to;
if (timePoint.after(markPassings.last().getTimePoint()) &&
markPassings.last().getWaypoint() == trackedRace.getRace().getCourse().getLastWaypoint()) {
// stop counting when competitor finished the race
to = markPassings.last().getTimePoint();
} else {
if (trackedRace.getEndOfTracking() != null && timePoint.after(trackedRace.getEndOfTracking())) {
result = null; // race not finished until end of tracking; no reasonable value can be computed for competitor
break;
if (!timePoint.before(from)) { // but only if the race started after timePoint
TimePoint to;
if (timePoint.after(markPassings.last().getTimePoint())
&& markPassings.last().getWaypoint() == trackedRace.getRace().getCourse()
.getLastWaypoint()) {
// stop counting when competitor finished the race
to = markPassings.last().getTimePoint();
} else {
to = timePoint;
if (trackedRace.getEndOfTracking() != null
&& timePoint.after(trackedRace.getEndOfTracking())) {
result = null; // race not finished until end of tracking; no reasonable value can be
// computed for competitor
break;
} else {
to = timePoint;
}
}
long timeSpent = to.asMillis() - from.asMillis();
if (result == null) {
result = timeSpent;
} else {
result += timeSpent;
}
}
long timeSpent = to.asMillis() - from.asMillis();
if (result == null) {
result = timeSpent;
} else {
result += timeSpent;
}
}
}
@@ -10,8 +10,8 @@ public class EventLeaderboardImpl extends LeaderboardGroupImpl implements EventL
private static final long serialVersionUID = -8933204023833087837L;
private Event event;
public EventLeaderboardImpl(Event event, String name, String description, List<Leaderboard> leaderboards) {
super(name, description, leaderboards);
public EventLeaderboardImpl(Event event, String name, String description, boolean displayGroupsInReverseOrder, List<Leaderboard> leaderboards) {
super(name, description, displayGroupsInReverseOrder, leaderboards);
this.event = event;
}
@@ -140,15 +140,15 @@ public class FlexibleLeaderboardImpl extends AbstractLeaderboardImpl implements
race = r;
}
}
if (race == null) {
return;
}
int index = 0;
index = races.lastIndexOf(race);
index--;
if (index >= 0) {
races.remove(race);
races.add(index, race);
if (race != null) {
int index = 0;
index = races.lastIndexOf(race);
index--;
if (index >= 0) {
races.remove(race);
races.add(index, race);
getRaceColumnListeners().notifyListenersAboutRaceColumnMoved(race, index);
}
}
}
@@ -160,18 +160,17 @@ public class FlexibleLeaderboardImpl extends AbstractLeaderboardImpl implements
race = r;
}
}
if (race == null) {
return;
}
int index = 0;
index = races.lastIndexOf(race);
if (index == -1) {
return;
}
index++;
if (index < races.size()) {
races.remove(race);
races.add(index, race);
if (race != null) {
int index = 0;
index = races.lastIndexOf(race);
if (index != -1) {
index++;
if (index < races.size()) {
races.remove(race);
races.add(index, race);
getRaceColumnListeners().notifyListenersAboutRaceColumnMoved(race, index);
}
}
}
}
@@ -5,7 +5,10 @@ import java.util.List;
import com.sap.sailing.domain.common.ScoringSchemeType;
/**
* In this variant of the {@link HighPoint}
* In this variant of the {@link HighPoint} scoring scheme, scoring by "better race" (sometimes referred to as a
* "back run" where the races are sorted with best races first and the first difference is counted) is disabled, such
* that even if the last race is not a medeal race, the last race score decides the ordering.
*
* @author Axel Uhl (d043530)
*
*/
@@ -15,6 +15,7 @@ public class LeaderboardGroupImpl implements LeaderboardGroup {
private static final long serialVersionUID = 2035927369446736934L;
private String name;
private String description;
private boolean displayGroupsInReverseOrder;
private final List<Leaderboard> leaderboards;
private final Set<LeaderboardGroupListener> listeners;
@@ -25,9 +26,10 @@ public class LeaderboardGroupImpl implements LeaderboardGroup {
*/
private Leaderboard overallLeaderboard;
public LeaderboardGroupImpl(String name, String description, List<? extends Leaderboard> leaderboards) {
public LeaderboardGroupImpl(String name, String description, boolean displayGroupsInReverseOrder, List<? extends Leaderboard> leaderboards) {
this.name = name;
this.description = description;
this.displayGroupsInReverseOrder = displayGroupsInReverseOrder;
this.leaderboards = new ArrayList<Leaderboard>(leaderboards);
this.listeners = new HashSet<LeaderboardGroupListener>();
}
@@ -140,4 +142,8 @@ public class LeaderboardGroupImpl implements LeaderboardGroup {
}
}
@Override
public boolean isDisplayGroupsInReverseOrder() {
return displayGroupsInReverseOrder;
}
}
@@ -36,7 +36,7 @@ import com.sap.sailing.domain.tracking.TrackedRace;
* Instances of subclasses need to register a {@link ScoreCorrectionChangeForwarder} as listener on each leaderboard
* referenced by this meta leaderboard to ensure that score corrections applied to the "inner" leaderboard are signaled
* to {@link ScoreCorrectionListeners} on this meta leaderboard's score correction. See
* {@link #registerScoreCorrectionChangeForwarder(Leaderboard)} and
* {@link #registerScoreCorrectionAndRaceColumnChangeForwarder(Leaderboard)} and
* {@link #unregisterScoreCorrectionChangeForwarder(Leaderboard)}.
*
* @author Axel Uhl (d043530)
@@ -130,12 +130,14 @@ public abstract class AbstractMetaLeaderboard extends AbstractSimpleLeaderboardI
RaceColumn result = columnsForLeaderboards.get(leaderboard);
if (result == null) {
result = new MetaLeaderboardColumn(leaderboard, metaFleet);
result.addRaceColumnListener(this);
columnsForLeaderboards.put(leaderboard, result);
}
return result;
}
protected void registerScoreCorrectionChangeForwarder(Leaderboard leaderboard) {
protected void registerScoreCorrectionAndRaceColumnChangeForwarder(Leaderboard leaderboard) {
leaderboard.addRaceColumnListener(this);
final ScoreCorrectionChangeForwarder listener = new ScoreCorrectionChangeForwarder();
scoreCorrectionChangeForwardersByLeaderboard.put(leaderboard, listener);
leaderboard.getScoreCorrection().addScoreCorrectionListener(listener);
@@ -38,7 +38,7 @@ public class FlexibleMetaLeaderboard extends AbstractMetaLeaderboard {
LockUtil.lockForWrite(leaderboardsLock);
try {
leaderboards.add(leaderboard);
registerScoreCorrectionChangeForwarder(leaderboard);
registerScoreCorrectionAndRaceColumnChangeForwarder(leaderboard);
getRaceColumnListeners().notifyListenersAboutRaceColumnAddedToContainer(getColumnForLeaderboard(leaderboard));
} finally {
LockUtil.unlockAfterWrite(leaderboardsLock);
@@ -20,9 +20,9 @@ import com.sap.sailing.domain.tracking.TrackedRace;
* <p>
*
* After an object of this type has been de-serialized, and after all objects referenced by it (leaderboard, leaderboard
* group) have been initialized, {@link #registerAsScoreCorrectionChangeForwarderOnAllLeaderboards()} must be called to
* group) have been initialized, {@link #registerAsScoreCorrectionChangeForwarderAndRaceColumnListenerOnAllLeaderboards()} must be called to
* ensure that score corrections are propagated as desired. This is because score correction listeners are "transient"
* and are as such not serialized. Note: calling {@link #registerAsScoreCorrectionChangeForwarderOnAllLeaderboards()}
* and are as such not serialized. Note: calling {@link #registerAsScoreCorrectionChangeForwarderAndRaceColumnListenerOnAllLeaderboards()}
* during <code>readObject</code> or <code>readResolve</code> is not possible because the object graph hasn't been fully
* initialized yet when those methods are called. Hence, the structures for registering the score correction listeners
* are not yet in place at that time.
@@ -40,13 +40,12 @@ public class LeaderboardGroupMetaLeaderboard extends AbstractMetaLeaderboard imp
super(leaderboardGroup.getName()+" "+LeaderboardNameConstants.OVERALL, scoringScheme, resultDiscardingRule);
this.leaderboardGroup = leaderboardGroup;
leaderboardGroup.addLeaderboardGroupListener(this);
registerAsScoreCorrectionChangeForwarderOnAllLeaderboards();
registerAsScoreCorrectionChangeForwarderAndRaceColumnListenerOnAllLeaderboards();
}
public void registerAsScoreCorrectionChangeForwarderOnAllLeaderboards() {
public void registerAsScoreCorrectionChangeForwarderAndRaceColumnListenerOnAllLeaderboards() {
for (Leaderboard leaderboard : leaderboardGroup.getLeaderboards()) {
leaderboard.addRaceColumnListener(this);
registerScoreCorrectionChangeForwarder(leaderboard);
registerScoreCorrectionAndRaceColumnChangeForwarder(leaderboard);
}
}
@@ -57,8 +56,7 @@ public class LeaderboardGroupMetaLeaderboard extends AbstractMetaLeaderboard imp
@Override
public void leaderboardAdded(LeaderboardGroup group, Leaderboard leaderboard) {
leaderboard.addRaceColumnListener(this);
registerScoreCorrectionChangeForwarder(leaderboard);
registerScoreCorrectionAndRaceColumnChangeForwarder(leaderboard);
for (RaceColumn raceColumn : leaderboard.getRaceColumns()) {
for (Fleet fleet : raceColumn.getFleets()) {
TrackedRace trackedRace = raceColumn.getTrackedRace(fleet);
@@ -39,14 +39,6 @@ public class MetaLeaderboardColumn extends SimpleAbstractRaceColumn implements R
return leaderboard.getName();
}
@Override
public void addRaceColumnListener(RaceColumnListener listener) {
}
@Override
public void removeRaceColumnListener(RaceColumnListener listener) {
}
@Override
public Iterable<? extends Fleet> getFleets() {
return Collections.singleton(metaFleet);
@@ -129,6 +121,16 @@ public class MetaLeaderboardColumn extends SimpleAbstractRaceColumn implements R
getRaceColumnListeners().notifyListenersAboutRaceColumnRemovedFromContainer(raceColumn);
}
@Override
public void raceColumnMoved(RaceColumn raceColumn, int newIndex) {
getRaceColumnListeners().notifyListenersAboutRaceColumnMoved(raceColumn, newIndex);
}
@Override
public void factorChanged(RaceColumn raceColumn, Double oldFactor, Double newFactor) {
getRaceColumnListeners().notifyListenersAboutFactorChanged(raceColumn, oldFactor, newFactor);
}
@Override
public boolean isTransient() {
return false;
@@ -1,6 +1,7 @@
package com.sap.sailing.domain.tracking;
import com.sap.sailing.domain.base.SpeedWithBearing;
import com.sap.sailing.domain.common.Distance;
import com.sap.sailing.domain.common.ManeuverType;
import com.sap.sailing.domain.common.Tack;
@@ -15,4 +16,6 @@ public interface Maneuver extends GPSFix {
double getDirectionChangeInDegrees();
Distance getManeuverLoss();
}
@@ -6,6 +6,7 @@ import java.util.List;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Leg;
import com.sap.sailing.domain.base.SpeedWithBearing;
import com.sap.sailing.domain.base.impl.MillisecondsTimePoint;
import com.sap.sailing.domain.common.Distance;
import com.sap.sailing.domain.common.LegType;
import com.sap.sailing.domain.common.NoWindException;
@@ -13,6 +14,7 @@ import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.Speed;
import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.common.impl.Util.Pair;
import com.sap.sailing.domain.tracking.impl.TrackedLegImpl;
public interface TrackedLegOfCompetitor extends Serializable {
Leg getLeg();
@@ -169,6 +171,26 @@ public interface TrackedLegOfCompetitor extends Serializable {
Distance getAverageCrossTrackError(TimePoint timePoint, boolean waitForLatestAnalysis) throws NoWindException;
/**
* If the current {@link #getLeg() leg} is +/- {@link TrackedLegImpl#UPWIND_DOWNWIND_TOLERANCE_IN_DEG} degrees
* co-linear with the wind's bearing, the competitor's position is projected onto the line crossing
* <code>buoy</code> in the wind's bearing, and the distance from the projection to the <code>buoy</code> is
* returned. Otherwise, it is assumed that the leg is neither an upwind nor a downwind leg, and hence the along-track
* distance to <code>buoy</code> is returned.
*
* @param at
* the wind estimation is performed for this point in time
*/
Distance getWindwardDistance(Position pos1, Position pos2, TimePoint at) throws NoWindException;
/**
* Computes the maneuver loss as the "windward distance" that the boat lost compared to not having maneuvered.
* As defined by {@link TrackedLegOfCompetitor#getWindwardDistance(Position, Position, TimePoint)}, distances for
* reaching legs are computed as the along-track distance. For upwind/downwind legs it's taken to be the
* along-wind projection. With this distance measure, the competitors speed and bearing before the maneuver,
* as defined by <code>timePointBeforeManeuver</code> is extrapolated until <code>timePointAfterManeuver</code>,
* and the resulting extrapolated position's "windward distance" is computed to the competitor's actual position
* at that time. This distance is returned as the result of this method.
*/
Distance getManeuverLoss(MillisecondsTimePoint timePointBeforeManeuver, MillisecondsTimePoint timePointAfterManeuver) throws NoWindException;
}
@@ -11,11 +11,11 @@ import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Leg;
import com.sap.sailing.domain.base.Timed;
import com.sap.sailing.domain.base.Waypoint;
import com.sap.sailing.domain.base.impl.MeterDistance;
import com.sap.sailing.domain.common.Distance;
import com.sap.sailing.domain.common.LegType;
import com.sap.sailing.domain.common.NoWindException;
import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.common.impl.MeterDistance;
import com.sap.sailing.domain.tracking.GPSFix;
import com.sap.sailing.domain.tracking.GPSFixMoving;
import com.sap.sailing.domain.tracking.GPSFixTrack;
@@ -1,12 +1,17 @@
package com.sap.sailing.domain.tracking.impl;
import com.sap.sailing.domain.base.SpeedWithBearing;
import com.sap.sailing.domain.common.Distance;
import com.sap.sailing.domain.common.ManeuverType;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.Tack;
import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.tracking.Maneuver;
/**
* @author Axel Uhl (d043530)
*
*/
public class ManeuverImpl extends AbstractGPSFixImpl implements Maneuver {
private static final long serialVersionUID = -5317959066507472580L;
private final ManeuverType type;
@@ -16,9 +21,10 @@ public class ManeuverImpl extends AbstractGPSFixImpl implements Maneuver {
private final SpeedWithBearing speedWithBearingBefore;
private final SpeedWithBearing speedWithBearingAfter;
private final double directionChangeInDegrees;
private final Distance maneuverLoss;
public ManeuverImpl(ManeuverType type, Tack newTack, Position position, TimePoint timePoint, SpeedWithBearing speedWithBearingBefore,
SpeedWithBearing speedWithBearingAfter, double directionChangeInDegrees) {
SpeedWithBearing speedWithBearingAfter, double directionChangeInDegrees, Distance maneuverLoss) {
super();
this.type = type;
this.newTack = newTack;
@@ -27,6 +33,7 @@ public class ManeuverImpl extends AbstractGPSFixImpl implements Maneuver {
this.speedWithBearingBefore = speedWithBearingBefore;
this.speedWithBearingAfter = speedWithBearingAfter;
this.directionChangeInDegrees = directionChangeInDegrees;
this.maneuverLoss = maneuverLoss;
}
@Override
@@ -64,11 +71,16 @@ public class ManeuverImpl extends AbstractGPSFixImpl implements Maneuver {
return newTack;
}
@Override
public Distance getManeuverLoss() {
return maneuverLoss;
}
@Override
public String toString() {
return super.toString() + " " + type + " on new tack " + newTack + " on position " + position
+ " at time point " + timePoint + ". " + "Speed before maneuver " + speedWithBearingBefore
+ " speed after maneuver " + speedWithBearingAfter + ". The maneuver changed the course by "
+ directionChangeInDegrees + "deg.";
+ directionChangeInDegrees + "deg." + (getManeuverLoss() == null ? "" : " Lost approximately "+getManeuverLoss());
}
}
@@ -35,7 +35,7 @@ public class TrackedLegImpl implements TrackedLeg, RaceChangeListener {
private final static Logger logger = Logger.getLogger(TrackedLegImpl.class.getName());
private final static double UPWIND_DOWNWIND_TOLERANCE_IN_DEG = 45; // TracTrac does 22.5, Marcus Baur suggest 40; Nils Schröder suggests 60
private final static double UPWIND_DOWNWIND_TOLERANCE_IN_DEG = 45; // TracTrac does 22.5, Marcus Baur suggest 40; Nils Schr�der suggests 60
private final Leg leg;
private final Map<Competitor, TrackedLegOfCompetitor> trackedLegsOfCompetitors;
@@ -135,7 +135,8 @@ public class TrackedLegImpl implements TrackedLeg, RaceChangeListener {
public LegType getLegType(TimePoint at) throws NoWindException {
Wind wind = getWindOnLeg(at);
if (wind == null) {
throw new NoWindException("Need to know wind direction to determine whether leg "+getLeg()+
throw new NoWindException("Need to know wind direction in race "+getTrackedRace().getRace().getName()+
" to determine whether leg "+getLeg()+
" is an upwind or downwind leg");
}
Bearing legBearing = getLegBearing(at);
@@ -8,7 +8,7 @@ import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Leg;
import com.sap.sailing.domain.base.SpeedWithBearing;
import com.sap.sailing.domain.base.impl.KnotSpeedWithBearingImpl;
import com.sap.sailing.domain.base.impl.MeterDistance;
import com.sap.sailing.domain.base.impl.MillisecondsTimePoint;
import com.sap.sailing.domain.common.Bearing;
import com.sap.sailing.domain.common.Distance;
import com.sap.sailing.domain.common.ManeuverType;
@@ -16,6 +16,7 @@ import com.sap.sailing.domain.common.NoWindException;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.Speed;
import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.common.impl.MeterDistance;
import com.sap.sailing.domain.common.impl.Util.Pair;
import com.sap.sailing.domain.tracking.GPSFixMoving;
import com.sap.sailing.domain.tracking.GPSFixTrack;
@@ -196,10 +197,10 @@ public class TrackedLegOfCompetitorImpl implements TrackedLegOfCompetitor {
}
/**
* If the current {@link #getLeg() leg} is +/- {@link #UPWIND_DOWNWIND_TOLERANCE_IN_DEG} degrees collinear with the
* If the current {@link #getLeg() leg} is +/- {@link TrackedLegImpl#UPWIND_DOWNWIND_TOLERANCE_IN_DEG} degrees collinear with the
* wind's bearing, the competitor's position is projected onto the line crossing <code>buoy</code> in the wind's
* bearing, and the distance from the projection to the <code>buoy</code> is returned. Otherwise, it is assumed that
* the leg is neither an upwind nor a downwind leg, and hence the true distance to <code>buoy</code> is returned.
* the leg is neither an upwind nor a downwind leg, and hence the along-track distance to <code>buoy</code> is returned.
*
* @param at the wind estimation is performed for this point in time
*/
@@ -578,4 +579,37 @@ public class TrackedLegOfCompetitorImpl implements TrackedLegOfCompetitor {
return result;
}
@Override
public Distance getManeuverLoss(MillisecondsTimePoint timePointBeforeManeuver,
MillisecondsTimePoint timePointAfterManeuver) throws NoWindException {
assert timePointBeforeManeuver != null;
assert timePointAfterManeuver != null;
Distance result;
long millisecondsOverWhichToAverageSpeed = getTrackedRace().getMillisecondsOverWhichToAverageSpeed();
TimePoint extrapolationBase = timePointBeforeManeuver.minus(millisecondsOverWhichToAverageSpeed/2);
final GPSFixTrack<Competitor, GPSFixMoving> track = getTrackedRace().getTrack(getCompetitor());
Position estimatedPositionAtBase = track.getEstimatedPosition(extrapolationBase, /* extrapolate */ false);
if (estimatedPositionAtBase != null) {
SpeedWithBearing estimatedSpeedAtBase = track.getEstimatedSpeed(extrapolationBase);
if (estimatedSpeedAtBase != null) {
TimePoint extrapolationTarget = timePointAfterManeuver.plus(millisecondsOverWhichToAverageSpeed / 2);
Position extrapolatedPositionWithoutManeuver = estimatedSpeedAtBase.travelTo(estimatedPositionAtBase,
extrapolationBase, extrapolationTarget);
Position estimatedActualPositionAfterManeuver = track.getEstimatedPosition(extrapolationTarget, /* extrapolate */
false);
if (estimatedActualPositionAfterManeuver != null) {
result = getWindwardDistance(estimatedActualPositionAfterManeuver, extrapolatedPositionWithoutManeuver,
new MillisecondsTimePoint((timePointBeforeManeuver.asMillis() + timePointAfterManeuver.asMillis())/2));
} else {
result = null;
}
} else {
result = null;
}
} else {
result = null;
}
return result;
}
}
@@ -92,7 +92,7 @@ public abstract class TrackedRaceImpl implements TrackedRace, CourseListener {
private static final Logger logger = Logger.getLogger(TrackedRaceImpl.class.getName());
private static final double PENALTY_CIRCLE_DEGREES_THRESHOLD = 320;
private static final double PENALTY_CIRCLE_DEGREES_THRESHOLD = 270;
// TODO make this variable
private static final long DELAY_FOR_CACHE_CLEARING_IN_MILLISECONDS = 7500;
@@ -1075,9 +1075,9 @@ public abstract class TrackedRaceImpl implements TrackedRace, CourseListener {
@Override
public boolean hasGPSData() {
boolean result = false;
if(!tracks.values().isEmpty()) {
for(GPSFixTrack<Competitor, GPSFixMoving> gpsTrack: tracks.values()) {
if(gpsTrack.getFirstRawFix() != null) {
if (!tracks.values().isEmpty()) {
for (GPSFixTrack<Competitor, GPSFixMoving> gpsTrack : tracks.values()) {
if (gpsTrack.getFirstRawFix() != null) {
result = true;
break;
}
@@ -1379,7 +1379,7 @@ public abstract class TrackedRaceImpl implements TrackedRace, CourseListener {
}
@Override
public void waypointRemoved(int zeroBasedIndex, Waypoint waypointThatGotRemoved) {
public void waypointRemoved(int zeroBasedIndex, Waypoint waypointThatGotRemoved) {
invalidateMarkPassingTimes();
LockUtil.lockForRead(serializationLock);
try {
@@ -1402,6 +1402,7 @@ public abstract class TrackedRaceImpl implements TrackedRace, CourseListener {
toRemove = last;
}
if (toRemove != null) {
logger.info("Removing tracked leg starting at "+toRemove.getFrom()+" from tracked race "+getRace().getName());
trackedLegs.remove(toRemove);
updated(/* time point */null);
}
@@ -1846,11 +1847,8 @@ public abstract class TrackedRaceImpl implements TrackedRace, CourseListener {
List<Pair<GPSFixMoving, CourseChange>> group = new ArrayList<Pair<GPSFixMoving, CourseChange>>();
if (!courseChangeSequenceInSameDirection.isEmpty()) {
Distance twoHullLengths = competitor.getBoat().getBoatClass().getHullLength().scale(2);
SpeedWithBearing beforeGroupOnApproximation = speedWithBearingOnApproximationAtBeginning; // speed/bearing
// before group
SpeedWithBearing beforeCurrentCourseChangeOnApproximation = beforeGroupOnApproximation; // speed/bearing
// before current
// course change
SpeedWithBearing beforeGroupOnApproximation = speedWithBearingOnApproximationAtBeginning; // speed/bearing before group
SpeedWithBearing beforeCurrentCourseChangeOnApproximation = beforeGroupOnApproximation; // speed/bearing before current course change
Iterator<Pair<GPSFixMoving, CourseChange>> iter = courseChangeSequenceInSameDirection.iterator();
double totalCourseChangeInDegrees = 0.0;
long totalMilliseconds = 0l;
@@ -1909,17 +1907,21 @@ public abstract class TrackedRaceImpl implements TrackedRace, CourseListener {
Tack tackAfterManeuver = getTack(maneuverPosition, timePointAfterManeuver,
speedWithBearingOnApproximationAtEnd.getBearing());
ManeuverType maneuverType;
Distance maneuverLoss = null;
// the TrackedLegOfCompetitor variables may be null, e.g., in case the time points are before or after the race
TrackedLegOfCompetitor legBeforeManeuver = getTrackedLeg(competitor, timePointBeforeManeuver);
TrackedLegOfCompetitor legAfterManeuver = getTrackedLeg(competitor, timePointAfterManeuver);
if (Math.abs(totalCourseChangeInDegrees) > PENALTY_CIRCLE_DEGREES_THRESHOLD) {
maneuverType = ManeuverType.PENALTY_CIRCLE;
} else if (legBeforeManeuver != legAfterManeuver
&&
// check for mask passing first; a tacking / jibe-setting mark rounding thus takes precedence over being detected as a penalty circle
if (legBeforeManeuver != legAfterManeuver
// a maneuver at the start line is not to be considered a MARK_PASSING maneuver; show a tack as a tack
legAfterManeuver != null
&& legAfterManeuver != null
&& legAfterManeuver.getLeg().getFrom() != getRace().getCourse().getFirstWaypoint()) {
maneuverType = ManeuverType.MARK_PASSING;
} else if (Math.abs(totalCourseChangeInDegrees) > PENALTY_CIRCLE_DEGREES_THRESHOLD) {
maneuverType = ManeuverType.PENALTY_CIRCLE;
if (legBeforeManeuver != null) {
maneuverLoss = legBeforeManeuver.getManeuverLoss(timePointBeforeManeuver, timePointAfterManeuver);
}
} else {
if (tackBeforeManeuver != tackAfterManeuver) {
LegType legType = legBeforeManeuver != null ? getTrackedLeg(legBeforeManeuver.getLeg()).getLegType(
@@ -1930,9 +1932,15 @@ public abstract class TrackedRaceImpl implements TrackedRace, CourseListener {
switch (legType) {
case UPWIND:
maneuverType = ManeuverType.TACK;
if (legBeforeManeuver != null) {
maneuverLoss = legBeforeManeuver.getManeuverLoss(timePointBeforeManeuver, timePointAfterManeuver);
}
break;
case DOWNWIND:
maneuverType = ManeuverType.JIBE;
if (legBeforeManeuver != null) {
maneuverLoss = legBeforeManeuver.getManeuverLoss(timePointBeforeManeuver, timePointAfterManeuver);
}
break;
default:
maneuverType = ManeuverType.UNKNOWN;
@@ -1963,7 +1971,7 @@ public abstract class TrackedRaceImpl implements TrackedRace, CourseListener {
}
Maneuver maneuver = new ManeuverImpl(maneuverType, tackAfterManeuver, maneuverPosition, maneuverTimePoint,
speedWithBearingOnApproximationAtBeginning, speedWithBearingOnApproximationAtEnd,
totalCourseChangeInDegrees);
totalCourseChangeInDegrees, maneuverLoss);
return maneuver;
}
@@ -69,6 +69,12 @@ public class RaceColumnListeners implements Serializable {
}
}
public void notifyListenersAboutFactorChanged(RaceColumn raceColumn, Double oldFactor, Double newFactor) {
for (RaceColumnListener listener : getRaceColumnListeners()) {
listener.factorChanged(raceColumn, oldFactor, newFactor);
}
}
public void notifyListenersAboutIsMedalRaceChanged(RaceColumn raceColumn, boolean newIsMedalRace) {
for (RaceColumnListener listener : getRaceColumnListeners()) {
listener.isMedalRaceChanged(raceColumn, newIsMedalRace);
@@ -87,6 +93,12 @@ public class RaceColumnListeners implements Serializable {
}
}
public void notifyListenersAboutRaceColumnMoved(RaceColumn raceColumn, int newIndex) {
for (RaceColumnListener listener : getRaceColumnListeners()) {
listener.raceColumnMoved(raceColumn, newIndex);
}
}
private Set<RaceColumnListener> getRaceColumnListeners() {
synchronized (raceColumnListeners) {
return new HashSet<RaceColumnListener>(raceColumnListeners);
@@ -103,4 +115,5 @@ public class RaceColumnListeners implements Serializable {
}
return result;
}
}
@@ -17,9 +17,26 @@ import com.sap.sailing.util.impl.SmartFutureCache.UpdateInterval;
* A cache for which a background update can be triggered. Readers can decide whether they want to wait for any ongoing
* background update or read the latest cached value for a key. An update trigger can provide an optional parameter for
* the update which may, e.g., control the interval of the cached value to update. When an update is triggered and
* another update is already running, the update is queued. If there already is an update queued for the same key,
* the optional update parameters are "joined" (for example, the two update intervals are joined to form one interval
* which incorporates both original update intervals).
* another update is already running, the update is queued. If there already is an update queued for the same key, the
* optional update parameters are "joined" (for example, the two update intervals are joined to form one interval which
* incorporates both original update intervals).
* <p>
*
* A {@link CacheUpdater} needs to be passed to the constructor which carries out the actual calculation whose values
* are to be cached. The {@link CacheUpdater} interface assumes that a cache update may be computed in two steps: first,
* a value is computed for a key and an update interval which may be computationally expensive. Then, in a second step,
* the new value is combined with the previous cache value for the same key and update interval. The default
* implementation of {@link CacheUpdater#provideNewCacheValue(Object, Object, Object, UpdateInterval)} simply returns
* the <code>computedCacheUpdate</code> parameter which is the result computed by
* {@link CacheUpdater#computeCacheUpdate(Object, UpdateInterval)} before.
*
* @param <K>
* the key type for which values of type <code>V</code> are cached
* @param <V>
* the value type of which instances are cached for particular keys of type <code>K</code>
* @param <U>
* a parameter type for the cache update method for a single key, such that the parameters of multiple queued
* requests for the same key can be joined into one for a faster update
*
* @author Axel Uhl (D043530)
*
@@ -85,7 +102,7 @@ public class SmartFutureCache<K, V, U extends UpdateInterval<U>> {
V computeCacheUpdate(K key, U updateInterval) throws Exception;
/**
* Expected to deliver an updated cache value quick (compared to the potentially much more expensive
* Expected to deliver an updated cache value quickly (compared to the potentially much more expensive
* {@link #computeCacheUpdate(Object, UpdateInterval)} method which is run in a background task and doesn't lock
* the cache for readers).
*
@@ -220,6 +237,12 @@ public class SmartFutureCache<K, V, U extends UpdateInterval<U>> {
recalculator.execute(future);
}
/**
* Fetches a value for <code>key</code> from the cache. If no {@link #triggerUpdate(Object, UpdateInterval)} for the <code>key</code>
* has ever happened, <code>null</code> will be returned. Otherwise, depending on <code>waitForLatest</code> the result is taken
* from the cache straight away (<code>waitForLatest==false</code>) or, if a re-calculation for the <code>key</code> is still
* ongoing, the result of that ongoing re-calculation is returned.
*/
public V get(K key, boolean waitForLatest) {
V value = null;
if (waitForLatest) {
@@ -53,9 +53,9 @@ public class TestColumnSwapping {
try {
// get Leaderboard with name and current date
leaderboardOriginalDTO = new LeaderboardDTO(null, null, /* higherScoreIsBetter */ false);
leaderboardOriginalDTO.addRace("Race1", DEFAULT_FLEET, true, null, null);
leaderboardOriginalDTO.addRace("Race3", DEFAULT_FLEET, true, null, null);
leaderboardOriginalDTO.addRace("Race2", DEFAULT_FLEET, true, null, null);
leaderboardOriginalDTO.addRace("Race1", /* explicitFactor */ null, DEFAULT_FLEET, true, null, null);
leaderboardOriginalDTO.addRace("Race3", /* explicitFactor */ null, DEFAULT_FLEET, true, null, null);
leaderboardOriginalDTO.addRace("Race2", /* explicitFactor */ null, DEFAULT_FLEET, true, null, null);
} catch (Exception e) {
e.printStackTrace();
}
@@ -104,9 +104,9 @@ public class TestColumnSwapping {
public void testLeaderBoardDTOMethods() {
lb = new LeaderboardDTO(null, null, /* higherScoreIsBetter */ false);
assertNotNull("Leaderboard != NULL", lb);
lb.addRace("1", DEFAULT_FLEET, false, null, null);
lb.addRace("2", DEFAULT_FLEET, false, null, null);
lb.addRace("3", DEFAULT_FLEET, true, null, null);
lb.addRace("1", /* explicitFactor */ null, DEFAULT_FLEET, false, null, null);
lb.addRace("2", /* explicitFactor */ null, DEFAULT_FLEET, false, null, null);
lb.addRace("3", /* explicitFactor */ null, DEFAULT_FLEET, true, null, null);
lb.moveRaceDown("1");
String[] s = new String[] { "2", "1", "3" };
for (int i = 0; i < lb.getRaceList().size(); i++)
@@ -186,7 +186,7 @@ body ::-webkit-scrollbar-track {
.subTitleLabelWrapper {
position: absolute;
min-width: 400px;
min-width: 200px;
text-align: right;
height: 68px;
right: 0;
@@ -4,7 +4,7 @@
<stringAttribute key="com.google.gdt.eclipse.suiteMainTypeProcessor.PREVIOUSLY_SET_MAIN_TYPE_NAME" value="com.google.gwt.dev.GWTShell"/>
<booleanAttribute key="com.google.gdt.eclipse.suiteWarArgumentProcessor.IS_WAR_FROM_PROJECT_PROPERTIES" value="true"/>
<listAttribute key="com.google.gwt.eclipse.core.ENTRY_POINT_MODULES">
<listEntry value="com.sap.sailing.gwt.ui.RaceBoard"/>
<listEntry value="com.sap.sailing.gwt.ui.TvView"/>
</listAttribute>
<stringAttribute key="com.google.gwt.eclipse.core.URL" value="/gwt/AdminConsole.html"/>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
@@ -19,7 +19,7 @@
</listAttribute>
<stringAttribute key="org.eclipse.jdt.launching.CLASSPATH_PROVIDER" value="com.google.gwt.eclipse.core.moduleClasspathProvider"/>
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.google.gwt.dev.DevMode"/>
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-war &quot;${project_loc:com.sap.sailing.gwt.ui}&quot; -noserver -remoteUI &quot;${gwt_remote_ui_server_port}:${unique_id}&quot; -logLevel INFO -codeServerPort 9997 -startupUrl /gwt/AdminConsole.html com.sap.sailing.gwt.ui.AdminConsole -startupUrl /gwt/LeaderboardEditing.html com.sap.sailing.gwt.ui.LeaderboardEditing -startupUrl /gwt/UserManagement.html com.sap.sailing.gwt.ui.UserManagement -startupUrl /gwt/Leaderboard.html com.sap.sailing.gwt.ui.Leaderboard -startupUrl /gwt/Spectator.html com.sap.sailing.gwt.ui.Spectator -startupUrl /gwt/RaceBoard.html com.sap.sailing.gwt.ui.RaceBoard"/>
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-war &quot;${project_loc:com.sap.sailing.gwt.ui}&quot; -noserver -remoteUI &quot;${gwt_remote_ui_server_port}:${unique_id}&quot; -logLevel INFO -codeServerPort 9997 -startupUrl /gwt/AdminConsole.html com.sap.sailing.gwt.ui.AdminConsole -startupUrl /gwt/LeaderboardEditing.html com.sap.sailing.gwt.ui.LeaderboardEditing -startupUrl /gwt/UserManagement.html com.sap.sailing.gwt.ui.UserManagement -startupUrl /gwt/Leaderboard.html com.sap.sailing.gwt.ui.Leaderboard -startupUrl /gwt/Spectator.html com.sap.sailing.gwt.ui.Spectator -startupUrl /gwt/RaceBoard.html com.sap.sailing.gwt.ui.RaceBoard -startupUrl /gwt/TvView.html com.sap.sailing.gwt.ui.TvView"/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="com.sap.sailing.gwt.ui"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xmx2048m -XX:MaxPermSize=512m"/>
</launchConfiguration>
+33
View File
@@ -0,0 +1,33 @@
<!doctype html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title>SAP Sailing Analytics TV View</title>
<meta content='width = device-width, initial-scale = 1.0, user-scalable = yes' name='viewport'>
<meta content='yes' name='apple-mobile-web-app-capable'>
<meta content='black' name='apple-mobile-web-app-status-bar-style'>
<!-- <link href='iphone-splash-screen.png' rel='apple-touch-startup-image'> -->
<link href='images/sap-sailing-app-icon.png' rel='apple-touch-icon'>
<link rel="shortcut icon" type="image/x-icon" href="images/sap.ico" />
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<script type="text/javascript" src="highcharts/js/highcharts.js"></script>
<!-- Optionally, add a highcharts theme file -->
<script type="text/javascript" src="highcharts/js/themes/grid.js"></script>
<script type="text/javascript" language="javascript" src="com.sap.sailing.gwt.ui.TvView/com.sap.sailing.gwt.ui.TvView.nocache.js"></script>
</head>
<body>
<iframe src="javascript:''" id="__gwt_historyFrame" tabIndex='-1' style="position:absolute;width:0;height:0;border:0"></iframe>
<!-- RECOMMENDED if your web app will not function without JavaScript enabled -->
<noscript>
<div style="width: 22em; position: absolute; left: 50%; margin-left: -11em; color: red; background-color: white; border: 1px solid red; padding: 4px; font-family: sans-serif">
Your web browser must have JavaScript enabled
in order for this application to display correctly.
</div>
</noscript>
</body>
</html>
+3 -1
View File
@@ -28,5 +28,7 @@ bin.includes = META-INF/,\
highcharts/,\
Spectator.css,\
fontface.css,\
webfonts/
webfonts/,\
TvView.html,\
com.sap.sailing.gwt.ui.TvView/
output.. = WEB-INF/classes/
Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 19 KiB

+1
View File
@@ -50,6 +50,7 @@
<module>com.sap.sailing.gwt.ui.AdminConsole</module>
<module>com.sap.sailing.gwt.ui.Leaderboard</module>
<module>com.sap.sailing.gwt.ui.UserManagement</module>
<module>com.sap.sailing.gwt.ui.TvView</module>
</modules>
<gen>${basedir}/.generated</gen>
<localWorkers>3</localWorkers>
@@ -8,6 +8,9 @@ interface AdminConsoleResources extends ClientBundle {
@Source("com/sap/sailing/gwt/ui/client/images/settings.png")
ImageResource settingsIcon();
@Source("com/sap/sailing/gwt/ui/client/images/settingsActionIcon.png")
ImageResource settingsActionIcon();
@Source("com/sap/sailing/gwt/ui/client/images/remove.png")
ImageResource removeIcon();
@@ -1,21 +1,66 @@
package com.sap.sailing.gwt.ui.adminconsole;
import com.google.gwt.cell.client.AbstractSafeHtmlCell;
import com.google.gwt.cell.client.FieldUpdater;
import com.google.gwt.cell.client.ValueUpdater;
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.EventTarget;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.resources.client.ImageResource;
import com.google.gwt.safecss.shared.SafeStyles;
import com.google.gwt.safecss.shared.SafeStylesUtils;
import com.google.gwt.safehtml.client.SafeHtmlTemplates;
import com.google.gwt.safehtml.shared.SafeHtml;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.text.shared.SafeHtmlRenderer;
import com.google.gwt.text.shared.SimpleSafeHtmlRenderer;
import com.google.gwt.user.cellview.client.CellTable;
import com.google.gwt.user.client.ui.AbstractImagePrototype;
/**
* A cell type for use in a {@link CellTable} which renders a horizontal sequence of images. Use by subclassing and
* overriding the {@link #getImageSpecs()} method where action name, tool tip and image prototype can be provided for
* each image to be rendered. Add a {@link FieldUpdater} to the column rendered using this cell type; the updater will
* receive the clicked image's action name as the <code>value</code> parameter. The image rendering style can be adjusted by
* overriding the default {@link #getImageStyle()} implementation.
*/
public abstract class ImagesBarCell extends AbstractSafeHtmlCell<String> {
/**
* Subclasses use this template to render a single image tag
*/
private final static ImagesBarTemplates imageTemplate = GWT.create(ImagesBarTemplates.class);
protected class ImageSpec {
private final AbstractImagePrototype imagePrototype;
private final String actionName;
private final String tooltip;
public ImageSpec(String actionName, String tooltip, AbstractImagePrototype imagePrototype) {
super();
this.imagePrototype = imagePrototype;
this.actionName = actionName;
this.tooltip = tooltip;
}
public AbstractImagePrototype getImagePrototype() {
return imagePrototype;
}
public String getActionName() {
return actionName;
}
public String getTooltip() {
return tooltip;
}
}
interface ImagesBarTemplates extends SafeHtmlTemplates {
/**
* @param name
* @param title
* the tool-tip to display for the image on mouse-over
* @param value
* how to render the image; this needs to be an &lt;img&gt; tag, not enclosed by any other element,
* as returned by {@link ImagesBarCell#makeImagePrototype(ImageResource)}
*/
@SafeHtmlTemplates.Template("<div name=\"{0}\" style=\"{1}\" title=\"{2}\">{3}</div>")
SafeHtml cell(String name, SafeStyles styles, String title, SafeHtml value);
}
@@ -28,6 +73,10 @@ public abstract class ImagesBarCell extends AbstractSafeHtmlCell<String> {
super(renderer, "click", "keydown");
}
protected static ImagesBarTemplates getImageTemplate() {
return imageTemplate;
}
/**
* Called when an event occurs in a rendered instance of this Cell. The
* parent element refers to the element that contains the rendered cell, NOT
@@ -42,26 +91,22 @@ public abstract class ImagesBarCell extends AbstractSafeHtmlCell<String> {
if ("click".equals(event.getType())) {
// Ignore clicks that occur outside of the outermost element.
EventTarget eventTarget = event.getEventTarget();
if (parent.isOrHasChild(Element.as(eventTarget))) {
// if (parent.getFirstChildElement().isOrHasChild(
// Element.as(eventTarget))) {
// use this to get the selected element!!
Element el = Element.as(eventTarget);
// check if we really click on the image
if (el.getNodeName().equalsIgnoreCase("IMG")) {
doAction(el.getParentElement().getAttribute("name"), valueUpdater);
}
}
}
};
}
/**
* onEnterKeyDown is called when the user presses the ENTER key will the
* Cell is selected. You are not required to override this method, but its a
* common convention that allows your cell to respond to key events.
* onEnterKeyDown is called when the user presses the ENTER key while the Cell is selected. You are not required to
* override this method, but it is a common convention that allows your cell to respond to key events.
*/
@Override
protected void onEnterKeyDown(Context context, Element parent,
@@ -80,16 +125,38 @@ public abstract class ImagesBarCell extends AbstractSafeHtmlCell<String> {
// Trigger a value updater. In this case, the value doesn't actually
// change, but we use a ValueUpdater to let the app know that a value
// was clicked.
if (valueUpdater != null)
if (valueUpdater != null) {
valueUpdater.update(value);
}
}
/**
* Make icons available as SafeHtml
* @param resource
* @return
* Make icons available as image prototype
*/
protected static SafeHtml makeImage(ImageResource resource) {
return AbstractImagePrototype.create(resource).getSafeHtml();
protected static AbstractImagePrototype makeImagePrototype(ImageResource resource) {
return AbstractImagePrototype.create(resource);
}
protected abstract Iterable<ImageSpec> getImageSpecs();
@Override
protected void render(com.google.gwt.cell.client.Cell.Context context, SafeHtml data, SafeHtmlBuilder sb) {
/*
* Always do a null check on the value. Cell widgets can pass null to
* cells if the underlying data contains a null, or if the data arrives
* out of order.
*/
if (data != null) {
SafeStyles imgStyle = getImageStyle();
for (ImageSpec imageSpec : getImageSpecs()) {
SafeHtml rendered = getImageTemplate().cell(imageSpec.getActionName(), imgStyle, imageSpec.getTooltip(),
imageSpec.getImagePrototype().getSafeHtml());
sb.append(rendered);
}
}
}
protected SafeStyles getImageStyle() {
return SafeStylesUtils.fromTrustedString("float:left;cursor:hand;cursor:pointer;padding-right:5px;");
}
}
@@ -1,57 +1,34 @@
package com.sap.sailing.gwt.ui.adminconsole;
import java.util.Arrays;
import com.google.gwt.core.client.GWT;
import com.google.gwt.safecss.shared.SafeStyles;
import com.google.gwt.safecss.shared.SafeStylesUtils;
import com.google.gwt.safehtml.shared.SafeHtml;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.text.shared.SafeHtmlRenderer;
import com.sap.sailing.gwt.ui.client.StringMessages;
public class LeaderboardConfigImagesBarCell extends ImagesBarCell {
public static final String ACTION_REMOVE = "ACTION_REMOVE";
public static final String ACTION_EDIT_SCORES = "ACTION_EDIT_SCORES";
public static final String ACTION_EDIT = "ACTION_EDIT";
public static final String ACTION_CONFIGURE_URL = "ACTION_CONFIGURE_URL";
private static AdminConsoleResources resources = GWT.create(AdminConsoleResources.class);
private final StringMessages stringMessages;
public LeaderboardConfigImagesBarCell(StringMessages stringConstants) {
public LeaderboardConfigImagesBarCell(StringMessages stringMessages) {
super();
this.stringConstants = stringConstants;
this.stringMessages = stringMessages;
}
public LeaderboardConfigImagesBarCell(SafeHtmlRenderer<String> renderer, StringMessages stringConstants) {
super();
this.stringConstants = stringConstants;
this.stringMessages = stringConstants;
}
private static ImagesBarTemplates templates = GWT.create(ImagesBarTemplates.class);
private static AdminConsoleResources resources = GWT.create(AdminConsoleResources.class);
private static final SafeHtml ICON_EDIT = makeImage(resources.editIcon());
private static final SafeHtml ICON_REMOVE = makeImage(resources.removeIcon());
private static final SafeHtml ICON_EDIT_SCORES = makeImage(resources.scoresIcon());
private StringMessages stringConstants;
@Override
protected void render(com.google.gwt.cell.client.Cell.Context context, SafeHtml data, SafeHtmlBuilder sb) {
/*
* Always do a null check on the value. Cell widgets can pass null to
* cells if the underlying data contains a null, or if the data arrives
* out of order.
*/
if (data == null) {
return;
}
SafeStyles imgStyle = SafeStylesUtils.fromTrustedString("float:left;cursor:hand;cursor:pointer;padding-right:5px;");
SafeHtml rendered = templates.cell("ACTION_EDIT", imgStyle, stringConstants.actionEdit(), ICON_EDIT);
sb.append(rendered);
rendered = templates.cell("ACTION_EDIT_SCORES", imgStyle, stringConstants.actionEditScores(), ICON_EDIT_SCORES);
sb.append(rendered);
rendered = templates.cell("ACTION_REMOVE", imgStyle, stringConstants.actionRemove(), ICON_REMOVE);
sb.append(rendered);
protected Iterable<ImageSpec> getImageSpecs() {
return Arrays.asList(new ImageSpec(ACTION_EDIT, stringMessages.actionEdit(), makeImagePrototype(resources.editIcon())),
new ImageSpec(ACTION_EDIT_SCORES, stringMessages.actionEditScores(), makeImagePrototype(resources.scoresIcon())),
new ImageSpec(ACTION_CONFIGURE_URL, stringMessages.actionConfigureUrl(), makeImagePrototype(resources.settingsActionIcon())),
new ImageSpec(ACTION_REMOVE, stringMessages.actionRemove(), makeImagePrototype(resources.removeIcon())));
}
}
@@ -47,6 +47,7 @@ import com.sap.sailing.domain.common.RegattaIdentifier;
import com.sap.sailing.domain.common.RegattaName;
import com.sap.sailing.domain.common.RegattaNameAndRaceName;
import com.sap.sailing.domain.common.impl.Util.Pair;
import com.sap.sailing.gwt.ui.adminconsole.RaceColumnInLeaderboardDialog.RaceColumnDescriptor;
import com.sap.sailing.gwt.ui.client.DataEntryDialog.DialogCallback;
import com.sap.sailing.gwt.ui.client.ErrorReporter;
import com.sap.sailing.gwt.ui.client.ParallelExecutionCallback;
@@ -58,7 +59,9 @@ import com.sap.sailing.gwt.ui.client.RegattaDisplayer;
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.client.URLFactory;
import com.sap.sailing.gwt.ui.leaderboard.LeaderboardEntryPoint;
import com.sap.sailing.gwt.ui.leaderboard.ScoringSchemeTypeFormatter;
import com.sap.sailing.gwt.ui.shared.AbstractLeaderboardDTO;
import com.sap.sailing.gwt.ui.shared.FleetDTO;
import com.sap.sailing.gwt.ui.shared.RaceColumnDTO;
import com.sap.sailing.gwt.ui.shared.RegattaDTO;
@@ -231,19 +234,19 @@ public class LeaderboardConfigPanel extends FormPanel implements RegattaDisplaye
leaderboardActionColumn.setFieldUpdater(new FieldUpdater<StrippedLeaderboardDTO, String>() {
@Override
public void update(int index, StrippedLeaderboardDTO leaderboardDTO, String value) {
if ("ACTION_REMOVE".equals(value)) {
if (LeaderboardConfigImagesBarCell.ACTION_REMOVE.equals(value)) {
if (Window.confirm("Do you really want to remove the leaderboard: '" + leaderboardDTO.name + "' ?")) {
removeLeaderboard(leaderboardDTO);
}
} else if ("ACTION_EDIT".equals(value)) {
} else if (LeaderboardConfigImagesBarCell.ACTION_EDIT.equals(value)) {
final String oldLeaderboardName = leaderboardDTO.name;
List<StrippedLeaderboardDTO> otherExistingLeaderboard = new ArrayList<StrippedLeaderboardDTO>();
otherExistingLeaderboard.addAll(availableLeaderboardList);
otherExistingLeaderboard.remove(leaderboardDTO);
if(leaderboardDTO.isMetaLeaderboard) {
if (leaderboardDTO.isMetaLeaderboard) {
Window.alert("This is a meta leaderboard. It can't be changed here.");
} else {
if(leaderboardDTO.isRegattaLeaderboard) {
if (leaderboardDTO.isRegattaLeaderboard) {
LeaderboardDescriptor descriptor = new LeaderboardDescriptor(leaderboardDTO.name,
null, leaderboardDTO.discardThresholds, leaderboardDTO.regattaName);
AbstractLeaderboardDialog dialog = new RegattaLeaderboardEditDialog(Collections
@@ -278,10 +281,12 @@ public class LeaderboardConfigPanel extends FormPanel implements RegattaDisplaye
dialog.show();
}
}
} else if ("ACTION_EDIT_SCORES".equals(value)) {
} else if (LeaderboardConfigImagesBarCell.ACTION_EDIT_SCORES.equals(value)) {
String debugParam = Window.Location.getParameter("gwt.codesvr");
Window.open("/gwt/LeaderboardEditing.html?name=" + leaderboardDTO.name
+ (debugParam != null && !debugParam.isEmpty() ? "&gwt.codesvr=" + debugParam : ""), "_blank", null);
} else if (LeaderboardConfigImagesBarCell.ACTION_CONFIGURE_URL.equals(value)) {
openLeaderboardUrlConfigDialog(leaderboardDTO, stringMessages);
}
}
});
@@ -382,6 +387,12 @@ public class LeaderboardConfigPanel extends FormPanel implements RegattaDisplaye
return object.getB().name;
}
};
TextColumn<Pair<RaceColumnDTO, FleetDTO>> explicitFactorColumn = new TextColumn<Pair<RaceColumnDTO, FleetDTO>>() {
@Override
public String getValue(Pair<RaceColumnDTO, FleetDTO> object) {
return object.getA().getExplicitFactor() == null ? "" : object.getA().getExplicitFactor().toString();
}
};
Column<Pair<RaceColumnDTO, FleetDTO>, Boolean> isMedalRaceCheckboxColumn = new Column<Pair<RaceColumnDTO, FleetDTO>, Boolean>(
new CheckboxCell()) {
@@ -411,39 +422,35 @@ public class LeaderboardConfigPanel extends FormPanel implements RegattaDisplaye
raceActionColumn.setFieldUpdater(new FieldUpdater<Pair<RaceColumnDTO, FleetDTO>, String>() {
@Override
public void update(int index, Pair<RaceColumnDTO, FleetDTO> object, String value) {
if ("ACTION_REMOVE".equals(value)) {
if (LeaderboardRaceConfigImagesBarCell.ACTION_REMOVE.equals(value)) {
if (Window.confirm(stringMessages.reallyRemoveRace(object.getA().getRaceColumnName()))) {
removeRaceColumn(object.getA());
}
} else if ("ACTION_EDIT".equals(value)) {
} else if (LeaderboardRaceConfigImagesBarCell.ACTION_EDIT.equals(value)) {
editRaceColumnOfLeaderboard(object);
} else if ("ACTION_UNLINK".equals(value)) {
} else if (LeaderboardRaceConfigImagesBarCell.ACTION_UNLINK.equals(value)) {
unlinkRaceColumnFromTrackedRace(object.getA().getRaceColumnName(), object.getB());
}
}
});
Label lblRaceNamesIn = new Label(stringMessages.races());
vPanel.add(lblRaceNamesIn);
raceColumnTable = new CellTable<Pair<RaceColumnDTO, FleetDTO>>(/* pageSize */200, tableRes);
raceColumnTable.addColumn(raceLinkColumn, stringMessages.name());
raceColumnTable.addColumn(fleetNameColumn, stringMessages.fleet());
raceColumnTable.addColumn(isMedalRaceCheckboxColumn, stringMessages.medalRace());
raceColumnTable.addColumn(isLinkedRaceColumn, stringMessages.islinked());
raceColumnTable.addColumn(explicitFactorColumn, stringMessages.factor());
raceColumnTable.addColumn(raceActionColumn, stringMessages.actions());
raceColumnAndFleetList.addDataDisplay(raceColumnTable);
raceColumnTable.setWidth("500px");
raceColumnTableSelectionModel = new SingleSelectionModel<Pair<RaceColumnDTO, FleetDTO>>();
raceColumnTable.setSelectionModel(raceColumnTableSelectionModel);
raceColumnTableSelectionModel.addSelectionChangeHandler(new SelectionChangeEvent.Handler() {
public void onSelectionChange(SelectionChangeEvent event) {
leaderboardRaceColumnSelectionChanged();
}
});
vPanel.add(raceColumnTable);
HorizontalPanel selectedLeaderboardRaceButtonPanel = new HorizontalPanel();
@@ -483,6 +490,16 @@ public class LeaderboardConfigPanel extends FormPanel implements RegattaDisplaye
loadAndRefreshLeaderboards();
}
/**
* Allow the user to combine the various URL parameters that exist for the {@link LeaderboardEntryPoint} and obtain the
* resulting URL in a link. The link's reference target is updated dynamically as the user adjusts the settings. Therefore,
* the link can be clicked, bookmarked or copied to the clipboard at any time. The OK / Cancel actions for the dialog shown
* are no-ops.
*/
private void openLeaderboardUrlConfigDialog(AbstractLeaderboardDTO leaderboard, StringMessages stringMessages) {
LeaderboardEntryPoint.getUrlConfigurationDialog(leaderboard, stringMessages).show();
}
public void loadAndRefreshLeaderboards() {
sailingService.getLeaderboards(new AsyncCallback<List<StrippedLeaderboardDTO>>() {
@Override
@@ -687,27 +704,43 @@ public class LeaderboardConfigPanel extends FormPanel implements RegattaDisplaye
private void editRaceColumnOfLeaderboard(final Pair<RaceColumnDTO, FleetDTO> raceColumnWithFleet) {
final String selectedLeaderboardName = getSelectedLeaderboardName();
final boolean oldIsMedalRace = raceColumnWithFleet.getA().isMedalRace();
final String oldRaceColumnName = raceColumnWithFleet.getA().getRaceColumnName();
final Double oldExplicitFactor = raceColumnWithFleet.getA().getExplicitFactor();
List<RaceColumnDTO> existingRacesWithoutThisRace = new ArrayList<RaceColumnDTO>();
for(Pair<RaceColumnDTO, FleetDTO> pair: raceColumnAndFleetList.getList()) {
existingRacesWithoutThisRace.add(pair.getA());
}
existingRacesWithoutThisRace.remove(raceColumnWithFleet.getA());
final RaceColumnInLeaderboardDialog raceDialog = new RaceColumnInLeaderboardDialog(existingRacesWithoutThisRace,
raceColumnWithFleet.getA(), stringMessages, new DialogCallback<RaceColumnDTO>() {
raceColumnWithFleet.getA(), stringMessages, new DialogCallback<RaceColumnDescriptor>() {
@Override
public void cancel() {
}
@Override
public void ok(final RaceColumnDTO result) {
public void ok(final RaceColumnDescriptor result) {
boolean rename = !oldRaceColumnName.equals(result.getName());
boolean updateIsMedalRace = oldIsMedalRace != result.isMedalRace();
boolean updateFactor = oldExplicitFactor != result.getExplicitFactor();
List<ParallelExecutionCallback<Void>> callbacks = new ArrayList<ParallelExecutionCallback<Void>>();
final ParallelExecutionCallback<Void> renameLeaderboardColumnCallback = new ParallelExecutionCallback<Void>();
if (rename) {
callbacks.add(renameLeaderboardColumnCallback);
}
final ParallelExecutionCallback<Void> updateIsMedalRaceCallback = new ParallelExecutionCallback<Void>();
new ParallelExecutionHolder(renameLeaderboardColumnCallback, updateIsMedalRaceCallback) {
if (updateIsMedalRace) {
callbacks.add(updateIsMedalRaceCallback);
}
final ParallelExecutionCallback<Void> updateLeaderboardColumnFactorCallback = new ParallelExecutionCallback<Void>();
if (updateFactor) {
callbacks.add(updateLeaderboardColumnFactorCallback);
}
new ParallelExecutionHolder(callbacks.toArray(new ParallelExecutionCallback<?>[0])) {
@Override
public void handleSuccess() {
loadAndRefreshLeaderboard(selectedLeaderboardName);
selectRaceColumn(result.getRaceColumnName());
selectRaceColumn(result.getName());
}
@Override
public void handleFailure(Throwable t) {
@@ -716,10 +749,18 @@ public class LeaderboardConfigPanel extends FormPanel implements RegattaDisplaye
+ t.getMessage());
}
};
sailingService.renameLeaderboardColumn(selectedLeaderboardName, oldRaceColumnName,
result.getRaceColumnName(), renameLeaderboardColumnCallback);
sailingService.updateIsMedalRace(selectedLeaderboardName, result.getRaceColumnName(),
result.isMedalRace(), updateIsMedalRaceCallback);
if (rename) {
sailingService.renameLeaderboardColumn(selectedLeaderboardName, oldRaceColumnName,
result.getName(), renameLeaderboardColumnCallback);
}
if (updateIsMedalRace) {
sailingService.updateIsMedalRace(selectedLeaderboardName, result.getName(),
result.isMedalRace(), updateIsMedalRaceCallback);
}
if (updateFactor) {
sailingService.updateLeaderboardColumnFactor(selectedLeaderboardName, result.getName(),
result.getExplicitFactor(), updateLeaderboardColumnFactorCallback);
}
}
});
raceDialog.show();
@@ -729,11 +770,10 @@ public class LeaderboardConfigPanel extends FormPanel implements RegattaDisplaye
final boolean isMedalRace) {
sailingService.updateIsMedalRace(leaderboardName, raceInLeaderboard.getRaceColumnName(), isMedalRace,
new AsyncCallback<Void>() {
@Override
public void onFailure(Throwable caught) {
errorReporter.reportError(stringMessages.errorUpdatingIsMedalRace(caught.getMessage()));
}
@Override
public void onSuccess(Void result) {
selectedLeaderboard.setIsMedalRace(raceInLeaderboard.getRaceColumnName(), isMedalRace);
@@ -801,7 +841,7 @@ public class LeaderboardConfigPanel extends FormPanel implements RegattaDisplaye
raceSelectionProvider.addRaceSelectionChangeListener(LeaderboardConfigPanel.this);
}
});
if (selectedLeaderboard != null && !selectedLeaderboard.isMetaLeaderboard) {
if (selectedLeaderboard != null) {
raceColumnAndFleetList.getList().clear();
for (RaceColumnDTO raceColumn : selectedLeaderboard.getRaceList()) {
for (FleetDTO fleet : raceColumn.getFleets()) {
@@ -809,8 +849,10 @@ public class LeaderboardConfigPanel extends FormPanel implements RegattaDisplaye
}
}
selectedLeaderBoardPanel.setVisible(true);
trackedRacesCaptionPanel.setVisible(true);
selectedLeaderBoardPanel.setCaptionText("Details of leaderboard '" + selectedLeaderboard.name + "'");
if (!selectedLeaderboard.isMetaLeaderboard) {
trackedRacesCaptionPanel.setVisible(true);
}
} else {
selectedLeaderBoardPanel.setVisible(false);
trackedRacesCaptionPanel.setVisible(false);
@@ -1,52 +1,30 @@
package com.sap.sailing.gwt.ui.adminconsole;
import java.util.Arrays;
import com.google.gwt.core.client.GWT;
import com.google.gwt.safecss.shared.SafeStyles;
import com.google.gwt.safecss.shared.SafeStylesUtils;
import com.google.gwt.safehtml.shared.SafeHtml;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.text.shared.SafeHtmlRenderer;
import com.sap.sailing.gwt.ui.client.StringMessages;
public class LeaderboardGroupConfigImagesBarCell extends ImagesBarCell {
public LeaderboardGroupConfigImagesBarCell(StringMessages stringConstants) {
super();
this.stringConstants = stringConstants;
}
public LeaderboardGroupConfigImagesBarCell(SafeHtmlRenderer<String> renderer, StringMessages stringConstants) {
super();
this.stringConstants = stringConstants;
}
private static ImagesBarTemplates templates = GWT.create(ImagesBarTemplates.class);
public static final String ACTION_REMOVE = "ACTION_REMOVE";
public static final String ACTION_EDIT = "ACTION_EDIT";
private static AdminConsoleResources resources = GWT.create(AdminConsoleResources.class);
private final StringMessages stringMessages;
private static final SafeHtml ICON_EDIT = makeImage(resources.editIcon());
public LeaderboardGroupConfigImagesBarCell(StringMessages stringMessages) {
super();
this.stringMessages = stringMessages;
}
private static final SafeHtml ICON_REMOVE = makeImage(resources.removeIcon());
private StringMessages stringConstants;
public LeaderboardGroupConfigImagesBarCell(SafeHtmlRenderer<String> renderer, StringMessages stringMessages) {
super();
this.stringMessages = stringMessages;
}
@Override
protected void render(com.google.gwt.cell.client.Cell.Context context, SafeHtml data, SafeHtmlBuilder sb) {
/*
* Always do a null check on the value. Cell widgets can pass null to
* cells if the underlying data contains a null, or if the data arrives
* out of order.
*/
if (data == null) {
return;
}
SafeStyles imgStyle = SafeStylesUtils.fromTrustedString("float:left;cursor:hand;cursor:pointer;padding-right:5px;");
SafeHtml rendered = templates.cell("ACTION_EDIT", imgStyle, stringConstants.actionEdit(), ICON_EDIT);
sb.append(rendered);
rendered = templates.cell("ACTION_REMOVE", imgStyle, stringConstants.actionRemove(), ICON_REMOVE);
sb.append(rendered);
protected Iterable<ImageSpec> getImageSpecs() {
return Arrays.asList(new ImageSpec(ACTION_EDIT, stringMessages.actionEdit(), makeImagePrototype(resources.editIcon())),
new ImageSpec(ACTION_REMOVE, stringMessages.actionRemove(), makeImagePrototype(resources.removeIcon())));
}
}
@@ -36,6 +36,7 @@ import com.google.gwt.view.client.MultiSelectionModel;
import com.google.gwt.view.client.SelectionChangeEvent;
import com.google.gwt.view.client.SingleSelectionModel;
import com.sap.sailing.gwt.ui.adminconsole.LeaderboardConfigPanel.AnchorCell;
import com.sap.sailing.gwt.ui.adminconsole.LeaderboardGroupDialog.LeaderboardGroupDescriptor;
import com.sap.sailing.gwt.ui.client.AbstractRegattaPanel;
import com.sap.sailing.gwt.ui.client.DataEntryDialog.DialogCallback;
import com.sap.sailing.gwt.ui.client.ErrorReporter;
@@ -446,21 +447,22 @@ public class LeaderboardGroupConfigPanel extends AbstractRegattaPanel {
new LeaderboardGroupConfigImagesBarCell(stringMessages));
groupActionsColumn.setFieldUpdater(new FieldUpdater<LeaderboardGroupDTO, String>() {
@Override
public void update(int index, LeaderboardGroupDTO group, String command) {
if (command.equals("ACTION_EDIT")) {
public void update(int index, final LeaderboardGroupDTO group, String command) {
if (command.equals(LeaderboardGroupConfigImagesBarCell.ACTION_EDIT)) {
final String oldGroupName = group.name;
ArrayList<LeaderboardGroupDTO> otherExistingGroups = new ArrayList<LeaderboardGroupDTO>(availableLeaderboardGroups);
otherExistingGroups.remove(group);
LeaderboardGroupEditDialog dialog = new LeaderboardGroupEditDialog(group, otherExistingGroups, stringMessages, new DialogCallback<LeaderboardGroupDTO>() {
LeaderboardGroupEditDialog dialog = new LeaderboardGroupEditDialog(group, otherExistingGroups, stringMessages,
new DialogCallback<LeaderboardGroupDescriptor>() {
@Override
public void cancel() {}
@Override
public void ok(LeaderboardGroupDTO group) {
updateGroup(oldGroupName, group);
public void ok(LeaderboardGroupDescriptor groupDescriptor) {
updateGroup(oldGroupName, group, groupDescriptor);
}
});
dialog.show();
} else if (command.equals("ACTION_REMOVE")) {
} else if (command.equals(LeaderboardGroupConfigImagesBarCell.ACTION_REMOVE)) {
if (Window.confirm("Do you really want to remove the leaderboard group: '" + group.name + "' ?")) {
removeLeaderboardGroup(group);
}
@@ -558,24 +560,24 @@ public class LeaderboardGroupConfigPanel extends AbstractRegattaPanel {
private void addNewGroup() {
LeaderboardGroupCreateDialog dialog = new LeaderboardGroupCreateDialog(
Collections.unmodifiableCollection(availableLeaderboardGroups), stringMessages,
new DialogCallback<LeaderboardGroupDTO>() {
new DialogCallback<LeaderboardGroupDescriptor>() {
@Override
public void cancel() {}
@Override
public void ok(LeaderboardGroupDTO newGroup) {
public void ok(LeaderboardGroupDescriptor newGroup) {
createNewGroup(newGroup);
}
});
dialog.show();
}
private void createNewGroup(final LeaderboardGroupDTO newGroup) {
sailingService.createLeaderboardGroup(newGroup.name, newGroup.description,
newGroup.getOverallLeaderboardDiscardThresholds(), newGroup.getOverallLeaderboardScoringSchemeType(),
new AsyncCallback<LeaderboardGroupDTO>() {
private void createNewGroup(final LeaderboardGroupDescriptor newGroup) {
sailingService.createLeaderboardGroup(newGroup.getName(), newGroup.getDescription(), newGroup.isDisplayLeaderboardsInReverseOrder(),
newGroup.getOverallLeaderboardDiscardThresholds(),
newGroup.getOverallLeaderboardScoringSchemeType(), new AsyncCallback<LeaderboardGroupDTO>() {
@Override
public void onFailure(Throwable t) {
errorReporter.reportError("Error trying to create new leaderboard group" + newGroup.name
errorReporter.reportError("Error trying to create new leaderboard group" + newGroup.getName()
+ ": " + t.getMessage());
}
@Override
@@ -588,14 +590,14 @@ public class LeaderboardGroupConfigPanel extends AbstractRegattaPanel {
});
}
private void updateGroup(final String oldGroupName, final LeaderboardGroupDTO groupToUpdate) {
private void updateGroup(final String oldGroupName, final LeaderboardGroupDTO groupToUpdate, final LeaderboardGroupDescriptor updateDescriptor) {
List<String> leaderboardNames = new ArrayList<String>();
for (StrippedLeaderboardDTO leaderboardDTO : groupToUpdate.leaderboards) {
leaderboardNames.add(leaderboardDTO.name);
}
sailingService.updateLeaderboardGroup(oldGroupName, groupToUpdate.name, groupToUpdate.description,
leaderboardNames, groupToUpdate.getOverallLeaderboardDiscardThresholds(),
groupToUpdate.getOverallLeaderboardScoringSchemeType(), new AsyncCallback<Void>() {
sailingService.updateLeaderboardGroup(oldGroupName, updateDescriptor.getName(), updateDescriptor.getDescription(),
leaderboardNames, updateDescriptor.getOverallLeaderboardDiscardThresholds(),
updateDescriptor.getOverallLeaderboardScoringSchemeType(), new AsyncCallback<Void>() {
@Override
public void onFailure(Throwable t) {
errorReporter.reportError("Error trying to update leaderboard group " + oldGroupName + ": "
@@ -603,10 +605,15 @@ public class LeaderboardGroupConfigPanel extends AbstractRegattaPanel {
}
@Override
public void onSuccess(Void v) {
//Update the availableLeaderboardGroups and the list of displayed groups
// Update the availableLeaderboardGroups and the list of displayed groups
for (int i = 0; i < availableLeaderboardGroups.size(); i++) {
LeaderboardGroupDTO group = availableLeaderboardGroups.get(i);
if (oldGroupName.equals(group.name)) {
groupToUpdate.name = updateDescriptor.getName();
groupToUpdate.description = updateDescriptor.getDescription();
groupToUpdate.displayLeaderboardsInReverseOrder = updateDescriptor.isDisplayLeaderboardsInReverseOrder();
groupToUpdate.setOverallLeaderboardDiscardThresholds(updateDescriptor.getOverallLeaderboardDiscardThresholds());
groupToUpdate.setOverallLeaderboardScoringSchemeType(updateDescriptor.getOverallLeaderboardScoringSchemeType());
availableLeaderboardGroups.set(i, groupToUpdate);
int displayedIndex = groupsProvider.getList().indexOf(group);
if (displayedIndex != -1) {
@@ -618,8 +625,27 @@ public class LeaderboardGroupConfigPanel extends AbstractRegattaPanel {
}
});
}
/**
* Updates a change in the group's leaderboard list to the server
*/
private void updateGroup(final LeaderboardGroupDTO group) {
updateGroup(group.name, group);
List<String> leaderboardNames = new ArrayList<String>();
for (StrippedLeaderboardDTO leaderboardDTO : group.leaderboards) {
leaderboardNames.add(leaderboardDTO.name);
}
sailingService.updateLeaderboardGroup(group.name, group.name, group.description,
leaderboardNames, group.getOverallLeaderboardDiscardThresholds(),
group.getOverallLeaderboardScoringSchemeType(), new AsyncCallback<Void>() {
@Override
public void onFailure(Throwable t) {
errorReporter.reportError("Error trying to update leaderboard group " + group.name + ": "
+ t.getMessage());
}
@Override
public void onSuccess(Void v) {
}
});
}
private void removeLeaderboardGroup(final LeaderboardGroupDTO group) {
@@ -8,11 +8,12 @@ import com.sap.sailing.gwt.ui.shared.LeaderboardGroupDTO;
public class LeaderboardGroupCreateDialog extends LeaderboardGroupDialog {
public LeaderboardGroupCreateDialog(Collection<LeaderboardGroupDTO> existingGroups,
StringMessages stringMessages, DialogCallback<LeaderboardGroupDTO> callback) {
StringMessages stringMessages, DialogCallback<LeaderboardGroupDescriptor> callback) {
super(new LeaderboardGroupDTO(), stringMessages,
callback, existingGroups);
nameEntryField = createTextBox(null);
descriptionEntryField = createTextArea(null);
displayLeaderboardsInReverseOrderCheckBox.setValue(false);
useOverallLeaderboardCheckBox.setValue(false);
}
}
@@ -25,20 +25,55 @@ import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.shared.LeaderboardGroupDTO;
import com.sap.sailing.gwt.ui.shared.StrippedLeaderboardDTO;
public class LeaderboardGroupDialog extends DataEntryDialog<LeaderboardGroupDTO> {
public class LeaderboardGroupDialog extends DataEntryDialog<LeaderboardGroupDialog.LeaderboardGroupDescriptor> {
protected StringMessages stringMessages;
protected LeaderboardGroupDTO group;
protected TextBox nameEntryField;
protected TextArea descriptionEntryField;
private CheckBox useOverallLeaderboardCheckBox;
protected CheckBox displayLeaderboardsInReverseOrderCheckBox;
protected CheckBox useOverallLeaderboardCheckBox;
private Panel overallLeaderboardConfigPanel;
private LongBox[] overallLeaderboardDiscardThresholdFields;
private ListBox overallLeaderboardScoringSchemeListBox;
protected static class LeaderboardGroupParameterValidator implements Validator<LeaderboardGroupDTO> {
public static class LeaderboardGroupDescriptor {
private final String name;
private final String description;
private final boolean displayLeaderboardsInReverseOrder;
private final boolean useOverallLeaderboard;
private final int[] overallLeaderboardDiscardThresholds;
private final ScoringSchemeType overallLeaderboardScoringSchemeType;
public LeaderboardGroupDescriptor(String name, String description, boolean displayLeaderboardsInReverseOrder,
boolean useOverallLeaderboard, int[] overallLeaderboardDiscardThresholds,
ScoringSchemeType overallLeaderboardScoringSchemeType) {
super();
this.name = name;
this.description = description;
this.displayLeaderboardsInReverseOrder = displayLeaderboardsInReverseOrder;
this.useOverallLeaderboard = useOverallLeaderboard;
this.overallLeaderboardDiscardThresholds = overallLeaderboardDiscardThresholds;
this.overallLeaderboardScoringSchemeType = overallLeaderboardScoringSchemeType;
}
public String getName() {
return name;
}
public String getDescription() {
return description;
}
public boolean isDisplayLeaderboardsInReverseOrder() {
return displayLeaderboardsInReverseOrder;
}
public boolean isUseOverallLeaderboard() {
return useOverallLeaderboard;
}
public int[] getOverallLeaderboardDiscardThresholds() {
return overallLeaderboardDiscardThresholds;
}
public ScoringSchemeType getOverallLeaderboardScoringSchemeType() {
return overallLeaderboardScoringSchemeType;
}
}
protected static class LeaderboardGroupParameterValidator implements Validator<LeaderboardGroupDescriptor> {
private final StringMessages stringMessages;
private final ArrayList<LeaderboardGroupDTO> existingGroups;
@@ -49,14 +84,14 @@ public class LeaderboardGroupDialog extends DataEntryDialog<LeaderboardGroupDTO>
}
@Override
public String getErrorMessage(LeaderboardGroupDTO groupToValidate) {
public String getErrorMessage(LeaderboardGroupDescriptor groupToValidate) {
String errorMessage = null;
boolean nameNotEmpty = groupToValidate.name != null && groupToValidate.name.length() > 0;
boolean descrNotEmpty = groupToValidate.description != null && groupToValidate.description.length() > 0;
boolean nameNotEmpty = groupToValidate.getName() != null && groupToValidate.getName().length() > 0;
boolean descrNotEmpty = groupToValidate.getDescription() != null && groupToValidate.getDescription().length() > 0;
boolean unique = true;
for (LeaderboardGroupDTO group : existingGroups) {
if (group.name.equals(groupToValidate.name)) {
if (group.name.equals(groupToValidate.getName())) {
unique = false;
break;
}
@@ -88,16 +123,12 @@ public class LeaderboardGroupDialog extends DataEntryDialog<LeaderboardGroupDTO>
return overallLeaderboardDiscardThresholdFields;
}
protected CheckBox getUseOverallLeaderboardCheckBox() {
return useOverallLeaderboardCheckBox;
}
public LeaderboardGroupDialog(LeaderboardGroupDTO group, StringMessages stringMessages,
DialogCallback<LeaderboardGroupDTO> callback, Collection<LeaderboardGroupDTO> existingLeaderboardGroups) {
DialogCallback<LeaderboardGroupDescriptor> callback, Collection<LeaderboardGroupDTO> existingLeaderboardGroups) {
super(stringMessages.leaderboardGroup(), null, stringMessages.ok(), stringMessages.cancel(),
new LeaderboardGroupParameterValidator(stringMessages, existingLeaderboardGroups), callback);
this.stringMessages = stringMessages;
this.group = group;
displayLeaderboardsInReverseOrderCheckBox = createCheckbox(stringMessages.displayGroupsInReverseOrder());
useOverallLeaderboardCheckBox = createCheckbox(stringMessages.useOverallLeaderboard());
Grid formGrid = new Grid(3,2);
formGrid.setCellSpacing(3);
@@ -126,19 +157,14 @@ public class LeaderboardGroupDialog extends DataEntryDialog<LeaderboardGroupDTO>
}
@Override
protected LeaderboardGroupDTO getResult() {
group.name = nameEntryField.getText();
group.description = descriptionEntryField.getText();
if (useOverallLeaderboardCheckBox.getValue()) {
group.setOverallLeaderboardDiscardThresholds(AbstractLeaderboardDialog
.getDiscardThresholds(getOverallLeaderboardDiscardThresholdFields()));
group.setOverallLeaderboardScoringSchemeType(AbstractLeaderboardDialog.getSelectedScoringSchemeType(
overallLeaderboardScoringSchemeListBox, stringMessages));
} else {
group.setOverallLeaderboardDiscardThresholds(null);
group.setOverallLeaderboardScoringSchemeType(null);
}
return group;
protected LeaderboardGroupDescriptor getResult() {
return new LeaderboardGroupDescriptor(nameEntryField.getText(), descriptionEntryField.getText(),
displayLeaderboardsInReverseOrderCheckBox.getValue(),
useOverallLeaderboardCheckBox.getValue(),
useOverallLeaderboardCheckBox.getValue() ? AbstractLeaderboardDialog
.getDiscardThresholds(getOverallLeaderboardDiscardThresholdFields()) : null,
useOverallLeaderboardCheckBox.getValue() ? AbstractLeaderboardDialog.getSelectedScoringSchemeType(
overallLeaderboardScoringSchemeListBox, stringMessages) : null);
}
@Override
@@ -155,26 +181,31 @@ public class LeaderboardGroupDialog extends DataEntryDialog<LeaderboardGroupDTO>
descriptionEntryField.setVisibleLines(6);
descriptionEntryField.getElement().getStyle().setProperty("resize", "none");
panel.add(descriptionEntryField);
panel.add(displayLeaderboardsInReverseOrderCheckBox);
panel.add(useOverallLeaderboardCheckBox);
useOverallLeaderboardCheckBox.addValueChangeHandler(new ValueChangeHandler<Boolean>() {
@Override
public void onValueChange(ValueChangeEvent<Boolean> event) {
if (event.getValue()) {
useOverallLeaderboard(panel);
} else {
dontUseOverallLeaderboard(panel);
}
updateOverallLeaderboardDetailsVisibility(event.getValue(), panel);
}
});
updateOverallLeaderboardDetailsVisibility(useOverallLeaderboardCheckBox.getValue(), panel);
return panel;
}
protected void useOverallLeaderboard(final VerticalPanel panel) {
private void updateOverallLeaderboardDetailsVisibility(boolean overallLeaderboardDetailsVisible, Panel panel) {
if (overallLeaderboardDetailsVisible) {
useOverallLeaderboard(panel);
} else {
dontUseOverallLeaderboard(panel);
}
}
protected void useOverallLeaderboard(final Panel panel) {
panel.add(overallLeaderboardConfigPanel);
}
protected void dontUseOverallLeaderboard(final VerticalPanel panel) {
protected void dontUseOverallLeaderboard(final Panel panel) {
panel.remove(overallLeaderboardConfigPanel);
}
@@ -2,24 +2,19 @@ package com.sap.sailing.gwt.ui.adminconsole;
import java.util.Collection;
import com.google.gwt.user.client.ui.Widget;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.shared.LeaderboardGroupDTO;
public class LeaderboardGroupEditDialog extends LeaderboardGroupDialog {
public LeaderboardGroupEditDialog(LeaderboardGroupDTO group, Collection<LeaderboardGroupDTO> otherExistingGroups,
StringMessages stringMessages, DialogCallback<LeaderboardGroupDTO> callback) {
StringMessages stringMessages, DialogCallback<LeaderboardGroupDescriptor> callback) {
super(group, stringMessages, callback, otherExistingGroups);
// don't allow editing the overall leaderboard's scoring scheme if the group already uses an overall leaderboard
getOverallLeaderboardScoringSchemeListBox().setEnabled(!group.hasOverallLeaderboard());
nameEntryField = createTextBox(group.name);
descriptionEntryField = createTextArea(group.description);
}
@Override
protected Widget getAdditionalWidget() {
Widget result = super.getAdditionalWidget();
getUseOverallLeaderboardCheckBox().setValue(group.hasOverallLeaderboard(), /* fire event */ true);
return result;
displayLeaderboardsInReverseOrderCheckBox.setValue(group.displayLeaderboardsInReverseOrder);
displayLeaderboardsInReverseOrderCheckBox.setEnabled(false);
useOverallLeaderboardCheckBox.setValue(group.hasOverallLeaderboard());
}
}
@@ -1,57 +1,32 @@
package com.sap.sailing.gwt.ui.adminconsole;
import java.util.Arrays;
import com.google.gwt.core.client.GWT;
import com.google.gwt.safecss.shared.SafeStyles;
import com.google.gwt.safecss.shared.SafeStylesUtils;
import com.google.gwt.safehtml.shared.SafeHtml;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.text.shared.SafeHtmlRenderer;
import com.sap.sailing.gwt.ui.client.StringMessages;
public class LeaderboardRaceConfigImagesBarCell extends ImagesBarCell {
private StringMessages stringConstants;
public static final String ACTION_REMOVE = "ACTION_REMOVE";
public static final String ACTION_UNLINK = "ACTION_UNLINK";
public static final String ACTION_EDIT = "ACTION_EDIT";
private final StringMessages stringMessages;
private static AdminConsoleResources resources = GWT.create(AdminConsoleResources.class);
public LeaderboardRaceConfigImagesBarCell(StringMessages stringConstants) {
super();
this.stringConstants = stringConstants;
this.stringMessages = stringConstants;
}
public LeaderboardRaceConfigImagesBarCell(SafeHtmlRenderer<String> renderer, StringMessages stringConstants) {
public LeaderboardRaceConfigImagesBarCell(SafeHtmlRenderer<String> renderer, StringMessages stringMessages) {
super();
this.stringConstants = stringConstants;
this.stringMessages = stringMessages;
}
private static ImagesBarTemplates templates = GWT.create(ImagesBarTemplates.class);
private static AdminConsoleResources resources = GWT.create(AdminConsoleResources.class);
private static final SafeHtml ICON_EDIT = makeImage(resources.editIcon());
private static final SafeHtml ICON_UNLINK = makeImage(resources.unlinkIcon());
private static final SafeHtml ICON_REMOVE = makeImage(resources.removeIcon());
@Override
protected void render(com.google.gwt.cell.client.Cell.Context context, SafeHtml data, SafeHtmlBuilder sb) {
/*
* Always do a null check on the value. Cell widgets can pass null to cells if the underlying data contains a
* null, or if the data arrives out of order.
*/
if (data == null) {
return;
}
SafeStyles imgStyle = SafeStylesUtils
.fromTrustedString("float:left;cursor:hand;cursor:pointer;padding-right:5px;");
SafeHtml rendered = templates.cell("ACTION_EDIT", imgStyle, stringConstants.actionRaceEdit(), ICON_EDIT);
sb.append(rendered);
rendered = templates.cell("ACTION_UNLINK", imgStyle, stringConstants.actionRaceUnlink(), ICON_UNLINK);
sb.append(rendered);
rendered = templates.cell("ACTION_REMOVE", imgStyle, stringConstants.actionRaceRemove(), ICON_REMOVE);
sb.append(rendered);
protected Iterable<ImageSpec> getImageSpecs() {
return Arrays.asList(new ImageSpec(ACTION_EDIT, stringMessages.actionEdit(), makeImagePrototype(resources.editIcon())),
new ImageSpec(ACTION_UNLINK, stringMessages.actionRaceUnlink(), makeImagePrototype(resources.unlinkIcon())),
new ImageSpec(ACTION_REMOVE, stringMessages.actionRaceRemove(), makeImagePrototype(resources.removeIcon())));
}
}
@@ -3,6 +3,7 @@ package com.sap.sailing.gwt.ui.adminconsole;
import java.util.List;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.DoubleBox;
import com.google.gwt.user.client.ui.HasVerticalAlignment;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
@@ -18,14 +19,43 @@ import com.sap.sailing.gwt.ui.shared.RaceColumnDTO;
* @author Axel Uhl (D043530)
*
*/
public class RaceColumnInLeaderboardDialog extends DataEntryDialog<RaceColumnDTO> {
public class RaceColumnInLeaderboardDialog extends DataEntryDialog<RaceColumnInLeaderboardDialog.RaceColumnDescriptor> {
private final TextBox raceNameBox;
private final DoubleBox explicitFactorBox;
private final CheckBox isMedalRace;
private final StringMessages stringConstants;
private final StringMessages stringMessages;
private RaceColumnDTO raceColumnToEdit;
public static class RaceColumnDescriptor {
private String name;
private boolean isMedalRace;
private Double explicitFactor;
public RaceColumnDescriptor(String name, boolean isMedalRace, Double explicitFactor) {
super();
this.name = name;
this.isMedalRace = isMedalRace;
this.explicitFactor = explicitFactor;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean isMedalRace() {
return isMedalRace;
}
public void setMedalRace(boolean isMedalRace) {
this.isMedalRace = isMedalRace;
}
public Double getExplicitFactor() {
return explicitFactor;
}
public void setExplicitFactor(Double explicitFactor) {
this.explicitFactor = explicitFactor;
}
}
private static class RaceDialogValidator implements Validator<RaceColumnDTO> {
private static class RaceDialogValidator implements Validator<RaceColumnDescriptor> {
private StringMessages stringConstants;
private List<RaceColumnDTO> existingRaces;
@@ -36,16 +66,16 @@ public class RaceColumnInLeaderboardDialog extends DataEntryDialog<RaceColumnDTO
}
@Override
public String getErrorMessage(RaceColumnDTO valueToValidate) {
public String getErrorMessage(RaceColumnDescriptor valueToValidate) {
String errorMessage;
String racename = valueToValidate.getRaceColumnName();
String racename = valueToValidate.getName();
Boolean isMedalRace = valueToValidate.isMedalRace();
boolean isNameNotEmpty = racename != null & racename != "";
boolean medalRaceNotNull = isMedalRace != null;
boolean unique = true;
for (RaceColumnDTO raceColumn : existingRaces) {
if (raceColumn.getRaceColumnName().equals(valueToValidate.getRaceColumnName())) {
if (raceColumn.getRaceColumnName().equals(valueToValidate.getName())) {
unique = false;
}
}
@@ -64,21 +94,21 @@ public class RaceColumnInLeaderboardDialog extends DataEntryDialog<RaceColumnDTO
}
public RaceColumnInLeaderboardDialog(List<RaceColumnDTO> existingRaces, RaceColumnDTO raceColumnToEdit,
StringMessages stringConstants, DialogCallback<RaceColumnDTO> callback) {
super(stringConstants.actionRaceEdit(), null, stringConstants.ok(), stringConstants.cancel(),
new RaceDialogValidator(stringConstants, existingRaces), callback);
this.raceColumnToEdit = raceColumnToEdit;
this.stringConstants = stringConstants;
StringMessages stringMessages, DialogCallback<RaceColumnDescriptor> callback) {
super(stringMessages.actionRaceEdit(), null, stringMessages.ok(), stringMessages.cancel(),
new RaceDialogValidator(stringMessages, existingRaces), callback);
this.stringMessages = stringMessages;
raceNameBox = createTextBox(raceColumnToEdit.getRaceColumnName());
isMedalRace = createCheckbox(stringConstants.medalRace());
explicitFactorBox = raceColumnToEdit.getExplicitFactor() == null ?
createDoubleBox(/* visibleLength */ 4) : createDoubleBox(raceColumnToEdit.getExplicitFactor(), /* visibleLength */ 4);
isMedalRace = createCheckbox(stringMessages.medalRace());
isMedalRace.setValue(raceColumnToEdit.isMedalRace());
}
@Override
protected RaceColumnDTO getResult() {
raceColumnToEdit.name = raceNameBox.getValue();
raceColumnToEdit.setMedalRace(isMedalRace.getValue());
return raceColumnToEdit;
protected RaceColumnDescriptor getResult() {
return new RaceColumnDescriptor(raceNameBox.getValue(), isMedalRace.getValue(),
explicitFactorBox.getText().trim().length() == 0 ? null : explicitFactorBox.getValue());
}
@Override
@@ -88,10 +118,14 @@ public class RaceColumnInLeaderboardDialog extends DataEntryDialog<RaceColumnDTO
HorizontalPanel raceNamePanel = new HorizontalPanel();
raceNamePanel.setSpacing(3);
mainPanel.add(raceNamePanel);
raceNamePanel.add(new Label(stringConstants.name() + ":"));
raceNamePanel.add(new Label(stringMessages.name() + ":"));
raceNamePanel.add(raceNameBox);
HorizontalPanel factorPanel = new HorizontalPanel();
factorPanel.setSpacing(3);
mainPanel.add(factorPanel);
factorPanel.add(new Label(stringMessages.factor() + ":"));
factorPanel.add(explicitFactorBox);
alignAllPanelWidgetsVertically(raceNamePanel, HasVerticalAlignment.ALIGN_MIDDLE);
mainPanel.add(isMedalRace);
return mainPanel;
}
@@ -206,7 +206,7 @@ public class RaceColumnInRegattaSeriesDialog extends DataEntryDialog<Pair<Series
raceNamePrefixTextBox.setText(seriesName.substring(0, 1).toUpperCase());
}
} else {
seriesListBox.addItem("Please select a series");
seriesListBox.addItem(stringMessages.pleaseSelectASeries());
for (SeriesDTO series : regatta.series) {
seriesListBox.addItem(series.name);
}
@@ -378,6 +378,7 @@ public class RaceCourseManagementPanel extends AbstractRaceManagementPanel {
}
};
result.addColumn(buoyNameColumn, stringMessages.buoy());
final SafeHtmlCell buoyPositionCell = new SafeHtmlCell();
Column<BuoyDTO, SafeHtml> buoyPositionColumn = new Column<BuoyDTO, SafeHtml>(buoyPositionCell) {
@Override
@@ -388,6 +389,15 @@ public class RaceCourseManagementPanel extends AbstractRaceManagementPanel {
}
};
result.addColumn(buoyPositionColumn, stringMessages.position());
TextColumn<BuoyDTO> buoyColorColumn = new TextColumn<BuoyDTO>() {
@Override
public String getValue(BuoyDTO buoyDTO) {
return buoyDTO.displayColor != null ? buoyDTO.displayColor : "";
}
};
result.addColumn(buoyColorColumn, stringMessages.color());
return result;
}
@@ -1,56 +1,30 @@
package com.sap.sailing.gwt.ui.adminconsole;
import java.util.Arrays;
import com.google.gwt.core.client.GWT;
import com.google.gwt.safecss.shared.SafeStyles;
import com.google.gwt.safecss.shared.SafeStylesUtils;
import com.google.gwt.safehtml.shared.SafeHtml;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.text.shared.SafeHtmlRenderer;
import com.sap.sailing.gwt.ui.client.StringMessages;
public class RegattaConfigImagesBarCell extends ImagesBarCell {
static final String ACTION_REMOVE = "ACTION_REMOVE";
static final String ACTION_EDIT = "ACTION_EDIT";
private final StringMessages stringMessages;
private static AdminConsoleResources resources = GWT.create(AdminConsoleResources.class);
public RegattaConfigImagesBarCell(StringMessages stringConstants) {
super();
this.stringMessages = stringConstants;
}
public RegattaConfigImagesBarCell(SafeHtmlRenderer<String> renderer, StringMessages stringConstants) {
public RegattaConfigImagesBarCell(SafeHtmlRenderer<String> renderer, StringMessages stringMessages) {
super();
this.stringMessages = stringConstants;
this.stringMessages = stringMessages;
}
private static ImagesBarTemplates templates = GWT.create(ImagesBarTemplates.class);
private static AdminConsoleResources resources = GWT.create(AdminConsoleResources.class);
private static final SafeHtml ICON_EDIT = makeImage(resources.editIcon());
private static final SafeHtml ICON_REMOVE = makeImage(resources.removeIcon());
private StringMessages stringMessages;
@Override
protected void render(com.google.gwt.cell.client.Cell.Context context, SafeHtml data, SafeHtmlBuilder sb) {
/*
* Always do a null check on the value. Cell widgets can pass null to
* cells if the underlying data contains a null, or if the data arrives
* out of order.
*/
if (data == null) {
return;
}
SafeStyles imgStyle = SafeStylesUtils.fromTrustedString("float:left;cursor:hand;cursor:pointer;padding-right:5px;");
SafeHtml rendered = templates.cell(ACTION_EDIT, imgStyle, stringMessages.actionEdit(), ICON_EDIT);
sb.append(rendered);
rendered = templates.cell(ACTION_REMOVE, imgStyle, stringMessages.actionRemove(), ICON_REMOVE);
sb.append(rendered);
protected Iterable<ImageSpec> getImageSpecs() {
return Arrays.asList(new ImageSpec(ACTION_EDIT, stringMessages.actionEdit(), makeImagePrototype(resources.editIcon())),
new ImageSpec(ACTION_REMOVE, stringMessages.actionRemove(), makeImagePrototype(resources.removeIcon())));
}
}
@@ -499,17 +499,17 @@ public class TracTracEventManagementPanel extends AbstractEventManagementPanel {
private boolean checkBoatClassMatch(TracTracRaceRecordDTO tracTracRecord, RegattaDTO selectedRegatta) {
Iterable<String> boatClassNames = tracTracRecord.boatClassNames;
if(boatClassNames != null && Util.size(boatClassNames) > 0) {
if (boatClassNames != null && Util.size(boatClassNames) > 0) {
String tracTracBoatClass = boatClassNames.iterator().next();
if(selectedRegatta == null) {
if (selectedRegatta == null) {
// in case no regatta has been selected we check if there would be a matching regatta
for(RegattaDTO regatta: allRegattas) {
if(tracTracBoatClass.equalsIgnoreCase(regatta.boatClass.name)) {
for (RegattaDTO regatta : allRegattas) {
if (tracTracBoatClass.equalsIgnoreCase(regatta.boatClass.name)) {
return false;
}
}
} else {
if(!tracTracBoatClass.equalsIgnoreCase(selectedRegatta.boatClass.name)) {
if (!tracTracBoatClass.equalsIgnoreCase(selectedRegatta.boatClass.name)) {
return false;
}
}
@@ -530,21 +530,22 @@ public class TracTracEventManagementPanel extends AbstractEventManagementPanel {
List<TracTracRaceRecordDTO> racesWithNotMatchingBoatClasses = new ArrayList<TracTracRaceRecordDTO>();
for (TracTracRaceRecordDTO rr : raceList.getList()) {
if (raceTable.getSelectionModel().isSelected(rr)) {
if(!checkBoatClassMatch(rr, selectedRegatta))
if (!checkBoatClassMatch(rr, selectedRegatta)) {
racesWithNotMatchingBoatClasses.add(rr);
}
}
}
if(racesWithNotMatchingBoatClasses.size() > 0) {
if (racesWithNotMatchingBoatClasses.size() > 0) {
String warningText = "WARNING\n";
if(selectedRegatta != null) {
if (selectedRegatta != null) {
warningText += stringMessages.boatClassDoesNotMatchSelectedRegatta(selectedRegatta.boatClass.name, selectedRegatta.name);
} else {
warningText += stringMessages.regattaExistForSelectedBoatClass();
}
warningText += "\n\n";
warningText += stringMessages.races() + "\n";
for(TracTracRaceRecordDTO record: racesWithNotMatchingBoatClasses) {
for (TracTracRaceRecordDTO record : racesWithNotMatchingBoatClasses) {
warningText += record.name + "\n";
}
if(!Window.confirm(warningText)) {
@@ -297,7 +297,7 @@ public class TrackedRacesListComposite extends SimplePanel implements Component<
TextColumn<RaceDTO> hasWindDataColumn = new TextColumn<RaceDTO>() {
@Override
public String getValue(RaceDTO raceDTO) {
if (raceDTO.isTracked && raceDTO.trackedRace != null && raceDTO.trackedRace.hasWindData == true)
if (raceDTO.trackedRace != null && raceDTO.trackedRace.hasWindData == true)
return stringMessages.yes();
else
return stringMessages.no();
@@ -307,17 +307,18 @@ public class TrackedRacesListComposite extends SimplePanel implements Component<
TextColumn<RaceDTO> hasGPSDataColumn = new TextColumn<RaceDTO>() {
@Override
public String getValue(RaceDTO raceDTO) {
if (raceDTO.isTracked && raceDTO.trackedRace != null && raceDTO.trackedRace.hasGPSData == true)
if (raceDTO.trackedRace != null && raceDTO.trackedRace.hasGPSData == true) {
return stringMessages.yes();
else
} else {
return stringMessages.no();
}
}
};
TextColumn<RaceDTO> raceLiveDelayColumn = new TextColumn<RaceDTO>() {
@Override
public String getValue(RaceDTO raceDTO) {
if (raceDTO.isTracked && raceDTO.trackedRace.delayToLiveInMs > 0) {
if (raceDTO.isTracked && raceDTO.trackedRace != null && raceDTO.trackedRace.delayToLiveInMs > 0) {
return "" + raceDTO.trackedRace.delayToLiveInMs / 1000;
}
return "";
@@ -39,7 +39,8 @@ public class ColorMap<T> {
* Returns a color that is computed once.
*
* @param object
* An ID unique for a competitor.
* An ID unique for something this map is to provide a color for. Color assignment is based on the
* object's {@link Object#equals(Object)} and {@link Object#hashCode()}.
* @return A color in hex/html-format (e.g. #ff0000)
*/
public String getColorByID(T object) {
@@ -22,6 +22,7 @@ import com.google.gwt.user.client.ui.IntegerBox;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.LongBox;
import com.google.gwt.user.client.ui.RadioButton;
import com.google.gwt.user.client.ui.TextArea;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.Widget;
@@ -296,15 +297,12 @@ public abstract class DataEntryDialog<T> {
public FlowPanel createHeadline(String headlineText, boolean regularHeadline) {
FlowPanel headlinePanel = new FlowPanel();
Label headlineLabel = new Label(headlineText);
if (regularHeadline) {
headlinePanel.addStyleName("dialogInnerHeadline");
} else {
headlinePanel.addStyleName("dialogInnerHeadlineOther");
}
headlinePanel.addStyleName("dialogInnerHeadline");
} else {
headlinePanel.addStyleName("dialogInnerHeadlineOther");
}
headlinePanel.add(headlineLabel);
return headlinePanel;
}
@@ -323,6 +321,20 @@ public abstract class DataEntryDialog<T> {
return result;
}
public RadioButton createRadioButton(String radioButtonGroupName, String radioButtonLabel) {
RadioButton result = new RadioButton(radioButtonGroupName, radioButtonLabel);
result.setWordWrap(false);
result.addValueChangeHandler(new ValueChangeHandler<Boolean>() {
@Override
public void onValueChange(ValueChangeEvent<Boolean> event) {
validate();
}
});
AbstractEntryPoint.linkEnterToButton(getOkButton(), result);
AbstractEntryPoint.linkEscapeToButton(getCancelButton(), result);
return result;
}
public ListBox createListBox(boolean isMultipleSelect) {
ListBox result = new ListBox(isMultipleSelect);
result.addChangeHandler(new ChangeHandler() {
@@ -336,13 +348,12 @@ public abstract class DataEntryDialog<T> {
return result;
}
protected void alignAllPanelWidgetsVertically(HorizontalPanel panel, HasVerticalAlignment.VerticalAlignmentConstant alignment) {
public void alignAllPanelWidgetsVertically(HorizontalPanel panel, HasVerticalAlignment.VerticalAlignmentConstant alignment) {
for(int i = 0; i < panel.getWidgetCount(); i++) {
panel.setCellVerticalAlignment(panel.getWidget(i), alignment);
}
}
/**
* Can contribute an additional widget to be displayed underneath the text entry field. If <code>null</code> is
* returned, no additional widget will be displayed. This is the default behavior of this default implementation.
@@ -62,6 +62,10 @@ public class DetailTypeFormatter {
return stringMessages.maximumSpeedOverGroundInKnots();
case TOTAL_TIME_SAILED_DOWNWIND_IN_SECONDS:
return stringMessages.totalTimeSailedDownwindInSeconds();
case TOTAL_TIME_SAILED_UPWIND_IN_SECONDS:
return stringMessages.totalTimeSailedUpwindInSeconds();
case TOTAL_TIME_SAILED_REACHING_IN_SECONDS:
return stringMessages.totalTimeSailedReachingInSeconds();
case TOTAL_TIME_SAILED_IN_SECONDS:
return stringMessages.totalTimeSailedInSeconds();
}
@@ -50,4 +50,23 @@ public class LogoAndTitlePanel extends FlowPanel {
}
}
public String getTitle() {
return titleLabel != null ? titleLabel.getText() : null;
}
public void setTitle(String title) {
if(titleLabel != null) {
titleLabel.setText(title);
}
}
public String getSubTitle() {
return subTitleLabel != null ? subTitleLabel.getText() : null;
}
public void setSubTitle(String subTitle) {
if(subTitleLabel != null) {
subTitleLabel.setText(subTitle);
}
}
}
@@ -13,7 +13,7 @@ import com.sap.sailing.gwt.ui.shared.RaceTimesInfoDTO;
import com.sap.sailing.gwt.ui.shared.components.SettingsDialogComponent;
public class RaceTimePanel extends TimePanel<RaceTimePanelSettings> implements RaceSelectionChangeListener, RaceTimesInfoProviderListener {
private RaceTimesInfoProvider raceTimesInfoProvider;
private final RaceTimesInfoProvider raceTimesInfoProvider;
private RegattaAndRaceIdentifier selectedRace;
private boolean autoAdjustPlayMode;
private RaceTimesInfoDTO lastRaceTimesInfo;
@@ -84,9 +84,10 @@ public class RaceTimePanel extends TimePanel<RaceTimePanelSettings> implements R
@Override
public void onTimeZoomChanged(Date zoomStartTimepoint, Date zoomEndTimepoint) {
super.onTimeZoomChanged(zoomStartTimepoint, zoomEndTimepoint);
isTimeZoomed = true;
timeSlider.setZoomed(true);
timer.setAutoAdvance(false);
// timer.setAutoAdvance(false);
setMinMax(zoomStartTimepoint, zoomEndTimepoint, false);
timeSlider.clearMarkersAndLabelsAndTicks();
redrawAllMarkers(lastRaceTimesInfo);
@@ -94,9 +95,10 @@ public class RaceTimePanel extends TimePanel<RaceTimePanelSettings> implements R
@Override
public void onTimeZoomReset() {
super.onTimeZoomReset();
isTimeZoomed = false;
timeSlider.setZoomed(false);
timer.setAutoAdvance(true);
// timer.setAutoAdvance(true);
updateMinMax(this.lastRaceTimesInfo);
timeSlider.clearMarkersAndLabelsAndTicks();
redrawAllMarkers(lastRaceTimesInfo);
@@ -11,44 +11,46 @@ import java.util.Set;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.RepeatingCommand;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
import com.sap.sailing.domain.common.RaceIdentifier;
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
import com.sap.sailing.gwt.ui.shared.FleetDTO;
import com.sap.sailing.gwt.ui.shared.LeaderboardDTO;
import com.sap.sailing.gwt.ui.shared.RaceColumnDTO;
import com.sap.sailing.gwt.ui.shared.RaceTimesInfoDTO;
public class RaceTimesInfoProvider {
private SailingServiceAsync sailingService;
private ErrorReporter errorReporter;
private final SailingServiceAsync sailingService;
private final ErrorReporter errorReporter;
private Set<RegattaAndRaceIdentifier> raceIdentifiers;
private long requestInterval;
private final Set<RegattaAndRaceIdentifier> raceIdentifiers;
private long requestIntervalInMillis;
private HashMap<RegattaAndRaceIdentifier, RaceTimesInfoDTO> raceTimesInfos;
private final HashMap<RegattaAndRaceIdentifier, RaceTimesInfoDTO> raceTimesInfos;
private Set<RaceTimesInfoProviderListener> listeners;
private final Set<RaceTimesInfoProviderListener> listeners;
/**
* The <code>raceIdentifiers</code> has to be <code>not-null</code>, but can be empty.
* The <code>raceIdentifiers</code> has to be non-<code>null</code>, but can be empty.
*/
public RaceTimesInfoProvider(SailingServiceAsync sailingService, ErrorReporter errorReporter, Collection<RegattaAndRaceIdentifier> raceIdentifiers, long requestInterval) {
public RaceTimesInfoProvider(SailingServiceAsync sailingService, ErrorReporter errorReporter,
Collection<RegattaAndRaceIdentifier> raceIdentifiers, long requestIntervalInMillis) {
this.sailingService = sailingService;
this.errorReporter = errorReporter;
this.raceIdentifiers = new HashSet<RegattaAndRaceIdentifier>(raceIdentifiers);
this.requestInterval = requestInterval;
this.requestIntervalInMillis = requestIntervalInMillis;
raceTimesInfos = new HashMap<RegattaAndRaceIdentifier, RaceTimesInfoDTO>();
listeners = new HashSet<RaceTimesInfoProviderListener>();
RepeatingCommand command = new RepeatingCommand() {
@Override
public boolean execute() {
readTimesInfos();
Scheduler.get().scheduleFixedPeriod(this, (int) RaceTimesInfoProvider.this.requestInterval);
Scheduler.get().scheduleFixedPeriod(this, (int) RaceTimesInfoProvider.this.requestIntervalInMillis);
// don't execute *this* particular scheduled repeating command again; the line above re-scheduled already
return false;
}
};
Scheduler.get().scheduleFixedPeriod(command, (int) this.requestInterval);
forceTimesInfosUpdate();
command.execute();
}
/**
@@ -113,7 +115,7 @@ public class RaceTimesInfoProvider {
}
public long getRequestInterval() {
return requestInterval;
return requestIntervalInMillis;
}
/**
@@ -121,7 +123,7 @@ public class RaceTimesInfoProvider {
* @param requestInterval The new request interval
*/
public void setRequestInterval(long requestInterval){
this.requestInterval = requestInterval;
this.requestIntervalInMillis = requestInterval;
}
/**
@@ -177,4 +179,22 @@ public class RaceTimesInfoProvider {
}
}
public RegattaAndRaceIdentifier getFirstStartedAndUnfinishedRace(LeaderboardDTO leaderboard) {
RegattaAndRaceIdentifier firstStartedAndUnfinishedRace = null;
Map<RegattaAndRaceIdentifier, RaceTimesInfoDTO> raceTimesInfos = getRaceTimesInfos();
for (RaceColumnDTO race : leaderboard.getRaceList()) {
for (FleetDTO fleet : race.getFleets()) {
RegattaAndRaceIdentifier raceIdentifier = race.getRaceIdentifier(fleet);
if (raceIdentifier != null) {
RaceTimesInfoDTO raceTimes = raceTimesInfos.get(raceIdentifier);
if (raceTimes != null && raceTimes.startOfTracking != null && raceTimes.endOfRace == null) {
firstStartedAndUnfinishedRace = raceIdentifier;
break;
}
}
}
}
return firstStartedAndUnfinishedRace;
}
}
@@ -203,7 +203,8 @@ public interface SailingService extends RemoteService {
void removeLeaderboardGroup(String groupName);
LeaderboardGroupDTO createLeaderboardGroup(String groupName, String description, int[] overallLeaderboardDiscardThresholds,
LeaderboardGroupDTO createLeaderboardGroup(String groupName, String description,
boolean displayGroupsInReverseOrder, int[] overallLeaderboardDiscardThresholds,
ScoringSchemeType overallLeaderboardScoringSchemeType);
void updateLeaderboardGroup(String oldName, String newName, String description, List<String> leaderboardNames,
@@ -264,4 +265,6 @@ public interface SailingService extends RemoteService {
StrippedLeaderboardDTO getLeaderboard(String leaderboardName);
void suppressCompetitorInLeaderboard(String leaderboardName, String competitorIdAsString, boolean suppressed);
void updateLeaderboardColumnFactor(String leaderboardName, String columnName, Double newFactor);
}
@@ -302,10 +302,11 @@ public interface SailingServiceAsync {
/**
* Creates a new group with the name <code>groupname</code>, the description <code>description</code> and an empty list of leaderboards.<br/>
* @param displayGroupsInReverseOrder TODO
*/
void createLeaderboardGroup(String groupName, String description,
int[] overallLeaderboardDiscardThresholds, ScoringSchemeType overallLeaderboardScoringSchemeType,
AsyncCallback<LeaderboardGroupDTO> callback);
boolean displayGroupsInReverseOrder, int[] overallLeaderboardDiscardThresholds,
ScoringSchemeType overallLeaderboardScoringSchemeType, AsyncCallback<LeaderboardGroupDTO> callback);
/**
* Updates the data of the group with the name <code>oldName</code>.
@@ -421,4 +422,8 @@ public interface SailingServiceAsync {
void suppressCompetitorInLeaderboard(String leaderboardName, String competitorIdAsString, boolean suppressed, AsyncCallback<Void> asyncCallback);
void updateLeaderboardColumnFactor(String leaderboardName, String columnName, Double newFactor,
AsyncCallback<Void> callback);
}
@@ -389,6 +389,8 @@ public interface StringMessages extends Messages {
String racesWithNamePrefix();
String maximumSpeedOverGroundInKnots();
String totalTimeSailedDownwindInSeconds();
String totalTimeSailedUpwindInSeconds();
String totalTimeSailedReachingInSeconds();
String totalTimeSailedInSeconds();
String overallDetailsToShow();
String hhmmssUnit();
@@ -405,4 +407,23 @@ public interface StringMessages extends Messages {
String unsuppress();
String suppressedCompetitors();
String pleaseSelectASeries();
String pleaseSelectARace();
String displayGroupsInReverseOrder();
String actionConfigureUrl();
String configureLeaderboardUrlDescription();
String embedded();
String additionalUrlSettings();
String restrictToSingleSelectedRace();
String expandSelectedRace();
String showRaceDetails();
String numberOfRaces();
String numberOfRacesMustBeNonNegativeNumber();
String chooseTheWayYouSelectRaces();
String selectFromAllRaces();
String selectANumberOfRaces();
String numberOfLastNRaces();
String expandLastRace();
String factor();
String errorUpdatingIsMedalRace(String message);
String maneuverLoss();
}
@@ -390,6 +390,8 @@ actionAddRaces=Add races
racesWithNamePrefix=races with name prefix
maximumSpeedOverGroundInKnots=Max SOG
totalTimeSailedDownwindInSeconds=Total time downwind
totalTimeSailedUpwindInSeconds=Total time upwind
totalTimeSailedReachingInSeconds=Total time reaching
totalTimeSailedInSeconds=Total time
hhmmssUnit=hh:mm:ss
actionAddWindData=Add wind data
@@ -405,5 +407,23 @@ isPublic=Is public
suppress=Suppress
unsuppress=Unsuppress
suppressedCompetitors=Suppressed Competitors
pleaseSelectASeries=Please select a series.
pleaseSelectASeries=Please select a series
pleaseSelectARace=Please select a race
displayGroupsInReverseOrder=Display groups in reverse order
actionConfigureUrl=Configure URL
configureLeaderboardUrlDescription=Configure a leaderboard and obtain the resulting URL from the link
embedded=Embedded
additionalUrlSettings=Additional URL Settings
restrictToSingleSelectedRace=Restrict to single selected race
expandSelectedRace=Expand selected race
showRaceDetails=Allow for race details
numberOfRaces=Number of races
numberOfRacesMustBeNonNegativeNumber=Please enter the number of last races to be displayed
chooseTheWayYouSelectRaces=Choose the way you select races
selectFromAllRaces=Select from all races
selectANumberOfRaces=Select a number of races
numberOfLastNRaces=Number of most current races to show
expandLastRace=Always expand last race shown
factor=Factor
errorUpdatingIsMedalRace=Error updating the medal race setting: {0}
maneuverLoss=Maneuver loss
@@ -389,8 +389,10 @@ overallStandings=Gesamtstand
type=Typ
actionAddRaces=Rennen hinzuf�gen
racesWithNamePrefix=Rennen mit Namens-Pr�fix
maximumSpeedOverGroundInKnots=Max F�G
maximumSpeedOverGroundInKnots=Max FüG
totalTimeSailedDownwindInSeconds=Gesamtzeit Vorwind
totalTimeSailedUpwindInSeconds=Gesamtzeit Kreuz
totalTimeSailedReachingInSeconds=Gesamtzeit Holeschlag
totalTimeSailedInSeconds=Gesamtzeit
hhmmssUnit=hh:mm:ss
actionAddWindData=Winddaten hinzufügen
@@ -406,4 +408,23 @@ isPublic=Öffentlich zugänglich
suppress=Entfernen
unsuppress=Wieder hinzufügen
suppressedCompetitors=Unterdrückte Wettkampfteilnehmer
pleaseSelectASeries=Bitte wählen Sie eine Serie aus.
pleaseSelectASeries=Bitte wählen Sie eine Serie aus
pleaseSelectARace=Bitte wählen Sie ein Rennen aus
displayGroupsInReverseOrder=Zeige die Gruppen in umgekehrter Reihenfolge
actionConfigureUrl=URL konfigurieren
configureLeaderboardUrlDescription=Ergebnisansicht konfigurieren. Die fertige URL kann dem Link entnommen werden.
embedded=Eingebettet
additionalUrlSettings=Weitere URL-relevante Einstellungen
restrictToSingleSelectedRace=Nur einzelnes ausgewähltes Rennen zeigen
expandSelectedRace=Details des ausgewähltes Rennens zeigen
showRaceDetails=Details anbieten
numberOfRaces=Anzahl Rennen
numberOfRacesMustBeNonNegativeNumber=Bitte Anzahl der anzuzeigenden letzten Rennen eingeben
chooseTheWayYouSelectRaces=Wie wollen die anzuzeigenden Rennen ausgewählt werden?
selectFromAllRaces=Aus allen Rennen auswählen
selectANumberOfRaces=Eine Anzahl anzuzeigender Rennen auswählen
numberOfLastNRaces=Anzahl der zu zeigenden aktuellsten Rennen
expandLastRace=Immer letztes gezeigtes Rennen expandieren
factor=Faktor
errorUpdatingIsMedalRace=Fehler beim Ändern der Medaillenrennen-Einstellung: {0}
maneuverLoss=Manöververlust
@@ -0,0 +1,37 @@
package com.sap.sailing.gwt.ui.leaderboard;
import java.util.ArrayList;
import java.util.List;
import com.sap.sailing.domain.common.impl.Util;
import com.sap.sailing.gwt.ui.shared.LeaderboardDTO;
import com.sap.sailing.gwt.ui.shared.RaceColumnDTO;
public abstract class AbstractRaceColumnSelection implements RaceColumnSelection {
@Override
public boolean isSelected(String raceColumnName) {
return Util.contains(getSelectedRaceColumnNames(), raceColumnName);
}
@Override
public Iterable<String> getSelectedRaceColumnNames() {
List<String> names = new ArrayList<String>();
for (RaceColumnDTO column : getSelectedRaceColumns()) {
names.add(column.name);
}
return names;
}
@Override
public Iterable<RaceColumnDTO> getSelectedRaceColumnsOrderedAsInLeaderboard(LeaderboardDTO leaderboard) {
Iterable<RaceColumnDTO> selectedColumns = getSelectedRaceColumns();
List<RaceColumnDTO> result = new ArrayList<RaceColumnDTO>();
for (RaceColumnDTO column : leaderboard.getRaceList()) {
if (Util.contains(selectedColumns, column)) {
result.add(column);
}
}
return result;
}
}
@@ -0,0 +1,31 @@
package com.sap.sailing.gwt.ui.leaderboard;
import com.google.gwt.i18n.client.NumberFormat;
import com.sap.sailing.domain.common.SortingOrder;
import com.sap.sailing.gwt.ui.client.NumberFormatterFactory;
import com.sap.sailing.gwt.ui.shared.LeaderboardRowDTO;
public class AbstractTimeInHoursMinutesSecondsColumn extends FormattedDoubleLegDetailColumn {
public AbstractTimeInHoursMinutesSecondsColumn(String title, String unit,
com.sap.sailing.gwt.ui.leaderboard.LegDetailColumn.LegDetailField<Double> field, int decimals, SortingOrder preferredSortingOrder,
String headerStyle, String columnStyle) {
super(title, unit, field, decimals, preferredSortingOrder, headerStyle, columnStyle);
}
@Override
public String getStringValueToRender(LeaderboardRowDTO object) {
Double timeInSeconds = getDoubleValue(object);
String result;
if (timeInSeconds == null) {
result = null;
} else {
int hh = (int) (timeInSeconds / 3600);
int mm = (int) ((timeInSeconds - 3600 * hh) / 60);
int ss = (int) (timeInSeconds - 3600 * hh - 60 * mm);
NumberFormat numberFormat = NumberFormatterFactory.getDecimalFormat(2, 0);
result = "" + numberFormat.format(hh) + ":" + numberFormat.format(mm) + ":" + numberFormat.format(ss);
}
return result;
}
}
@@ -1,27 +1,37 @@
package com.sap.sailing.gwt.ui.leaderboard;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import com.google.gwt.dom.client.Style.Float;
import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
import com.sap.sailing.domain.common.impl.Util.Pair;
import com.sap.sailing.gwt.ui.actions.AsyncActionsExecutor;
import com.sap.sailing.gwt.ui.client.CompetitorSelectionProvider;
import com.sap.sailing.gwt.ui.client.ErrorReporter;
import com.sap.sailing.gwt.ui.client.RaceSelectionModel;
import com.sap.sailing.gwt.ui.client.RaceSelectionProvider;
import com.sap.sailing.gwt.ui.client.RaceTimesCalculationUtil;
import com.sap.sailing.gwt.ui.client.RaceTimesInfoProvider;
import com.sap.sailing.gwt.ui.client.RaceTimesInfoProviderListener;
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.client.TimeRangeChangeListener;
import com.sap.sailing.gwt.ui.client.TimeRangeWithZoomModel;
import com.sap.sailing.gwt.ui.client.TimeRangeWithZoomProvider;
import com.sap.sailing.gwt.ui.client.Timer;
import com.sap.sailing.gwt.ui.shared.RaceTimesInfoDTO;
import com.sap.sailing.gwt.ui.shared.charts.MultiChartPanel;
import com.sap.sailing.gwt.ui.shared.charts.MultiChartSettings;
import com.sap.sailing.gwt.ui.shared.components.ComponentToolbar;
@@ -32,27 +42,36 @@ import com.sap.sailing.gwt.ui.shared.components.ComponentToolbar;
* @author Benjamin Ebling, Axel Uhl (d043530)
*
*/
public class CompareCompetitorsChartDialog extends DialogBoxExt {
public class CompareCompetitorsChartDialog extends DialogBoxExt implements RaceTimesInfoProviderListener {
private final RaceSelectionProvider raceSelectionProvider;
private final SailingServiceAsync sailingService;
private final TimeRangeWithZoomProvider timeRangeWithZoomProvider;
private final Timer timer;
private final ErrorReporter errorReporter;
private final MultiChartPanel multiChartPanel;
final ListBox racesListBox;
private final ListBox racesListBox;
public CompareCompetitorsChartDialog(SailingServiceAsync sailingService,
List<RegattaAndRaceIdentifier> races, final CompetitorSelectionProvider competitorSelectionProvider, Timer timer,
StringMessages stringConstants, ErrorReporter errorReporter) {
List<RegattaAndRaceIdentifier> races, final RaceTimesInfoProvider raceTimesInfoProvider, final CompetitorSelectionProvider competitorSelectionProvider,
Timer timer, StringMessages stringConstants, ErrorReporter errorReporter) {
super(new Label(stringConstants.close()));
this.sailingService = sailingService;
this.timer = timer;
this.errorReporter = errorReporter;
this.timeRangeWithZoomProvider = new TimeRangeWithZoomModel();
this.setPopupPosition(15, 15);
this.setHTML(stringConstants.compareCompetitors());
this.setWidth(Window.getClientWidth() - 250 + "px");
this.setAnimationEnabled(true);
raceTimesInfoProvider.addRaceTimesInfoProviderListener(this);
raceSelectionProvider = new RaceSelectionModel();
raceSelectionProvider.setAllRaces(races);
multiChartPanel = new MultiChartPanel(sailingService, new AsyncActionsExecutor(), competitorSelectionProvider, raceSelectionProvider,
timer, new TimeRangeWithZoomModel(), stringConstants, errorReporter, false, false);
timer, timeRangeWithZoomProvider, stringConstants, errorReporter, false, false);
multiChartPanel.setSize("100%", "100%");
VerticalPanel contentPanel = new VerticalPanel();
@@ -66,7 +85,7 @@ public class CompareCompetitorsChartDialog extends DialogBoxExt {
HorizontalPanel raceSelectionPanel = new HorizontalPanel();
raceSelectionPanel.setSpacing(3);
racesListBox = new ListBox();
raceSelectionPanel.add(new Label("Select a race:"));
raceSelectionPanel.add(new Label(stringConstants.pleaseSelectARace() + ":"));
raceSelectionPanel.add(racesListBox);
for (RegattaAndRaceIdentifier race : raceSelectionProvider.getAllRaces()) {
racesListBox.addItem(race.toString());
@@ -101,7 +120,43 @@ public class CompareCompetitorsChartDialog extends DialogBoxExt {
}
private void selectRace(final RegattaAndRaceIdentifier selectedRace) {
raceSelectionProvider.setSelection(Collections.singletonList(selectedRace));
sailingService.getRaceTimesInfo(selectedRace, new AsyncCallback<RaceTimesInfoDTO>() {
@Override
public void onSuccess(RaceTimesInfoDTO raceTimesInfo) {
updateMinMax(raceTimesInfo);
raceSelectionProvider.setSelection(Collections.singletonList(selectedRace));
}
@Override
public void onFailure(Throwable caught) {
errorReporter.reportError("Error obtaining race times: " + caught.getMessage(), true /*silentMode */);
}
});
}
private void updateMinMax(RaceTimesInfoDTO newRaceTimesInfo) {
Pair<Date, Date> raceMinMax = RaceTimesCalculationUtil.caluclateRaceMinMax(timer, newRaceTimesInfo);
Date min = raceMinMax.getA();
Date max = raceMinMax.getB();
// never reduce max if it was already set
if (min != null && max != null && (timeRangeWithZoomProvider.getToTime() == null || timeRangeWithZoomProvider.getToTime().before(max))) {
timeRangeWithZoomProvider.setTimeRange(min, max, new TimeRangeChangeListener[0]);
}
}
private RegattaAndRaceIdentifier getSelectedRace() {
return raceSelectionProvider.getSelectedRaces().size() > 0 ? raceSelectionProvider.getSelectedRaces().get(0) : null;
}
@Override
public void raceTimesInfosReceived(Map<RegattaAndRaceIdentifier, RaceTimesInfoDTO> raceTimesInfos) {
RegattaAndRaceIdentifier selectedRace = getSelectedRace();
if(selectedRace != null) {
updateMinMax(raceTimesInfos.get(selectedRace));
}
}
}
@@ -0,0 +1,92 @@
package com.sap.sailing.gwt.ui.leaderboard;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.sap.sailing.gwt.ui.leaderboard.LeaderboardSettings.RaceColumnSelectionStrategies;
import com.sap.sailing.gwt.ui.shared.LeaderboardDTO;
import com.sap.sailing.gwt.ui.shared.RaceColumnDTO;
/**
* An explicit selection of race columns. Requests to add to or clear the selection are respected. If an updated version
* of the leaderboard is announced, race columns added are implicitly also added to this selection.
*
* @author Axel Uhl (D043530)
*
*/
public class ExplicitRaceColumnSelection extends AbstractRaceColumnSelection implements RaceColumnSelection {
private final Map<String, RaceColumnDTO> selectedRaceColumns;
public ExplicitRaceColumnSelection() {
this.selectedRaceColumns = new HashMap<String, RaceColumnDTO>();
}
@Override
public Iterable<RaceColumnDTO> getSelectedRaceColumns() {
return selectedRaceColumns.values();
}
@Override
public void requestRaceColumnSelection(String raceColumnName, RaceColumnDTO column) {
selectedRaceColumns.put(raceColumnName, column);
}
@Override
public void requestClear() {
selectedRaceColumns.clear();
}
private List<RaceColumnDTO> getRacesAddedNew(LeaderboardDTO oldLeaderboard, LeaderboardDTO newLeaderboard) {
List<RaceColumnDTO> result = new ArrayList<RaceColumnDTO>();
for (RaceColumnDTO s : newLeaderboard.getRaceList()) {
if (oldLeaderboard == null || !leaderboardContainsColumnNamed(oldLeaderboard, s.getRaceColumnName())) {
result.add(s);
}
}
return result;
}
private boolean leaderboardContainsColumnNamed(LeaderboardDTO leaderboard, String raceColumnName) {
for (RaceColumnDTO column : leaderboard.getRaceList()) {
if (column.getRaceColumnName().equals(raceColumnName)) {
return true;
}
}
return false;
}
/**
* Considers the {@link #preSelectedRace} field as follows: if <code>null</code>, all race columns that
* <code>leaderboard</code> adds on top of the existing <code>oldLeaderboard</code> are returned.
* Otherwise, the column list obtained as described before is filtered such that only columns pass whose race
* identifier equals {@link #preSelectedRace}.
*
* @param newLeaderboard
* the new leaderboard, not yet {@link #setLeaderboard(LeaderboardDTO) set}, so as to allow for a
* comparison
*/
protected List<RaceColumnDTO> getRaceColumnsToAddImplicitly(LeaderboardDTO newLeaderboard, LeaderboardDTO oldLeaderboard) {
List<RaceColumnDTO> columnsToAddImplicitly = getRacesAddedNew(oldLeaderboard, newLeaderboard);
return columnsToAddImplicitly;
}
@Override
public void autoUpdateRaceColumnSelectionForUpdatedLeaderboard(LeaderboardDTO oldLeaderboard, LeaderboardDTO newLeaderboard) {
for (RaceColumnDTO selectedRaceColumn : getRaceColumnsToAddImplicitly(newLeaderboard, oldLeaderboard)) {
requestRaceColumnSelection(selectedRaceColumn.name, selectedRaceColumn);
}
}
@Override
public RaceColumnSelectionStrategies getType() {
return RaceColumnSelectionStrategies.EXPLICIT;
}
@Override
public Integer getNumberOfLastRaceColumnsToShow() {
return null;
}
}
@@ -0,0 +1,43 @@
package com.sap.sailing.gwt.ui.leaderboard;
import java.util.Iterator;
import java.util.List;
import com.sap.sailing.domain.common.RaceIdentifier;
import com.sap.sailing.gwt.ui.shared.LeaderboardDTO;
import com.sap.sailing.gwt.ui.shared.RaceColumnDTO;
/**
* A variant of {@link ExplicitRaceColumnSelection} which has a pre-selected race. As opposed to the base class,
* a new leaderboard column is added implicitly only if it is linked to the pre-selected race.
*
* @author Axel Uhl (D043530)
*
*/
public class ExplicitRaceColumnSelectionWithPreselectedRace extends ExplicitRaceColumnSelection {
private final RaceIdentifier preSelectedRace;
/**
* @param preSelectedRace must be non-<code>null</code>. Otherwise, you may use the superclass instead.
*/
public ExplicitRaceColumnSelectionWithPreselectedRace(RaceIdentifier preSelectedRace) {
super();
assert preSelectedRace != null;
this.preSelectedRace = preSelectedRace;
}
@Override
protected List<RaceColumnDTO> getRaceColumnsToAddImplicitly(LeaderboardDTO newLeaderboard,
LeaderboardDTO oldLeaderboard) {
List<RaceColumnDTO> columnsToAddImplicitly = super.getRaceColumnsToAddImplicitly(newLeaderboard, oldLeaderboard);
if (preSelectedRace != null) {
for (Iterator<RaceColumnDTO> i = columnsToAddImplicitly.iterator(); i.hasNext();) {
RaceColumnDTO next = i.next();
if (!next.containsRace(preSelectedRace)) {
i.remove();
}
}
}
return columnsToAddImplicitly;
}
}
@@ -1,8 +1,6 @@
package com.sap.sailing.gwt.ui.leaderboard;
import com.google.gwt.i18n.client.NumberFormat;
import com.sap.sailing.domain.common.DetailType;
import com.sap.sailing.gwt.ui.client.NumberFormatterFactory;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.shared.LeaderboardRowDTO;
@@ -12,7 +10,7 @@ import com.sap.sailing.gwt.ui.shared.LeaderboardRowDTO;
* @author Axel Uhl (d043530)
*
*/
public class KingOfTheDownwindColumn extends FormattedDoubleLegDetailColumn {
public class KingOfTheDownwindColumn extends AbstractTimeInHoursMinutesSecondsColumn {
private static class KingOfTheDownwindField implements LegDetailField<Double> {
@Override
@@ -29,20 +27,4 @@ public class KingOfTheDownwindColumn extends FormattedDoubleLegDetailColumn {
headerStyle, columnStyle);
}
@Override
public String getStringValueToRender(LeaderboardRowDTO object) {
Double timeInSeconds = getDoubleValue(object);
String result;
if (timeInSeconds == null) {
result = null;
} else {
int hh = (int) (timeInSeconds / 3600);
int mm = (int) ((timeInSeconds - 3600 * hh) / 60);
int ss = (int) (timeInSeconds - 3600 * hh - 60 * mm);
NumberFormat numberFormat = NumberFormatterFactory.getDecimalFormat(2, 0);
result = "" + numberFormat.format(hh) + ":" + numberFormat.format(mm) + ":" + numberFormat.format(ss);
}
return result;
}
}
Loaded 100 of 243 files, more files were not shown because too many files have changed in this diff. Show more