Merge branch 'master' into bug5828

This commit is contained in:
Axel Uhl
2023-05-08 10:41:31 +02:00
186 changed files with 3450 additions and 828 deletions
@@ -2,21 +2,21 @@
<feature
id="com.amazon.aws.aws-java-api"
label="AWS API"
version="2.18.8"
version="2.20.59"
provider-name="Amazon">
<plugin
id="com.amazon.aws.aws-java-api"
download-size="0"
install-size="0"
version="2.18.8"
version="2.20.59"
unpack="false"/>
<plugin
id="com.amazon.aws.aws-java-api.source"
download-size="0"
install-size="0"
version="2.18.8"
version="2.20.59"
unpack="false"/>
</feature>
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<site>
<feature url="features/aws-sdk/com.amazon.aws.aws-java-api_2.18.8.jar" id="com.amazon.aws.aws-java-api" version="2.18.8">
<feature url="features/aws-sdk/com.amazon.aws.aws-java-api_2.20.59.jar" id="com.amazon.aws.aws-java-api" version="2.20.59">
<category name="aws-java-api"/>
</feature>
<category-def name="com.amazon.aws.aws-java-api" label="aws-java-api"/>
@@ -5,7 +5,7 @@ repositories {
}
dependencies {
implementation platform('software.amazon.awssdk:bom:2.18.8')
implementation platform('software.amazon.awssdk:bom:2.20.59')
implementation 'software.amazon.awssdk:s3'
implementation 'software.amazon.awssdk:lambda'
implementation 'software.amazon.awssdk:ec2'
+1 -1
View File
@@ -16,7 +16,7 @@
</properties>
<groupId>com.amazon.aws</groupId>
<artifactId>com.amazon.aws.aws-java-api</artifactId>
<version>2.18.8</version>
<version>2.20.59</version>
<packaging>eclipse-plugin</packaging>
<build>
<plugins>
@@ -244,4 +244,6 @@ DistanceToNextBoatToStarboardAtStartOfRacePerpendicularToStartLine=Distance to n
TotalDistanceToNeighboursPerpendicularToStarLineAtStartOfRace=Sum of absolute distances to start line neighbors at race start, perpendicular to start line
TotalWindwardDistanceToNeighboursAtStartOfRace=Sum of absolute windward distances to start line neighbors at race start
TotalDistanceToNeighboursProjectedToStartLineAtStartOfRace=Sum of along-line distances to start line neighbors (or end of line, respectively) at race start
IRM=IRM Code (OCS, DNC, DNF, etc.)
IRM=IRM Code (OCS, DNC, DNF, etc.)
XTE=Cross-Track Error
AbsoluteXTE=Absolute Cross-Track Error
@@ -245,3 +245,5 @@ TotalDistanceToNeighboursPerpendicularToStarLineAtStartOfRace=Součet absolutní
TotalWindwardDistanceToNeighboursAtStartOfRace=Součet absolutních vzdáleností s větrem k sousedním lodím na startovní čáře na startu rozjížďky
TotalDistanceToNeighboursProjectedToStartLineAtStartOfRace=Součet vzdáleností podél čáry k sousedním lodím na startovní čáře (případně konci čáry) na startu rozjížďky
IRM=Kód neplatného výsledku (OCS, DNC, DNF atd.)
XTE=Stranová odchylka
AbsoluteXTE=Absolutní stranová odchylka
@@ -245,3 +245,5 @@ TotalDistanceToNeighboursPerpendicularToStarLineAtStartOfRace=Sum af de absolutt
TotalWindwardDistanceToNeighboursAtStartOfRace=Sum af de absolutte distancer til luv til nabobådene ved startlinjen ved kapsejladsens start.
TotalDistanceToNeighboursProjectedToStartLineAtStartOfRace=Sum af distancerne langs startlinjen til nabobådene ved startlinjen (eller side af startlinjen) ved kapsejladsens start
IRM=IRM-kode (OCS, DNC, DNF etc.)
XTE=Cross track-fejl
AbsoluteXTE=Absolut Cross Track-fejl
@@ -234,7 +234,7 @@ Bias30SecondsAfterRaceStart=Bias der Startlinie 30 Sekunden nach Start des Renne
RankAtFirstMarkVsFinalRank=Rang bei erster Marke (X) vs. Finaler Rang (Y)
DistanceToAdvantageousSideAtStartOfRaceVsRankAtFirstMark=Relative Distanz zum bevorteilten Ende der Start beim Start des Rennens (X) vs. Position beim ersten Wegpunkt (Y)
DistanceToAdvantageousSideAtStartOfRaceVsFinalRank=Relative Distanz zum bevorteilten Ende der Start beim Start des Rennens (X) vs. Finale Platzierung (Y)
AverageRaceWindSpeed=Durchschnittliche Windgeschwindigkeit f�r Wettfahrt
AverageRaceWindSpeed=Durchschnittliche Windgeschwindigkeit für Wettfahrt
BiasAtStartOfRace=Bias der Startlinie zum Start des Rennens in Meter (negativ: BACKBORD, positiv: STEUERBORD)
DistanceToStarboardSideOfStartLineProjectedOntoLineAtStart=Abstand zur Steuerbordseite der Startlinie, projiziert auf die Linie, zum Start des Rennens
NormalizedDistanceToStarboardSideOfStartLineProjectedOntoLineAtStart=Abstand zur Steuerbordseite der Startlinie, projiziert auf die Linie, zum Start des Rennens, dividiert durch Startlinien-Länge
@@ -247,4 +247,6 @@ DistanceToNextBoatToStarboardAtStartOfRacePerpendicularToStartLine=Abstand zum n
TotalDistanceToNeighboursPerpendicularToStarLineAtStartOfRace=Summe der absoluten Abstände zu beiden Startlinien-Nachbarn, projiziert im rechten Winkel zur Startlinie
TotalWindwardDistanceToNeighboursAtStartOfRace=Summe der windwärtigen absoluten Abstände zu beiden Startlinien-Nachbarn
TotalDistanceToNeighboursProjectedToStartLineAtStartOfRace=Summe der Abstände zu beiden Startlinien-Nachbarn bzw. den Enden der Linie, projiziert auf die Startlinie
IRM=IRM Code (OCS, DNC, DNF etc.)
IRM=IRM Code (OCS, DNC, DNF etc.)
XTE=Querabweichung
AbsoluteXTE=Absolute Querabweichung
@@ -245,3 +245,5 @@ TotalDistanceToNeighboursPerpendicularToStarLineAtStartOfRace=Total de distancia
TotalWindwardDistanceToNeighboursAtStartOfRace=Total de distancias absolutas a barlovento para los vecinos en la línea de salida en el inicio de la carrera
TotalDistanceToNeighboursProjectedToStartLineAtStartOfRace=Total de distancias a lo largo de la línea para los vecinos en la línea de salida (o en posición final, respectivamente) en el inicio de la carrera
IRM=Código IRM (OCS, DNC, DNF, etc.)
XTE=Error de recorrido
AbsoluteXTE=Error de recorrido absoluto
@@ -245,3 +245,5 @@ TotalDistanceToNeighboursPerpendicularToStarLineAtStartOfRace=Somme des distance
TotalWindwardDistanceToNeighboursAtStartOfRace=Somme des distances au vent absolues par rapport aux voisins de la ligne de départ au départ de la course
TotalDistanceToNeighboursProjectedToStartLineAtStartOfRace=Somme des distances le long de la ligne par rapport aux voisins de la ligne de départ (ou d''arrivée, respectivement) au départ de la course
IRM=Code IRM (OCS, DNC, DNF, etc.)
XTE=Écart de route
AbsoluteXTE=Écart de route absolu
@@ -245,3 +245,5 @@ TotalDistanceToNeighboursPerpendicularToStarLineAtStartOfRace=Somma delle distan
TotalWindwardDistanceToNeighboursAtStartOfRace=Somma delle distanze sopravento dalle barche vicine alla linea di partenza alla partenza della gara
TotalDistanceToNeighboursProjectedToStartLineAtStartOfRace=Somma delle distanze lungo la linea di partenza dalle barche vicine alla linea di partenza (o, rispettivamente, alla fine della linea) alla partenza della gara
IRM=Codice IRM (OCS, DNC, DNF, ecc.)
XTE=Errore di fuori rotta
AbsoluteXTE=Errore assoluto di fuori rotta
@@ -245,3 +245,5 @@ TotalDistanceToNeighboursPerpendicularToStarLineAtStartOfRace=レーススター
TotalWindwardDistanceToNeighboursAtStartOfRace=レーススタート時のスタートライン近傍艇までの絶対風上距離の合計
TotalDistanceToNeighboursProjectedToStartLineAtStartOfRace=レーススタート時のスタートライン近傍艇 (または、それぞれのラインの端) までのアロングライン距離の合計
IRM=IRM コード (OCS、DNC、DNF など)
XTE=クロストラックエラー
AbsoluteXTE=絶対クロストラックエラー
@@ -245,3 +245,5 @@ TotalDistanceToNeighboursPerpendicularToStarLineAtStartOfRace=Total das distânc
TotalWindwardDistanceToNeighboursAtStartOfRace=Total das distâncias absolutas a barlavento para vizinhos da linha de partida no início da corrida
TotalDistanceToNeighboursProjectedToStartLineAtStartOfRace=Total das distâncias ao longo da linha para vizinhos da linha de partida (ou fim da linha, respectivamente) no início da corrida
IRM=Código IRM (OCS, DNC, DNF, etc.)
XTE=Erro de distância da posição da embarcação ao waypoint
AbsoluteXTE=Erro de distância da posição absoluta da embarcação ao waypoint
@@ -245,3 +245,5 @@ TotalDistanceToNeighboursPerpendicularToStarLineAtStartOfRace=Сумма абс
TotalWindwardDistanceToNeighboursAtStartOfRace=Сумма абсолютных расстояний до соседей по линии старта при старте гонки
TotalDistanceToNeighboursProjectedToStartLineAtStartOfRace=Сумма продольных расстояний до соседей по линии старта (или конца линии) при старте гонки
IRM=Код IRM (OCS, DNC, DNF и т д.)
XTE=Ошибка бокового уклонения
AbsoluteXTE=Ошибка абсолютного бокового уклонения
@@ -245,3 +245,5 @@ TotalDistanceToNeighboursPerpendicularToStarLineAtStartOfRace=Seštevek absolutn
TotalWindwardDistanceToNeighboursAtStartOfRace=Seštevek absolutnih razdalj proti vetru do najbližjih jadrnic na štartni liniji na štartu plova
TotalDistanceToNeighboursProjectedToStartLineAtStartOfRace=Seštevek razdalj vzdolž linije do najbližjih jadrnic na štartni liniji (ali ciljni liniji) na štartu plova
IRM=Koda IRM (OCS, DNC, DNF itd.)
XTE=Napaka za vse sledi
AbsoluteXTE=Absolutna napaka za vse sledi
@@ -245,3 +245,5 @@ TotalDistanceToNeighboursPerpendicularToStarLineAtStartOfRace=比赛轮次开始
TotalWindwardDistanceToNeighboursAtStartOfRace=比赛轮次开始时与起航线附近的绝对上风距离总和
TotalDistanceToNeighboursProjectedToStartLineAtStartOfRace=比赛轮次开始时与起航线附近(或相应航线终点)的沿线距离总和
IRM=IRM 代码(OCS、DNC、DNF 等)
XTE=交叉轨道误差
AbsoluteXTE=绝对交叉轨道误差
@@ -4,6 +4,7 @@ import com.sap.sailing.domain.common.NoWindException;
import com.sap.sailing.domain.common.SpeedWithBearing;
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sse.common.Bearing;
import com.sap.sse.common.Distance;
import com.sap.sse.datamining.annotations.Connector;
import com.sap.sse.datamining.annotations.Statistic;
@@ -22,4 +23,10 @@ public interface HasGPSFixContext {
@Statistic(messageKey = "VMG", resultDecimals = 2)
SpeedWithBearing getVelocityMadeGood();
@Statistic(messageKey = "XTE", resultDecimals = 2)
Distance getXTE();
@Statistic(messageKey = "AbsoluteXTE", resultDecimals = 2)
Distance getAbsoluteXTE();
}
@@ -7,6 +7,7 @@ import com.sap.sailing.domain.common.SpeedWithBearing;
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sse.common.Bearing;
import com.sap.sse.common.Distance;
import com.sap.sse.common.TimePoint;
/**
@@ -78,4 +79,14 @@ public class GPSFixWithContext implements HasGPSFixContext {
public SpeedWithBearing getVelocityMadeGood() {
return getTrackedRace().getVelocityMadeGood(getTrackedLegOfCompetitorContext().getCompetitor(), getTimePoint());
}
@Override
public Distance getXTE() {
return getTrackedLegOfCompetitorContext().getTrackedLegOfCompetitor().getSignedCrossTrackError(getTimePoint());
}
@Override
public Distance getAbsoluteXTE() {
return getTrackedLegOfCompetitorContext().getTrackedLegOfCompetitor().getAbsoluteCrossTrackError(getTimePoint());
}
}
@@ -17,7 +17,7 @@ public enum BoatClassMasterdata {
_49ER ("49er", true, 4.88, 1.93, BoatHullType.MONOHULL, true, "49-ER"),
_49ERFX ("49erFX", true, 4.88, 1.93, BoatHullType.MONOHULL, true, "49FX"),
_420 ("420", true, 4.20, 1.65, BoatHullType.MONOHULL, true, "420er", "420M", "420W"),
_470 ("470", true, 4.70, 1.68, BoatHullType.MONOHULL, true, "470er", "470M", "470W", "470 - M", "470 - W", "470 Men", "470 Women"),
_470 ("470", true, 4.70, 1.68, BoatHullType.MONOHULL, true, "470er", "470M", "470W", "470 - M", "470 - W", "470 Men", "470 Women", "470 Mixed"),
_5O5 ("5O5", true, 5.03, 1.88, BoatHullType.MONOHULL, true, "505", "5o5", "505er", "5o5er"),
_5_5MR ("5.5mR", true, 9.50, 1.92, BoatHullType.MONOHULL, true, "5.5 Meter", "5.5 Metre", "5.5", "5.5M", "5.5-metre", "5.5 metre"),
_6MR ("6mR", true, 11, 2.2, BoatHullType.MONOHULL, true, "6 Meter", "6 Metre", "6", "6M", "6-metre", "6 metre"),
@@ -71,6 +71,7 @@ public enum BoatClassMasterdata {
HANSA_303 ("Hansa 303", true, 3.03, 1.35, BoatHullType.MONOHULL, false, "Hansa-303", "Hansa303"),
HOBIE_TIGER ("Hobie Tiger", true, 5.51, 2.60, BoatHullType.CATAMARAN, true),
HOBIE_WILD_CAT ("Hobie Wild Cat", true, 5.49, 2.59, BoatHullType.CATAMARAN, true, "Hobie Wild Cat F18"),
IMOCA("IMOCA", true, 20.12, 5.5, BoatHullType.MONOHULL, true, "IMOCA 60"),
INTERNATIONAL_14("International 14", true, 4.27, 1.83, BoatHullType.MONOHULL, true, "I14", "Int.14", "Int14"),
IQFOIL_MEN("iQFOil Men", true, 2.20, 0.95, BoatHullType.SURFERBOARD, false, "iQFOil 95 Men", "iQF95"),
IQFOIL_WOMEN("iQFOil Women", true, 2.20, 0.95, BoatHullType.SURFERBOARD, false, "iQFOil 95 Women"),
@@ -148,7 +149,8 @@ public enum BoatClassMasterdata {
VAURIEN ("Vaurien", true, 4.08, 1.47, BoatHullType.MONOHULL, true),
VENT_D_OUEST ("Vent d'Ouest", true, 5.85, 1.75, BoatHullType.MONOHULL, true, "VENTDOUEST", "VENTD'OUEST"),
VIPER_640 ("Viper 640", true, 6.43, 2.49, BoatHullType.MONOHULL, true),
VO60 ("VO60", true, 19.5, 5.25, BoatHullType.MONOHULL, true, "Volvo Ocean 60", "VolvoOcean60", "W60", "Whitbread60", "Whitbread 60"),
VO60 ("VO60", true, 19.5, 5.25, BoatHullType.MONOHULL, true, "Volvo Ocean 60", "VolvoOcean60", "VO 60", "W60", "Whitbread60", "Whitbread 60"),
VO65 ("VO65", true, 22.14, 5.60, BoatHullType.MONOHULL, true, "Volvo Ocean 65", "VolvoOcean65", "VO 65"),
VX_ONE ("VX ONE", true, 5.79, 2.19, BoatHullType.MONOHULL, true, "VX-ONE"),
WASZP ("WASZP", true, 3.35, 2.25, BoatHullType.MONOHULL, false, "WASZPs"),
WAYFARER ("Wayfarer", true, 4.82, 1.85, BoatHullType.MONOHULL, false),
@@ -6,7 +6,7 @@ public enum ScoringSchemeType {
HIGH_POINT_FIRST_GETS_ONE, LOW_POINT_WINNER_GETS_ZERO, HIGH_POINT_WINNER_GETS_SIX, HIGH_POINT_WINNER_GETS_FIVE, HIGH_POINT_WINNER_GETS_EIGHT,
HIGH_POINT_WINNER_GETS_EIGHT_AND_INTERPOLATION, HIGH_POINT_FIRST_GETS_TEN_OR_EIGHT, HIGH_POINT_FIRST_GETS_TWELVE_OR_EIGHT, HIGH_POINT_FIRST_GETS_TWELVE_OR_EIGHT_2017, LOW_POINT_WITH_ELIMINATIONS_AND_ROUNDS_WINNER_GETS_07,
LOW_POINT_LEAGUE_OVERALL, HIGH_POINT_MATCH_RACING, LOW_POINT_TIE_BREAK_BASED_ON_LAST_SERIES_ONLY, LOW_POINT_FIRST_TO_WIN_TWO_RACES, LOW_POINT_FIRST_TO_WIN_THREE_RACES,
HIGH_POINT_BY_WINS_TIES_LASTLY_BROKEN_BY_OTHER_LEADERBOARD;
HIGH_POINT_BY_WINS_TIES_LASTLY_BROKEN_BY_OTHER_LEADERBOARD, LOW_POINT_A82_ONLY, LOW_POINT_FIRST_TO_WIN_THREE_RACES_A82_ONLY;
public static double getScaledScore(double columnFactor, double unscaledScore, boolean oneAlwaysStaysOne) {
return unscaledScore * columnFactor - (oneAlwaysStaysOne ? columnFactor-1 : 0);
@@ -14,6 +14,10 @@ public enum RaceLogRaceStatus {
RaceLogRaceStatus(int orderNumber) {
this.orderNumber = orderNumber;
}
public boolean isAbortingFlagFromPreviousPassValid() {
return this == UNSCHEDULED || this == PRESCHEDULED;
}
public static boolean isPreRunning(RaceLogRaceStatus status) {
return status != null &&
@@ -3,8 +3,20 @@ package com.sap.sailing.domain.abstractlog.race.analyzing.impl;
import com.sap.sailing.domain.abstractlog.race.RaceLog;
import com.sap.sailing.domain.abstractlog.race.RaceLogEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogFlagEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogPassChangeEvent;
import com.sap.sailing.domain.common.racelog.Flags;
/**
* Finds the last valid "abort" flag ({@link Flags#AP}, {@link Flags#NOVEMBER}, or {@link Flags#FIRSTSUBSTITUTE}) that
* has been displayed in the last-but-one pass, regardless the state of the current pass. This assumes that
* aborting a pass with one of these flags will automatically trigger a {@link RaceLogPassChangeEvent}, advancing
* immediately to the next pass.<p>
*
* This finder does <em>not<em> inspect the <em>current</em> pass, so simply finding a valid abort flag in the
* last-but-one pass with this finder does not necessarily mean that it would still have to be displayed. If,
* for example, the current pass has already set a start time again then this supersedes the race abort, postponing
* or general recall of the previous pass.
*/
public class AbortingFlagFinder extends RaceLogAnalyzer<RaceLogFlagEvent> {
public AbortingFlagFinder(RaceLog raceLog) {
@@ -52,7 +52,7 @@ import com.sap.sse.common.impl.MillisecondsTimePoint;
* same cases in case the {@link ScoringSchemeType#LOW_POINT} scoring scheme is used, e.g., for those competitors
* who did not advance to the semi-final stage.
*/
public class LeaderboardScoringAndRankingTestForLowPointThreeMedalWins extends LeaderboardScoringAndRankingTestBase {
public class LeaderboardScoringAndRankingForLowPointThreeMedalWinsTest extends LeaderboardScoringAndRankingTestBase {
private static final String SEMIFINAL_SERIES_NAME = "Semifinal";
private static final String SEMIFINAL_FLEET_A_NAME = "A";
private static final String SEMIFINAL_FLEET_B_NAME = "B";
@@ -228,7 +228,15 @@ public class LeaderboardScoringAndRankingTestForLowPointThreeMedalWins extends L
/**
* In this test A races one race, B two. The semi-finals are crafted such that there are ties on wins
* between competitors from A and B, so checking the last race's rank shall break the tie.
* between competitors from A and B, so checking the last race's rank shall break the tie within the
* respective semi-final fleet (A or B, respectively). Things are further crafted such that two competitors
* in B tied on wins are also tied on score in their last B semi-final race, both scoring DNF, so the
* tie-breaking comparison must continue with the last-but-one race. For the A fleet the set-up is such
* that they only race one race, but two competitors not winning both score a DNF, so their tie needs to
* be resolved based on their opening series ranks.<p>
*
* Once the A/B fleets have their internal tie-breaking done, their non-winning ranks have to be compared
* pair-wise (2nd vs. 2nd, 3rd vs. 3rd, and so on) based on their opening series rank for overall ranking.
*/
@Test
public void testWithTiesInSemiFinalWithDifferentNumbersOfSemifinalRaces() {
@@ -242,51 +250,78 @@ public class LeaderboardScoringAndRankingTestForLowPointThreeMedalWins extends L
// Carried Wins SF1 Wins
// 2 1 3
// 1 2 1
// 3 0
// 4 0
// 5 (DNF) 0 <------- tied with 4th seed based on wins, tied on last race, better by seed from opening series
// 5 (DNF) 0
final TrackedRace sf1ARace = new MockedTrackedRaceWithStartTimeAndRanks(now, semiFinalists.getA());
final RaceColumn sf1Column = leaderboard.getRaceColumnByName("SF1");
leaderboard.getScoreCorrection().setMaxPointsReason(semiFinalists.getA().get(2), sf1Column, MaxPointsReason.DNF);
leaderboard.getScoreCorrection().correctScore(semiFinalists.getA().get(2), sf1Column, 5.0);
leaderboard.getScoreCorrection().setMaxPointsReason(semiFinalists.getA().get(3), sf1Column, MaxPointsReason.DNF);
leaderboard.getScoreCorrection().correctScore(semiFinalists.getA().get(3), sf1Column, 5.0);
sf1Column.setTrackedRace(sf1Column.getFleetByName(SEMIFINAL_FLEET_A_NAME), sf1ARace);
// scores for the B semi-finalists in the order of their seeding:
// Carried Wins SF1 SF2 Wins
// 2 4 1 3
// 1 3 2 1
// 2 3 0
// 1 4 1
// Carried Wins SF1 SF2 Wins
// 2 4 1 3
// 1 3 5 (DNF) 1
// 2 2 0
// 1 5 (DNF) 1 <------ tied with 2nd seed based on wins, same rank/score in SF2, better in SF1
final TrackedRace sf1BRace = new MockedTrackedRaceWithStartTimeAndRanks(now, Arrays.asList(
semiFinalists.getB().get(3),
semiFinalists.getB().get(2),
semiFinalists.getB().get(1),
semiFinalists.getB().get(0)));
// The expected orders for A/B: A1,A2,A3/A4 (depending on opening series score); B1,B4,B2,B3
// This merges into places 5-10 overall, based on Opening Series rank: A2/B4, A3/A4/B2, A3/A4/B3 (where A3/A4 is decided based on Opening Series again)
sf1Column.setTrackedRace(sf1Column.getFleetByName(SEMIFINAL_FLEET_B_NAME), sf1BRace);
final TrackedRace sf2BRace = new MockedTrackedRaceWithStartTimeAndRanks(now, semiFinalists.getB());
final TrackedRace sf2BRace = new MockedTrackedRaceWithStartTimeAndRanks(now, Arrays.asList(
semiFinalists.getB().get(0),
semiFinalists.getB().get(2),
semiFinalists.getB().get(1),
semiFinalists.getB().get(3)));
final RaceColumn sf2Column = leaderboard.getRaceColumnByName("SF2");
sf2Column.setTrackedRace(sf2Column.getFleetByName(SEMIFINAL_FLEET_B_NAME), sf2BRace);
leaderboard.getScoreCorrection().setMaxPointsReason(semiFinalists.getB().get(1), sf2Column, MaxPointsReason.DNF);
leaderboard.getScoreCorrection().correctScore(semiFinalists.getB().get(1), sf2Column, 5.0);
leaderboard.getScoreCorrection().setMaxPointsReason(semiFinalists.getB().get(3), sf2Column, MaxPointsReason.DNF);
leaderboard.getScoreCorrection().correctScore(semiFinalists.getB().get(3), sf2Column, 5.0);
final Iterable<Competitor> rankResultsAfterSemifinals = leaderboard.getCompetitorsFromBestToWorst(later);
// So we should see ties on one win each for zero index-based A(1), B(1), and B(3).
// Their last race's scores are, respectively, 2 , 2 , 4
// So A(1) and B(1) need to break their tie based on the opening series, and B(3) ranks worse.
// Overall ranks 1 and 2 are taken by the finalists; 3 and 4 by the semi-final winners. Start looking at rank 5 (zero-based 4):
if (Util.indexOf(openingSeriesRankResult, semiFinalists.getA().get(1)) < Util.indexOf(openingSeriesRankResult, semiFinalists.getB().get(1))) {
assertSame(Util.get(rankResultsAfterSemifinals, 4), semiFinalists.getA().get(1));
assertSame(Util.get(rankResultsAfterSemifinals, 5), semiFinalists.getB().get(1));
assertRanksBasedOnOpeningSeriesRanking(/* zeroBasedRankOfCompetitorWithBetterOpeningSeriesScore */ 2,
semiFinalists.getA().get(0), semiFinalists.getB().get(0),
openingSeriesRankResult, rankResultsAfterSemifinals);
assertRanksBasedOnOpeningSeriesRanking(/* zeroBasedRankOfCompetitorWithBetterOpeningSeriesScore */ 4,
semiFinalists.getA().get(1), semiFinalists.getB().get(3),
openingSeriesRankResult, rankResultsAfterSemifinals);
// find out whether A3 or A4 scored better in the opening series:
final Competitor betterOfA3AndA4AfterOpeningSeries;
final Competitor worseOfA3AndA4AfterOpeningSeries;
if (Util.indexOf(openingSeriesRankResult, semiFinalists.getA().get(2)) < Util.indexOf(openingSeriesRankResult, semiFinalists.getA().get(3))) {
betterOfA3AndA4AfterOpeningSeries = semiFinalists.getA().get(2);
worseOfA3AndA4AfterOpeningSeries = semiFinalists.getA().get(3);
} else {
assertSame(Util.get(rankResultsAfterSemifinals, 5), semiFinalists.getA().get(1));
assertSame(Util.get(rankResultsAfterSemifinals, 4), semiFinalists.getB().get(1));
betterOfA3AndA4AfterOpeningSeries = semiFinalists.getA().get(3);
worseOfA3AndA4AfterOpeningSeries = semiFinalists.getA().get(2);
}
assertSame(Util.get(rankResultsAfterSemifinals, 6), semiFinalists.getB().get(3));
// And we should see ties on zero wins each for zero index-based A(2), A(3), and B(2)
// Their last race's scores are, respectively, 3 , 4 , 3
// So A(2) and B(2) are tied on the last race too, and the opening series rank decides,
// and A(3) follows
if (Util.indexOf(openingSeriesRankResult, semiFinalists.getA().get(2)) < Util.indexOf(openingSeriesRankResult, semiFinalists.getB().get(2))) {
assertSame(Util.get(rankResultsAfterSemifinals, 7), semiFinalists.getA().get(2));
assertSame(Util.get(rankResultsAfterSemifinals, 8), semiFinalists.getB().get(2));
assertRanksBasedOnOpeningSeriesRanking(/* zeroBasedRankOfCompetitorWithBetterOpeningSeriesScore */ 6,
betterOfA3AndA4AfterOpeningSeries, semiFinalists.getB().get(1),
openingSeriesRankResult, rankResultsAfterSemifinals);
assertRanksBasedOnOpeningSeriesRanking(/* zeroBasedRankOfCompetitorWithBetterOpeningSeriesScore */ 8,
worseOfA3AndA4AfterOpeningSeries, semiFinalists.getB().get(2),
openingSeriesRankResult, rankResultsAfterSemifinals);
}
private void assertRanksBasedOnOpeningSeriesRanking(
final int expectedZeroBasedRankAfterSemifinalsOfCompetitorWithBetterOpeningSeriesScore,
final Competitor aSemiFinalist, final Competitor bSemiFinalist,
final Iterable<Competitor> openingSeriesRankResult,
final Iterable<Competitor> rankResultsAfterSemifinals) {
if (Util.indexOf(openingSeriesRankResult, aSemiFinalist) < Util.indexOf(openingSeriesRankResult, bSemiFinalist)) {
assertSame(Util.get(rankResultsAfterSemifinals, expectedZeroBasedRankAfterSemifinalsOfCompetitorWithBetterOpeningSeriesScore), aSemiFinalist);
assertSame(Util.get(rankResultsAfterSemifinals, expectedZeroBasedRankAfterSemifinalsOfCompetitorWithBetterOpeningSeriesScore+1), bSemiFinalist);
} else {
assertSame(Util.get(rankResultsAfterSemifinals, 8), semiFinalists.getA().get(2));
assertSame(Util.get(rankResultsAfterSemifinals, 7), semiFinalists.getB().get(2));
assertSame(Util.get(rankResultsAfterSemifinals, expectedZeroBasedRankAfterSemifinalsOfCompetitorWithBetterOpeningSeriesScore+1), aSemiFinalist);
assertSame(Util.get(rankResultsAfterSemifinals, expectedZeroBasedRankAfterSemifinalsOfCompetitorWithBetterOpeningSeriesScore), bSemiFinalist);
}
assertSame(Util.get(rankResultsAfterSemifinals, 9), semiFinalists.getA().get(3));
}
/**
@@ -300,11 +335,13 @@ public class LeaderboardScoringAndRankingTestForLowPointThreeMedalWins extends L
final Series semiFinalSeries = regatta.getSeriesByName(SEMIFINAL_SERIES_NAME);
runRacesInFinalUntilThreeWins(semiFinalists.getA(), semiFinalSeries, SEMIFINAL_FLEET_A_NAME, now);
runRacesInFinalUntilThreeWins(semiFinalists.getB(), semiFinalSeries, SEMIFINAL_FLEET_B_NAME, now);
// check that the semi-final winners took three wins (including carried wins) each:
// check that the semi-final winners took three wins (including carried wins) each (note, they are at zero-based ranks 2/3 due to the
// grand finalists already at zero-based ranks 0/1):
final Iterable<Competitor> rankResultsAfterSemifinals = leaderboard.getCompetitorsFromBestToWorst(later);
assertEquals(3.0, leaderboard.getNetPoints(Util.get(rankResultsAfterSemifinals, 2), later), EPSILON);
assertEquals(3.0, leaderboard.getNetPoints(Util.get(rankResultsAfterSemifinals, 3), later), EPSILON);
assertSemiFinalistRanks(leaderboard, semiFinalSeries, semiFinalists, leaderboard.getCompetitorsFromBestToWorst(later), openingSeriesRankResult, later);
assertSemiFinalistRanks(leaderboard, semiFinalSeries, semiFinalists,
leaderboard.getCompetitorsFromBestToWorst(later), openingSeriesRankResult, later);
return openingSeriesRankResult;
}
@@ -337,7 +374,8 @@ public class LeaderboardScoringAndRankingTestForLowPointThreeMedalWins extends L
}
/**
* Returns the semi-finalists for A and B fleets
* Returns the semi-finalists for A and B fleets and set the carried wins for semi-finalists
* and grand finalists.
*/
private Pair<List<Competitor>, List<Competitor>> assignCarryForwardWinsToSemiFinalistsAndGrandFinalists(TimePoint timePoint) {
// now compute and enter the wins carried forward:
@@ -394,7 +432,7 @@ public class LeaderboardScoringAndRankingTestForLowPointThreeMedalWins extends L
}
/**
* For all finalists adjacent in the {@code rankResultsAfterGrandFinal} checks that the competitor ranked better
* For all finalists adjacent in the {@code rankResultsAfterSemiFinal} checks that the competitor ranked better
* has more or equal wins; if equal wins, scored better in the last race; if scored equal in the last race had
* better rank in {@code openingSeriesRankResult}.
*/
@@ -407,8 +445,31 @@ public class LeaderboardScoringAndRankingTestForLowPointThreeMedalWins extends L
allSemifinalists.addAll(semiFinalists.getA());
allSemifinalists.addAll(semiFinalists.getB());
assertEquals(allSemifinalists, new HashSet<>(semiFinalistsInRankedOrder));
// assert that each two adjacent odd/even-numbered semi-finalists in the overall
// ranking stem from the two different semi-final fleets:
for (int i=0; i<semiFinalists.getA().size(); i += 2) {
assertTrue(!semiFinalists.getA().contains(semiFinalistsInRankedOrder.get(i)) ||
semiFinalists.getB().contains(semiFinalistsInRankedOrder.get(i+1)));
assertTrue(!semiFinalists.getA().contains(semiFinalistsInRankedOrder.get(i+1)) ||
semiFinalists.getB().contains(semiFinalistsInRankedOrder.get(i)));
}
assertCorrectMedalSeriesSequence(leaderboard, semiFinalSeries, semiFinalists.getA(), rankResultsAfterSemiFinal, openingSeriesRankResult, timePoint);
assertCorrectMedalSeriesSequence(leaderboard, semiFinalSeries, semiFinalists.getB(), rankResultsAfterSemiFinal, openingSeriesRankResult, timePoint);
}
/**
* Constructs a sorted sequence from the {@code competitorsFromOneFleet}, sorted by their order in
* {@code rankResultsAfterSeries}, then compares all adjacent pairs in that sequence for their correct comparison,
* using
* {@link #assertCorrectMedalSeriesSequence(Leaderboard, Series, Competitor, Competitor, Iterable, TimePoint)}.
*/
private void assertCorrectMedalSeriesSequence(Leaderboard leaderboard, Series semiFinalSeries, List<Competitor> competitorsFromOneFleet,
Iterable<Competitor> rankResultsAfterSeries, Iterable<Competitor> openingSeriesRankResult,
TimePoint timePoint) {
final Iterable<Competitor> competitorsFromOneFleetOrderedByRankResults =
Util.filter(rankResultsAfterSeries, competitorsFromOneFleet::contains);
Competitor previous = null;
for (final Competitor next : semiFinalistsInRankedOrder) {
for (final Competitor next : competitorsFromOneFleetOrderedByRankResults) {
if (previous != null) {
assertCorrectMedalSeriesSequence(leaderboard, semiFinalSeries, previous, next, openingSeriesRankResult, timePoint);
}
@@ -437,30 +498,45 @@ public class LeaderboardScoringAndRankingTestForLowPointThreeMedalWins extends L
}
/**
* Can be used to assert the ranking <em>within</em> a single medal series fleet, such as the grand final,
* or the semi-final fleet A, or the semi-final fleet B.<p>
*
* Checks that {@code previous} (considered as ranked better than {@code next}) has more or equal wins; if equal
* wins, scored better in the last race; if scored equal in the last race had better rank in
* {@code openingSeriesRankResult}.
* wins, scored better in the last race, then last-but-one race, and so on; if scored equal in all races up to the first
* in the medal series, check that "previous" had better rank in {@code openingSeriesRankResult}.
*/
private void assertCorrectMedalSeriesSequence(Leaderboard leaderboard, Series medalSeries, Competitor previous,
Competitor next, Iterable<Competitor> openingSeriesRankResult, TimePoint timePoint) {
// assert same fleet
assertSame(medalSeries.getRaceColumns().iterator().next().getFleetOfCompetitor(previous),
medalSeries.getRaceColumns().iterator().next().getFleetOfCompetitor(next));
// assert previous is really "better" than next
assertTrue(leaderboard.getNetPoints(previous, timePoint) >= leaderboard.getNetPoints(next, timePoint));
if (leaderboard.getNetPoints(previous, timePoint).doubleValue() == leaderboard.getNetPoints(next, timePoint).doubleValue()) {
// equal number of wins; look at last race scored:
final Double previousLastScore = getLastScoredRace(leaderboard, medalSeries, previous, timePoint);
final Double nextLastScore = getLastScoredRace(leaderboard, medalSeries, next, timePoint);
assertTrue(previousLastScore <= nextLastScore);
if (previousLastScore.doubleValue() == nextLastScore.doubleValue()) {
// equal score in last race; expect tie to have been broken by opening series
// equal number of wins; look at last race scored, then last-but-one, and so on, until a difference is found
// or we reach the first race:
final List<? extends RaceColumnInSeries> raceColumnsInReverseOrder = Util.asList(medalSeries.getRaceColumns());
Collections.reverse(raceColumnsInReverseOrder);
boolean differenceFound = false;
for (final RaceColumnInSeries raceColumn : raceColumnsInReverseOrder) {
if (!raceColumn.isCarryForward()) {
final Double pointsScoredByPrevious = leaderboard.getTotalPoints(previous, raceColumn, timePoint);
final Double pointsScoredByNext = leaderboard.getTotalPoints(next, raceColumn, timePoint);
assertTrue((pointsScoredByPrevious==null && pointsScoredByNext==null)
|| pointsScoredByPrevious <= pointsScoredByNext);
if (pointsScoredByPrevious != null && pointsScoredByPrevious < pointsScoredByNext) {
differenceFound = true;
break;
}
}
}
if (!differenceFound) {
// equal score in all races in the medal series; expect tie to have been broken by opening series
assertTrue(Util.indexOf(openingSeriesRankResult, previous) < Util.indexOf(openingSeriesRankResult, next));
}
}
}
private Double getLastScoredRace(Leaderboard leaderboard, Series medalSeries, Competitor competitor, TimePoint timePoint) {
Iterable<Double> pointsScored = Util.map(medalSeries.getRaceColumns(), raceColumn->leaderboard.getTotalPoints(competitor, raceColumn, timePoint));
return Util.last(Util.filter(pointsScored, points->points!=null));
}
/**
* Creates races and attaches them to the series/fleet starting with the first non-carry column
* until one competitor achieves three wins. The competitors are expected to be ordered by their
@@ -1,6 +1,9 @@
package com.sap.sailing.domain.test;
import static org.junit.Assert.assertSame;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
@@ -27,6 +30,7 @@ import com.sap.sailing.domain.leaderboard.impl.LowPointFirstToWinTwoRaces;
import com.sap.sailing.domain.ranking.OneDesignRankingMetric;
import com.sap.sailing.domain.test.mock.MockedTrackedRaceWithStartTimeAndRanks;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sse.common.Duration;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util;
import com.sap.sse.common.Util.Pair;
@@ -39,7 +43,7 @@ import com.sap.sse.common.impl.MillisecondsTimePoint;
* behavior is not changed and still works for those same cases in case the {@link ScoringSchemeType#LOW_POINT} scoring
* scheme is used.
*/
public class LeaderboardScoringAndRankingTestForLowPoints extends LeaderboardScoringAndRankingTestBase {
public class LeaderboardScoringAndRankingForLowPointsTest extends LeaderboardScoringAndRankingTestBase {
private static final double EPSILON = 0.000001;
private void executePreSeries(List<Competitor> yellow, List<Competitor> blue, TimePoint now) {
@@ -69,9 +73,20 @@ public class LeaderboardScoringAndRankingTestForLowPoints extends LeaderboardSco
return regatta;
}
private Regatta setupRegattaWithHighPoint() {
final BoatClass boatClass = DomainFactory.INSTANCE.getOrCreateBoatClass("IMOCA", /* typicallyStartsUpwind */ false);
Regatta regatta = new RegattaImpl(RegattaImpl.getDefaultName("Test Regatta", boatClass.getName()), boatClass,
false, CompetitorRegistrationType.CLOSED, /* startDate */ null, /* endDate */ null, series, /* persistent */false,
DomainFactory.INSTANCE
.createScoringScheme(ScoringSchemeType.HIGH_POINT_LAST_BREAKS_TIE),
"123", /* course area */null, OneDesignRankingMetric::new,
/* registrationLinkSecret */ UUID.randomUUID().toString());
return regatta;
}
private void setupMedalSeriesWithCarryOverAndFourRaceColumns() {
Set<? extends Fleet> medalFleets = Collections.singleton(new FleetImpl("Default"));
List<String> medalRaceColumnNames = new ArrayList<String>();
List<String> medalRaceColumnNames = new ArrayList<>();
medalRaceColumnNames.add("Carry");
medalRaceColumnNames.add("M1");
medalRaceColumnNames.add("M2");
@@ -85,11 +100,11 @@ public class LeaderboardScoringAndRankingTestForLowPoints extends LeaderboardSco
}
private void setupQualificationSeriesWithOneRaceColumn() {
List<Fleet> qualificationFleets = new ArrayList<Fleet>();
final List<Fleet> qualificationFleets = new ArrayList<>();
for (String qualificationFleetName : new String[] { "Yellow", "Blue" }) {
qualificationFleets.add(new FleetImpl(qualificationFleetName));
}
List<String> qualificationRaceColumnNames = new ArrayList<String>();
List<String> qualificationRaceColumnNames = new ArrayList<>();
qualificationRaceColumnNames.add("Q");
Series qualificationSeries = new SeriesImpl("Qualification", /* isMedal */false,
/* isFleetsCanRunInParallel */ true, qualificationFleets, qualificationRaceColumnNames,
@@ -112,6 +127,45 @@ public class LeaderboardScoringAndRankingTestForLowPoints extends LeaderboardSco
List<Pair<Competitor, Double>> nonFinalistsAfterScore = afterFinalResults.subList(4, afterFinalResults.size());
Assert.assertEquals(nonFinalistsPreScore, nonFinalistsAfterScore);
}
@Test
public void testTORTieBreakOnLastRace() {
final TimePoint now = TimePoint.now();
final TimePoint later = now.plus(Duration.ONE_MINUTE);
series = new ArrayList<>();
final List<Fleet> fleets = new ArrayList<>();
final Fleet defaultFleet = new FleetImpl("Default");
fleets.add(defaultFleet);
List<String> raceColumnNames = Arrays.asList("Alicante", "Cape Town");
Series qualificationSeries = new SeriesImpl("IMOCA In-Port", /* isMedal */false,
/* isFleetsCanRunInParallel */ true, fleets, raceColumnNames,
/* trackedRegattaRegistry */ null);
series.add(qualificationSeries);
final Regatta regatta = setupRegattaWithHighPoint();
final List<Competitor> competitors = createCompetitors(5);
qualificationSeries.getRaceColumnByName("Alicante").setTrackedRace(defaultFleet, new MockedTrackedRaceWithStartTimeAndRanks(now, competitors));
qualificationSeries.getRaceColumnByName("Cape Town").setTrackedRace(defaultFleet, new MockedTrackedRaceWithStartTimeAndRanks(now, competitors));
final Leaderboard leaderboard = createLeaderboard(regatta, /* discarding thresholds */ new int[0]);
setScore(leaderboard, competitors.get(0), 4, 4);
setScore(leaderboard, competitors.get(1), 5, 3);
setScore(leaderboard, competitors.get(2), 0, 5);
setScore(leaderboard, competitors.get(3), 2, 2);
setScore(leaderboard, competitors.get(4), 3, 0);
assertSame(competitors.get(0), Util.get(leaderboard.getCompetitorsFromBestToWorst(later), 0));
assertSame(competitors.get(1), Util.get(leaderboard.getCompetitorsFromBestToWorst(later), 1));
assertSame(competitors.get(2), Util.get(leaderboard.getCompetitorsFromBestToWorst(later), 2));
assertSame(competitors.get(3), Util.get(leaderboard.getCompetitorsFromBestToWorst(later), 3));
assertSame(competitors.get(4), Util.get(leaderboard.getCompetitorsFromBestToWorst(later), 4));
}
private void setScore(Leaderboard leaderboard, Competitor competitor, double... scores) {
int i=0;
for (final RaceColumn raceColumn : leaderboard.getRaceColumns()) {
if (i < scores.length) {
leaderboard.getScoreCorrection().correctScore(competitor, raceColumn, scores[i++]);
}
}
}
/**
* In this test the preseries winner will win the first race, and should get a score of 2, all other finalists
@@ -120,7 +174,7 @@ public class LeaderboardScoringAndRankingTestForLowPoints extends LeaderboardSco
*/
@Test
public void testFirstPreseriesWinsAgain() throws NoWindException {
series = new ArrayList<Series>();
series = new ArrayList<>();
setupQualificationSeriesWithOneRaceColumn();
setupMedalSeriesWithCarryOverAndFourRaceColumns();
@@ -25,6 +25,7 @@ import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver;
import com.sap.sailing.domain.abstractlog.race.impl.RaceLogFlagEventImpl;
import com.sap.sailing.domain.abstractlog.race.impl.RaceLogImpl;
import com.sap.sailing.domain.abstractlog.race.impl.RaceLogPassChangeEventImpl;
import com.sap.sailing.domain.abstractlog.race.impl.RaceLogStartTimeEventImpl;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.impl.BoatClassImpl;
import com.sap.sailing.domain.base.impl.BoatImpl;
@@ -34,6 +35,7 @@ import com.sap.sailing.domain.base.impl.PersonImpl;
import com.sap.sailing.domain.base.impl.TeamImpl;
import com.sap.sailing.domain.common.MaxPointsReason;
import com.sap.sailing.domain.common.racelog.Flags;
import com.sap.sailing.domain.racelog.RaceLogAndTrackedRaceResolver;
import com.sap.sailing.domain.tractracadapter.DomainFactory;
import com.sap.sailing.domain.tractracadapter.impl.RaceAndCompetitorStatusWithRaceLogReconciler;
import com.sap.sse.common.Color;
@@ -84,6 +86,17 @@ public class TestTracTracRaceAndCompetitorStatusReconciler {
tractracRace = mock(IRace.class);
when(tractracRace.getStatus()).thenReturn(RaceStatusType.RACING);
trackedRace = mock(TrackedRace.class);
when(trackedRace.getRaceLogResolver()).thenReturn(new RaceLogAndTrackedRaceResolver() {
@Override
public RaceLog resolve(SimpleRaceLogIdentifier identifier) {
return null;
}
@Override
public TrackedRace resolveTrackedRace(SimpleRaceLogIdentifier identifier) {
return null;
}
});
tractracCompetitor = mock(ICompetitor.class);
tractracRaceCompetitor = mock(IRaceCompetitor.class);
when(tractracRaceCompetitor.getRace()).thenReturn(tractracRace);
@@ -178,6 +191,60 @@ public class TestTracTracRaceAndCompetitorStatusReconciler {
assertSame(abortFlagEvent, abortingFlagFinder.analyze());
}
@Test
public void testManualAbandonThenNewStartTimeShouldNotCauseChangeForTracTracStatusNONE() {
final TimePoint manualAbortTimePoint = startOfPass.plus(Duration.ONE_MINUTE);
final TimePoint newStartTimePoint = startOfPass.plus(Duration.ONE_MINUTE.times(2));
raceLog.add(new RaceLogFlagEventImpl(manualAbortTimePoint, author, /* pass id */ 1, Flags.NOVEMBER, /* lower flag */ null, /* isDisplayed */ true));
raceLog.add(new RaceLogPassChangeEventImpl(manualAbortTimePoint, author, /* pass id */ 2));
raceLog.add(new RaceLogStartTimeEventImpl(newStartTimePoint, author, /* pass id */ 2, newStartTimePoint, /* courseAreaId */ null));
when(tractracRace.getStatus()).thenReturn(RaceStatusType.NONE);
when(trackedRace.getRaceLogResolver()).thenReturn(new RaceLogAndTrackedRaceResolver() {
@Override
public RaceLog resolve(SimpleRaceLogIdentifier identifier) {
return null;
}
@Override
public TrackedRace resolveTrackedRace(SimpleRaceLogIdentifier identifier) {
return null;
}
});
final TimePoint noneStatusTimePoint = manualAbortTimePoint.plus(Duration.ONE_MINUTE.times(1));
when(tractracRace.getStatusLastChangedTime()).thenReturn(noneStatusTimePoint.asMillis());
final AbortingFlagFinder abortingFlagFinder = new AbortingFlagFinder(raceLog);
final RaceLogFlagEvent abortFlagEvent = abortingFlagFinder.analyze();
assertNotNull(abortFlagEvent);
assertSame(Flags.NOVEMBER, abortFlagEvent.getUpperFlag());
assertTrue(abortFlagEvent.isDisplayed());
assertEquals(manualAbortTimePoint, abortFlagEvent.getLogicalTimePoint());
assertEquals(1, abortFlagEvent.getPassId());
assertEquals(2, raceLog.getCurrentPassId());
reconciler.reconcileRaceStatus(tractracRace, trackedRace);
assertEquals(2, raceLog.getCurrentPassId()); // assert that the NONE state did not add a new pass
assertSame(abortFlagEvent, abortingFlagFinder.analyze());
}
@Test
public void testManualAbandonThenNoStartTimeInNextPassYetMayCauseNewPassChangeForTracTracStatusNONE() {
final TimePoint manualAbortTimePoint = startOfPass.plus(Duration.ONE_MINUTE);
raceLog.add(new RaceLogFlagEventImpl(manualAbortTimePoint, author, /* pass id */ 1, Flags.NOVEMBER, /* lower flag */ null, /* isDisplayed */ true));
raceLog.add(new RaceLogPassChangeEventImpl(manualAbortTimePoint, author, /* pass id */ 2));
when(tractracRace.getStatus()).thenReturn(RaceStatusType.NONE);
final TimePoint noneStatusTimePoint = manualAbortTimePoint.plus(Duration.ONE_MINUTE.times(1));
when(tractracRace.getStatusLastChangedTime()).thenReturn(noneStatusTimePoint.asMillis());
final AbortingFlagFinder abortingFlagFinder = new AbortingFlagFinder(raceLog);
final RaceLogFlagEvent abortFlagEvent = abortingFlagFinder.analyze();
assertNotNull(abortFlagEvent);
assertSame(Flags.NOVEMBER, abortFlagEvent.getUpperFlag());
assertTrue(abortFlagEvent.isDisplayed());
assertEquals(manualAbortTimePoint, abortFlagEvent.getLogicalTimePoint());
assertEquals(1, abortFlagEvent.getPassId());
assertEquals(2, raceLog.getCurrentPassId());
reconciler.reconcileRaceStatus(tractracRace, trackedRace);
assertTrue(raceLog.getCurrentPassId() >= 2); // if NONE advanced an empty pass by another one we don't mind
}
@Test
public void testIRMUpdateFromTracTracMapsToRaceLogCompetitorResult() {
// emulate we received a BFD for a competitor a second after the start of the pass
@@ -37,6 +37,7 @@ import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.CompetitorWithBoat;
import com.sap.sailing.domain.common.MaxPointsReason;
import com.sap.sailing.domain.common.racelog.Flags;
import com.sap.sailing.domain.common.racelog.RaceLogRaceStatus;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tractracadapter.DomainFactory;
import com.sap.sse.common.TimePoint;
@@ -241,8 +242,12 @@ public class RaceAndCompetitorStatusWithRaceLogReconciler {
RaceLogFlagEvent abortingFlagEvent = null;
for (final RaceLog raceLog : trackedRace.getAttachedRaceLogs()) {
if (abortingFlagEvent == null) {
final AbortingFlagFinder abortingFlagFinder = new AbortingFlagFinder(raceLog);
abortingFlagEvent = abortingFlagFinder.analyze();
final ReadonlyRaceState raceState = ReadonlyRaceStateImpl.getOrCreate(trackedRace.getRaceLogResolver(), raceLog);
final RaceLogRaceStatus status = raceState.getStatus();
if (status.isAbortingFlagFromPreviousPassValid()) {
final AbortingFlagFinder abortingFlagFinder = new AbortingFlagFinder(raceLog);
abortingFlagEvent = abortingFlagFinder.analyze();
}
}
}
final RaceLog defaultRaceLog = getDefaultRaceLog(trackedRace);
@@ -57,7 +57,9 @@ import com.sap.sailing.domain.leaderboard.impl.HighPointWinnerGetsEightAndInterp
import com.sap.sailing.domain.leaderboard.impl.HighPointWinnerGetsFive;
import com.sap.sailing.domain.leaderboard.impl.HighPointWinnerGetsSix;
import com.sap.sailing.domain.leaderboard.impl.LowPoint;
import com.sap.sailing.domain.leaderboard.impl.LowPointA82Only;
import com.sap.sailing.domain.leaderboard.impl.LowPointFirstToWinThreeRaces;
import com.sap.sailing.domain.leaderboard.impl.LowPointFirstToWinThreeRacesA82Only;
import com.sap.sailing.domain.leaderboard.impl.LowPointFirstToWinTwoRaces;
import com.sap.sailing.domain.leaderboard.impl.LowPointForLeagueOverallLeaderboard;
import com.sap.sailing.domain.leaderboard.impl.LowPointTieBreakBasedOnLastSeriesOnly;
@@ -158,6 +160,10 @@ public class DomainFactoryImpl extends SharedDomainFactoryImpl<RaceLogAndTracked
return new LowPointFirstToWinThreeRaces();
case HIGH_POINT_BY_WINS_TIES_LASTLY_BROKEN_BY_OTHER_LEADERBOARD:
return new HighPointByWinsTiesLastlyBrokenByOtherLeaderboard();
case LOW_POINT_A82_ONLY:
return new LowPointA82Only();
case LOW_POINT_FIRST_TO_WIN_THREE_RACES_A82_ONLY:
return new LowPointFirstToWinThreeRacesA82Only();
}
throw new RuntimeException("Unknown scoring scheme type "+scoringSchemeType.name());
}
@@ -166,14 +166,6 @@ public interface ScoringScheme extends Serializable {
return false;
}
/**
* Returning {@code true} makes the last medal race (having valid scores) a secondary ranking criteria for
* competitors that have an equal overall score.
*/
default boolean isLastMedalRaceCriteria() {
return false;
}
/**
* 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
@@ -212,10 +204,9 @@ public interface ScoringScheme extends Serializable {
}
/**
* Returns true if a race column evaluates to be a win for the given competitor at the given timepoint.
* If the competitor is not scored for this race, {@code false} is returned. "Winning" means to be sorted to the top
* for that column, considering any score corrections and penalties, too.
* @param totalPointsSupplier provides the
* Returns true if a race column evaluates to be a win for the given competitor at the given timepoint. If the
* competitor is not scored for this race, {@code false} is returned. "Winning" means to be sorted to the top for
* that column, considering any score corrections and penalties, too.<p>
*/
default boolean isWin(Leaderboard leaderboard, Competitor competitor, RaceColumn raceColumn, TimePoint timePoint,
Function<Competitor, Double> totalPointsSupplier, WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache) {
@@ -349,4 +340,20 @@ public interface ScoringScheme extends Serializable {
}
return result;
}
/**
* Compares by the scores of a single race column. If only one of the competitors has a result this competitor is
* ranked better than the other one.
*/
default int compareBySingleRaceColumnScore(Double o1Score, Double o2Score, boolean nullScoresAreBetter) {
return Comparator
.nullsLast((Double o1s, Double o2s) -> getScoreComparator(nullScoresAreBetter).compare(o1s, o2s))
.compare(o1Score, o2Score);
}
default int compareByLastMedalRacesCriteria(Competitor o1, List<Pair<RaceColumn, Double>> o1Scores, Competitor o2,
List<Pair<RaceColumn, Double>> o2Scores, boolean nullScoresAreBetter, Leaderboard leaderboard,
BiFunction<Competitor, RaceColumn, Double> totalPointsSupplier, WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache, TimePoint timePoint, int zeroBasedIndexOfLastMedalSeriesInWhichBothScored, int numberOfMedalRacesWonO1, int numberOfMedalRacesWonO2) {
return 0;
}
}
@@ -180,9 +180,10 @@ public abstract class AbstractScoringSchemeImpl implements ScoringScheme {
* scored. This implements Racing Rules of Sailing (RRS) section A8.2:
* <p>
*
* <em>"A8.2 If a tie remains between two or more boats, they shall be ranked in order of their scores in the last race.
* Any remaining ties shall be broken by using the tied boats' scores in the next-to-last race and so on until all
* ties are broken. These scores shall be used even if some of them are excluded scores."</em>
* <em>"A8.2 If a tie remains between two or more boats, they shall be ranked in order of their scores in the last
* race. Any remaining ties shall be broken by using the tied boats' scores in the next-to-last race and so on until
* all ties are broken. These scores shall be used even if some of them are excluded scores."</em>
*
* @param o1ScoresIncludingDiscarded
* scores that include the points for those races that have been discarded (total points)
* @param o2ScoresIncludingDiscarded
@@ -190,7 +191,9 @@ public abstract class AbstractScoringSchemeImpl implements ScoringScheme {
*/
@Override
public int compareByLastRace(List<com.sap.sse.common.Util.Pair<RaceColumn, Double>> o1ScoresIncludingDiscarded,
List<com.sap.sse.common.Util.Pair<RaceColumn, Double>> o2ScoresIncludingDiscarded, boolean nullScoresAreBetter, Competitor o1, Competitor o2, TimePoint timePoint, WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache) {
List<com.sap.sse.common.Util.Pair<RaceColumn, Double>> o2ScoresIncludingDiscarded,
boolean nullScoresAreBetter, Competitor o1, Competitor o2, TimePoint timePoint,
WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache) {
int result = 0;
final Comparator<Double> pureScoreComparator = getScoreComparator(nullScoresAreBetter);
ListIterator<Pair<RaceColumn, Double>> o1Iter = o1ScoresIncludingDiscarded.listIterator(o1ScoresIncludingDiscarded.size());
@@ -201,7 +204,7 @@ public abstract class AbstractScoringSchemeImpl implements ScoringScheme {
if (o1Iter.hasPrevious() != o2Iter.hasPrevious()) {
// if, as may be allowed by some scoring scheme variants, competitors with different numbers of scored races are compared
// and are equal for all races of the competitor who scored fewer races, the competitor who scored more races is preferred
result = o1Iter.hasNext() ? -1 : 1;
result = o1Iter.hasPrevious() ? -1 : 1;
}
return result;
}
@@ -142,8 +142,6 @@ public class LeaderboardTotalRankComparator implements Comparator<Competitor> {
Double o2MedalRaceScore = 0.0;
Double o1CarryForwardScoreInMedals = null;
Double o2CarryForwardScoreInMedals = null;
Double o1LastMedalScore = null;
Double o2LastMedalScore = null;
// When a column has isStartsWithZeroScore, the competitor's score only need to be reset to zero if from there on
// the competitor scored in this or any subsequent columns
boolean needToResetO1ScoreUponNextValidResult = false;
@@ -212,11 +210,9 @@ public class LeaderboardTotalRankComparator implements Comparator<Competitor> {
if (!raceColumn.isCarryForward()) {
if (o1Score != null) {
o1MedalRaceScore += o1Score;
o1LastMedalScore = o1Score;
}
if (o2Score != null) {
o2MedalRaceScore += o2Score;
o2LastMedalScore = o2Score;
}
} else {
o1CarryForwardScoreInMedals = o1Score;
@@ -282,12 +278,13 @@ public class LeaderboardTotalRankComparator implements Comparator<Competitor> {
zeroBasedIndexOfLastMedalSeriesInWhichO1Scored>=0 || zeroBasedIndexOfLastMedalSeriesInWhichO2Scored>=0);
if (result == 0) {
if (scoringScheme.isCarryForwardInMedalsCriteria()) {
result = compareBySingleRaceColumnScore(o1CarryForwardScoreInMedals, o2CarryForwardScoreInMedals);
result = scoringScheme.compareBySingleRaceColumnScore(o1CarryForwardScoreInMedals,
o2CarryForwardScoreInMedals, nullScoresAreBetter);
}
if (result == 0) {
if (scoringScheme.isLastMedalRaceCriteria()) {
result = compareBySingleRaceColumnScore(o1LastMedalScore, o2LastMedalScore);
}
result = scoringScheme.compareByLastMedalRacesCriteria(o1, o1Scores, o2, o2Scores, nullScoresAreBetter, leaderboard,
(competitor, raceColumn)->totalPointsCache.get(new Pair<>(competitor, raceColumn)),
cache, timePoint, zeroBasedIndexOfLastMedalSeriesInWhichO1Scored, numberOfMedalRacesWonO1, numberOfMedalRacesWonO2);
if (result == 0) {
result = scoringScheme.compareByMedalRaceScore(o1MedalRaceScore, o2MedalRaceScore, nullScoresAreBetter);
if (result == 0) {
@@ -324,14 +321,6 @@ public class LeaderboardTotalRankComparator implements Comparator<Competitor> {
return Util.indexOf(medalSeries.getRegatta().getSeries(), medalSeries);
}
/**
* Compares by the scores of a single race column. If only one of the competitors has a result this competitor is
* ranked better than the other one.
*/
private int compareBySingleRaceColumnScore(Double o1Score, Double o2Score) {
return Comparator.nullsLast((Double o1s, Double o2s)->getScoreComparator().compare(o1s, o2s)).compare(o1Score, o2Score);
}
private int compareByArbitraryButStableCriteria(Competitor o1, Competitor o2) {
return o1.getName().compareTo(o2.getName());
}
@@ -0,0 +1,39 @@
package com.sap.sailing.domain.leaderboard.impl;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.BiFunction;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.RaceColumn;
import com.sap.sailing.domain.common.ScoringSchemeType;
import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.tracking.WindLegTypeAndLegBearingAndORCPerformanceCurveCache;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util.Pair;
/**
* Eliminates the A8.1 comparison (sort scores by better/worst and compare one by one) and hence
* defaults to A8.2 (last race, second-to-last race, etc.).
*
* @author Axel Uhl (d043530)
*
*/
public class LowPointA82Only extends LowPoint {
private static final long serialVersionUID = 4715596377202890920L;
@Override
public int compareByBetterScore(Competitor o1, List<Pair<RaceColumn, Double>> o1Scores, Competitor o2,
List<Pair<RaceColumn, Double>> o2Scores, boolean nullScoresAreBetter, TimePoint timePoint,
Leaderboard leaderboard, Map<Competitor, Set<RaceColumn>> discardedRaceColumnsPerCompetitor,
BiFunction<Competitor, RaceColumn, Double> totalPointsSupplier,
WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache) {
return 0;
}
@Override
public ScoringSchemeType getType() {
return ScoringSchemeType.LOW_POINT_A82_ONLY;
}
}
@@ -1,16 +1,22 @@
package com.sap.sailing.domain.leaderboard.impl;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.RaceColumn;
import com.sap.sailing.domain.base.RaceColumnInSeries;
import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.base.Series;
import com.sap.sailing.domain.common.ScoringSchemeType;
import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.leaderboard.RegattaLeaderboard;
import com.sap.sailing.domain.leaderboard.caching.LeaderboardDTOCalculationReuseCache;
import com.sap.sailing.domain.tracking.WindLegTypeAndLegBearingAndORCPerformanceCurveCache;
import com.sap.sse.common.TimePoint;
@@ -61,16 +67,160 @@ public class LowPointFirstToWinThreeRaces extends LowPoint {
return ScoringSchemeType.LOW_POINT_FIRST_TO_WIN_THREE_RACES;
}
/**
* We cannot simply go by the number of medal races won because during the semi-final medal series which is split
* into two fleets the competitors are not ranked across these fleets based on wins but first within their fleet
* based on wins, then based on last race, second-to-last, ..., and then lastly by the opening series rank, and only
* then are the two semi-final fleets merged one by one, with two equal ranks in fleets A/B decided based on the
* opening series rank again. This all happens in
* {@link #compareByLastMedalRacesCriteria(Competitor, List, Competitor, List, boolean, Leaderboard, BiFunction, WindLegTypeAndLegBearingAndORCPerformanceCurveCache, TimePoint, int, int, int)}.
* Yet, we have to respond with {@code true} here in order to <em>count</em> the medal races won. We will then
* ignore that result in {@link #compareByMedalRacesWon(int, int)} and instead do it all in
* {@link #compareByLastMedalRacesCriteria(Competitor, List, Competitor, List, boolean, Leaderboard, BiFunction, WindLegTypeAndLegBearingAndORCPerformanceCurveCache, TimePoint, int, int, int)}.
*/
@Override
public boolean isMedalWinAmountCriteria() {
return true;
}
/**
* Always returns {@code 0}; the heavy lifting is done by
* {@link #compareByLastMedalRacesCriteria(Competitor, List, Competitor, List, boolean, Leaderboard, BiFunction, WindLegTypeAndLegBearingAndORCPerformanceCurveCache, TimePoint, int, int, int)}.
*/
@Override
public boolean isLastMedalRaceCriteria() {
return true;
public int compareByMedalRacesWon(int numberOfMedalRacesWonO1, int numberOfMedalRacesWonO2) {
return 0;
}
/**
* Assumes that {@code o1} and {@code o2} participated and scored in the same number of medal series and compares
* the two competitors with each other. If they both compete in the same fleet in their last medal series, first their
* number of wins (including the carried-forward wins), then the scores in the last, last-but-one, etc. races until a
* difference is found, and if no difference is found that way, by the opening series rank.<p>
*
* Should the competitors have competed in different fleets in the same medal series (like the A/B fleets of
* the semi-final stage) then first compute and compare their rank within their fleet by the criteria stated
* above, and if they both ranked equally well in their respective fleet, compare by their opening series rank.
*/
@Override
public int compareByLastMedalRacesCriteria(Competitor o1, List<Pair<RaceColumn, Double>> o1Scores, Competitor o2,
List<Pair<RaceColumn, Double>> o2Scores, boolean nullScoresAreBetter, Leaderboard leaderboard,
BiFunction<Competitor, RaceColumn, Double> totalPointsSupplier,
WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache, TimePoint timePoint,
int zeroBasedIndexOfLastMedalSeriesInWhichBothScored, int numberOfMedalRacesWonO1, int numberOfMedalRacesWonO2) {
int result;
if (zeroBasedIndexOfLastMedalSeriesInWhichBothScored < 0) {
result = 0; // neither scored in a medal series, so nothing to compare
} else {
if (!(leaderboard instanceof RegattaLeaderboard)) {
result = 0;
} else {
final Regatta regatta = ((RegattaLeaderboard) leaderboard).getRegatta();
final Series medalSeriesInWhichBothScored = Util.get(regatta.getSeries(), zeroBasedIndexOfLastMedalSeriesInWhichBothScored);
assert medalSeriesInWhichBothScored.isMedal();
final RaceColumn firstNonCarryRaceColumnInMedalSeries = Util.first(Util.filter(medalSeriesInWhichBothScored.getRaceColumns(),
rc->!rc.isCarryForward()));
final Fleet o1MedalFleet = firstNonCarryRaceColumnInMedalSeries.getFleetOfCompetitor(o1);
final Fleet o2MedalFleet = firstNonCarryRaceColumnInMedalSeries.getFleetOfCompetitor(o2);
final Iterable<RaceColumn> openingSeriesRaceColumns = getOpeningSeriesRaceColumns(leaderboard);
// pass on the totalPointsSupplier coming from the caller, most likely a LeaderboardTotalRankComparator,
// to speed up / save the total points (re-)calculation
final Supplier<LeaderboardTotalRankComparator> openingSeriesTotalRankComparator = ()->new LeaderboardTotalRankComparator(
leaderboard, timePoint, this, nullScoresAreBetter, openingSeriesRaceColumns,
totalPointsSupplier, cache); // TODO turn into supplier for lazy evaluation; it may not be needed in all cases
if (o1MedalFleet == o2MedalFleet) {
result = compareByInMedalSeriesFleetRules(o1, o2, totalPointsSupplier, medalSeriesInWhichBothScored, openingSeriesTotalRankComparator,
nullScoresAreBetter, leaderboard, timePoint, cache);
} else {
// o1 and o2 scored in different fleets of the same medal series; compare as follows:
// - by their rank inside their fleet
final int o1RankInMedalSeriesFleet = getRankInMedalSeriesFleet(medalSeriesInWhichBothScored, o1MedalFleet, totalPointsSupplier, o1,
openingSeriesTotalRankComparator, firstNonCarryRaceColumnInMedalSeries, nullScoresAreBetter, leaderboard, timePoint, cache);
final int o2RankInMedalSeriesFleet = getRankInMedalSeriesFleet(medalSeriesInWhichBothScored, o2MedalFleet, totalPointsSupplier, o2,
openingSeriesTotalRankComparator, firstNonCarryRaceColumnInMedalSeries, nullScoresAreBetter, leaderboard, timePoint, cache);
result = Integer.compare(o1RankInMedalSeriesFleet, o2RankInMedalSeriesFleet);
if (result == 0) {
// - then by opening series rank
result = openingSeriesTotalRankComparator.get().compare(o1, o2);
}
}
}
}
return result;
}
private int getRankInMedalSeriesFleet(Series medalSeries, Fleet medalFleet,
BiFunction<Competitor, RaceColumn, Double> totalPointsSupplier, Competitor competitor,
final Supplier<LeaderboardTotalRankComparator> openingSeriesTotalRankComparator, RaceColumn firstNonCarryRaceColumnInMedalSeries,
boolean nullScoresAreBetter,
Leaderboard leaderboard, TimePoint timePoint, WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache) {
final List<Competitor> competitorsInMedalFleet = Util.asList(firstNonCarryRaceColumnInMedalSeries.getAllCompetitors(medalFleet));
competitorsInMedalFleet.sort((c1, c2)->compareByInMedalSeriesFleetRules(c1, c2, totalPointsSupplier, medalSeries,
openingSeriesTotalRankComparator, nullScoresAreBetter, leaderboard, timePoint, cache));
return competitorsInMedalFleet.indexOf(competitor)+1;
}
/**
* Compares two competitors who scored in the same fleet of the same medal series by the following criteria,
* in this order of decreasing precedence:
* <ul>
* <li>The number of "wins" (including the wins carried from an earlier stage and noted in the corresponding "carry forward" column)</li>
* <li>The scores achieved in the medal races, from last to first, until the first difference is found</li>
* <li>The opening series rank</li>
* </ul>
*/
private int compareByInMedalSeriesFleetRules(Competitor o1, Competitor o2,
BiFunction<Competitor, RaceColumn, Double> totalPointsSupplier, final Series medalSeriesInWhichBothScored,
final Supplier<LeaderboardTotalRankComparator> openingSeriesTotalRankComparator,
boolean nullScoresAreBetter, Leaderboard leaderboard, TimePoint timePoint,
WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache) {
int result;
final int numberOfMedalRacesWonO1 = Util.stream(medalSeriesInWhichBothScored.getRaceColumns())
.mapToInt(raceColumn -> getWinCount(leaderboard, o1, raceColumn,
totalPointsSupplier.apply(o1, raceColumn), timePoint,
c -> totalPointsSupplier.apply(c, raceColumn), cache))
.sum();
final int numberOfMedalRacesWonO2 = Util.stream(medalSeriesInWhichBothScored.getRaceColumns())
.mapToInt(raceColumn -> getWinCount(leaderboard, o2, raceColumn,
totalPointsSupplier.apply(o2, raceColumn), timePoint,
c -> totalPointsSupplier.apply(c, raceColumn), cache))
.sum();
// compare by in-fleet rules:
// - first by number of wins:
if (numberOfMedalRacesWonO1 != numberOfMedalRacesWonO2) {
result = Integer.compare(numberOfMedalRacesWonO2, numberOfMedalRacesWonO1);
} else {
// - then by comparing scores statrting at the last race in the series, going backward until tie is broken or first race is reached
result = compareByScoresFromLastToFirstRace(medalSeriesInWhichBothScored.getRaceColumns(), o1, o2, totalPointsSupplier, nullScoresAreBetter);
if (result == 0) {
// - then by opening series rank
result = openingSeriesTotalRankComparator.get().compare(o1, o2);
}
}
return result;
}
/**
* Ignores the first carry column in the medal series where the carried wins are expected. Both competitors
* must have sailed in the same fleet in this race column.
*/
private int compareByScoresFromLastToFirstRace(Iterable<? extends RaceColumnInSeries> raceColumns, Competitor o1,
Competitor o2, BiFunction<Competitor, RaceColumn, Double> totalPointsSupplier, boolean nullScoresAreBetter) {
assert Util.stream(raceColumns).allMatch(rc->rc.getFleetOfCompetitor(o1) == rc.getFleetOfCompetitor(o2));
final List<? extends RaceColumnInSeries> medalRaceRolumnsWithReverseOrdering = Util.asList(raceColumns);
Collections.reverse(medalRaceRolumnsWithReverseOrdering);
int result = 0;
for (final RaceColumnInSeries raceColumn : medalRaceRolumnsWithReverseOrdering) {
final Double o1Score = totalPointsSupplier.apply(o1, raceColumn);
final Double o2Score = totalPointsSupplier.apply(o2, raceColumn);
result = getScoreComparator(nullScoresAreBetter).compare(o1Score, o2Score);
if (result != 0) {
break;
}
}
return result;
}
/**
* Still returns {@code null} if {@link Leaderboard#getNetPoints(Competitor, RaceColumn, TimePoint, Set)} returns {@code null}.
* Otherwise, if the {@code raceColumn} is a medal race column and not the medal series' carry-forward column, 1.0 is returned
@@ -120,7 +270,8 @@ public class LowPointFirstToWinThreeRaces extends LowPoint {
*/
@Override
public int getWinCount(Leaderboard leaderboard, Competitor competitor, RaceColumn raceColumn,
final Double totalPoints, TimePoint timePoint, Function<Competitor, Double> totalPointsSupplier, WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache) {
final Double totalPoints, TimePoint timePoint, Function<Competitor, Double> totalPointsSupplier,
WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache) {
final Integer winCount;
if (raceColumn.isCarryForward()) {
winCount = totalPoints == null ? 0 : totalPoints.intValue();
@@ -129,11 +280,27 @@ public class LowPointFirstToWinThreeRaces extends LowPoint {
}
return winCount;
}
/**
* See OG2024_SAL_ORIS_R8_V1.2_20230330.pdf: "A boat shall be credited with a win when it is scored with one point
* in a medal series race."
*/
@Override
public boolean isWin(Leaderboard leaderboard, Competitor competitor, RaceColumn raceColumn, TimePoint timePoint,
Function<Competitor, Double> totalPointsSupplier,
WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache) {
return isWin(competitor, totalPointsSupplier);
}
private boolean isWin(Competitor competitor, Function<Competitor, Double> totalPointsSupplier) {
return Util.equalsWithNull(totalPointsSupplier.apply(competitor), Double.valueOf(1.0));
}
/**
* When the competitors have valid medal race scores, this scoring scheme ignores the score sums altogether and
* assumes that a {@link #compareByMedalRacesWon(int, int) comparison by the number of medal races won} is being
* performed because {@link #isMedalWinAmountCriteria()} returns {@code true} for this scoring scheme.
* assumes that
* {@link #compareByLastMedalRacesCriteria(Competitor, List, Competitor, List, boolean, Leaderboard, BiFunction, WindLegTypeAndLegBearingAndORCPerformanceCurveCache, TimePoint, int, int, int)}
* handles the medal series ranking altogether.
*/
@Override
public int compareByScoreSum(double o1ScoreSum, double o2ScoreSum, boolean nullScoresAreBetter,
@@ -155,17 +322,20 @@ public class LowPointFirstToWinThreeRaces extends LowPoint {
* decide upon the first difference), across <em>all</em> scores throughout the leaderboard. Here, however, we need
* to distinguish between competitors who only sailed in the opening series (anything preceding the first medal
* series), and those who did score in at least one medal race, because the medal series are evaluated only by wins
* and as a secondary tie breaker then by the last medal race score and after that the rank at the end of the opening
* series.<p>
* and as a secondary tie breaker then by the medal race scores looked at from the end, and after that the rank at
* the end of the opening series.
* <p>
*
* It is safe to assume that if {@code o1} has valid medal series scores then so will {@code o2}, and vice versa,
* because otherwise ranking by medal series participation would already have decided who ranks better. If none has
* score in a medal race then we default to the {@code super} implementation with a default RRS A8.1 decision.<p>
* score in a medal race then we default to the {@code super} implementation with a default RRS A8.1 decision.
* <p>
*
* Otherwise (both have scored in at least one medal race) we have to assume that both sailed in the same medal series,
* scored the same number of wins (including wins carried forward) and were also tied on the scores in their respective
* last medal race. Then, the decision is to be made based on the opening series rank, which in itself includes the
* entire tie-breaking rule set with {@link #compareByBetterScore(Competitor, List, Competitor, List, boolean, TimePoint, Leaderboard, Map, BiFunction, WindLegTypeAndLegBearingAndORCPerformanceCurveCache)}
* Otherwise (both have scored in at least one medal race) we have to assume that both sailed in the same medal
* series, scored the same number of wins (including wins carried forward) and were also tied on the scores in their
* respective last medal race. Then, the decision is to be made based on the opening series rank, which in itself
* includes the entire tie-breaking rule set with
* {@link #compareByBetterScore(Competitor, List, Competitor, List, boolean, TimePoint, Leaderboard, Map, BiFunction, WindLegTypeAndLegBearingAndORCPerformanceCurveCache)}
* etc., only up to the end of the opening series.
*/
@Override
@@ -175,7 +345,7 @@ public class LowPointFirstToWinThreeRaces extends LowPoint {
BiFunction<Competitor, RaceColumn, Double> totalPointsSupplier, WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache) {
final int result;
if (!hasMedalScores(o1Scores)) {
result = super.compareByBetterScore(o1, o1Scores, o2, o2Scores, nullScoresAreBetter, timePoint, leaderboard,
result = compareByA81TieBreak(o1, o1Scores, o2, o2Scores, nullScoresAreBetter, timePoint, leaderboard,
discardedRaceColumnsPerCompetitor, totalPointsSupplier, cache);
} else {
final Iterable<RaceColumn> openingSeriesRaceColumns = getOpeningSeriesRaceColumns(leaderboard);
@@ -187,6 +357,23 @@ public class LowPointFirstToWinThreeRaces extends LowPoint {
return result;
}
/**
* Computes a tie break; this default implementation delegates to the super-class implementation which is
* expected to compute a regular A8.1 tie break, sorting the scores so the best are first, and then looking
* for the first difference.<p>
*
* Subclasses may change this behavior, e.g., so that 0 is returned and hence A8.1 is ignored; this will
* usually then default to a comparison based on A8.2 (last race).
*/
protected int compareByA81TieBreak(Competitor o1, List<Pair<RaceColumn, Double>> o1Scores, Competitor o2,
List<Pair<RaceColumn, Double>> o2Scores, boolean nullScoresAreBetter, TimePoint timePoint,
Leaderboard leaderboard, Map<Competitor, Set<RaceColumn>> discardedRaceColumnsPerCompetitor,
BiFunction<Competitor, RaceColumn, Double> totalPointsSupplier,
WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache) {
return super.compareByBetterScore(o1, o1Scores, o2, o2Scores, nullScoresAreBetter, timePoint, leaderboard,
discardedRaceColumnsPerCompetitor, totalPointsSupplier, cache);
}
/**
* Merge non-medal series columns, preserving order across {@code raceColumnsO1} and {@code raceColumnsO2}.
*/
@@ -0,0 +1,39 @@
package com.sap.sailing.domain.leaderboard.impl;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.BiFunction;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.RaceColumn;
import com.sap.sailing.domain.common.ScoringSchemeType;
import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.tracking.WindLegTypeAndLegBearingAndORCPerformanceCurveCache;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util.Pair;
/**
* Turns off the A8.1 comparison for opening series tie-breaking, thus defaulting to A8.2 (last race,
* then second-to-last, etc.).
*
* @author Axel Uhl (d043530)
*
*/
public class LowPointFirstToWinThreeRacesA82Only extends LowPointFirstToWinThreeRaces {
private static final long serialVersionUID = 8820858236331467431L;
@Override
protected int compareByA81TieBreak(Competitor o1, List<Pair<RaceColumn, Double>> o1Scores, Competitor o2,
List<Pair<RaceColumn, Double>> o2Scores, boolean nullScoresAreBetter, TimePoint timePoint,
Leaderboard leaderboard, Map<Competitor, Set<RaceColumn>> discardedRaceColumnsPerCompetitor,
BiFunction<Competitor, RaceColumn, Double> totalPointsSupplier,
WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache) {
return 0;
}
@Override
public ScoringSchemeType getType() {
return ScoringSchemeType.LOW_POINT_FIRST_TO_WIN_THREE_RACES_A82_ONLY;
}
}
@@ -1,8 +1,17 @@
package com.sap.sailing.domain.leaderboard.impl;
import java.util.List;
import java.util.function.BiFunction;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.RaceColumn;
import com.sap.sailing.domain.common.ScoringSchemeType;
import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.leaderboard.ScoringScheme;
import com.sap.sailing.domain.tracking.WindLegTypeAndLegBearingAndORCPerformanceCurveCache;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util;
import com.sap.sse.common.Util.Pair;
/**
* This class is a derivation of the LowPointScoring scheme. The following additional rules apply:
@@ -40,10 +49,32 @@ public class LowPointFirstToWinTwoRaces extends LowPoint {
}
@Override
public boolean isLastMedalRaceCriteria() {
return true;
public int compareByLastMedalRacesCriteria(Competitor o1, List<Pair<RaceColumn, Double>> o1Scores, Competitor o2,
List<Pair<RaceColumn, Double>> o2Scores, boolean nullScoresAreBetter, Leaderboard leaderboard,
BiFunction<Competitor, RaceColumn, Double> totalPointsSupplier, WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache, TimePoint timePoint, int zeroBasedIndexOfLastMedalSeriesInWhichBothScored, int numberOfMedalRacesWonO1, int numberOfMedalRacesWonO2) {
final Pair<RaceColumn, Double> o1LastNonNullMedalRaceScore = getLastNonNullMedalRaceScore(o1Scores);
final Pair<RaceColumn, Double> o2LastNonNullMedalRaceScore = getLastNonNullMedalRaceScore(o2Scores);
final int result;
if (o1LastNonNullMedalRaceScore == null) {
if (o2LastNonNullMedalRaceScore == null) {
result = 0;
} else {
result = nullScoresAreBetter ? -1 : 1;
}
} else if (o2LastNonNullMedalRaceScore == null) {
result = nullScoresAreBetter ? 1 : -1;
} else {
result = compareBySingleRaceColumnScore(o1LastNonNullMedalRaceScore.getB(), o2LastNonNullMedalRaceScore.getB(), nullScoresAreBetter);
}
return result;
}
private Pair<RaceColumn, Double> getLastNonNullMedalRaceScore(List<Pair<RaceColumn, Double>> o2Scores) {
return Util.first(Util.filter(
() -> o2Scores.listIterator(o2Scores.size()),
raceColumnAndScore -> raceColumnAndScore.getA().isMedalRace() && raceColumnAndScore.getB() != null));
}
/**
* If {@link #isMedalWinAmountCriteria()} returns {@code true}, this will be the amount of races that must be won,
* in order to win the medal series instantly
+7 -2
View File
@@ -723,8 +723,13 @@ img.openColumn {
}
.RegattaAndRaceTime-Header .RaceTime-Label {
font-size: 15px;
margin-top: 5px;
font-size: 15px;
margin-top: 5px;
}
.RegattaAndRaceTime-Header .HelpButton {
position: absolute;
left: -25px;
}
.compactHeader .RegattaAndRaceTime-Header>* {
@@ -86,6 +86,7 @@ public class BoatClassImageResolver {
boatClassIconsMap.put(BoatClassMasterdata.HOBIE_TIGER.getDisplayName(), imageResources.HobieTigerIcon());
boatClassIconsMap.put(BoatClassMasterdata.HOBIE_WILD_CAT.getDisplayName(), imageResources.HobieWildCatIcon());
boatClassIconsMap.put(BoatClassMasterdata.INTERNATIONAL_14.getDisplayName(), imageResources.International14Icon());
boatClassIconsMap.put(BoatClassMasterdata.IMOCA.getDisplayName(), imageResources.ImocaIcon());
boatClassIconsMap.put(BoatClassMasterdata.IQFOIL_MEN.getDisplayName(), imageResources.IQFOilMenIcon());
boatClassIconsMap.put(BoatClassMasterdata.IQFOIL_WOMEN.getDisplayName(), imageResources.IQFOilWoenIcon());
boatClassIconsMap.put(BoatClassMasterdata.IQFOIL_YOUTH.getDisplayName(), imageResources.IQFOilYouthIcon());
@@ -159,6 +160,8 @@ public class BoatClassImageResolver {
boatClassIconsMap.put(BoatClassMasterdata.TRIAS.getDisplayName(), imageResources.TriasIcon());
boatClassIconsMap.put(BoatClassMasterdata.VARIANTA.getDisplayName(), imageResources.VariantaIcon());
boatClassIconsMap.put(BoatClassMasterdata.VAURIEN.getDisplayName(), imageResources.VaurienIcon());
boatClassIconsMap.put(BoatClassMasterdata.VO60.getDisplayName(), imageResources.VOIcon());
boatClassIconsMap.put(BoatClassMasterdata.VO65.getDisplayName(), imageResources.VOIcon());
boatClassIconsMap.put(BoatClassMasterdata.VENT_D_OUEST.getDisplayName(), imageResources.VentdOuestIcon());
boatClassIconsMap.put(BoatClassMasterdata.VIPER_640.getDisplayName(), imageResources.Viper640Icon());
boatClassIconsMap.put(BoatClassMasterdata.VX_ONE.getDisplayName(), imageResources.VXOneIcon());
@@ -432,6 +432,10 @@ public interface BoatClassImageResources extends ClientBundle {
@ImageOptions(preventInlining = true)
ImageResource International14Icon();
@Source("com/sap/sailing/gwt/ui/client/images/boatclass/IMOCA.png")
@ImageOptions(preventInlining = true)
ImageResource ImocaIcon();
@Source("com/sap/sailing/gwt/ui/client/images/boatclass/IQFOIL_MEN.png")
@ImageOptions(preventInlining = true)
ImageResource IQFOilMenIcon();
@@ -596,6 +600,10 @@ public interface BoatClassImageResources extends ClientBundle {
@ImageOptions(preventInlining = true)
ImageResource VariantaIcon();
@Source("com/sap/sailing/gwt/ui/client/images/boatclass/VO.png")
@ImageOptions(preventInlining = true)
ImageResource VOIcon();
@Source("com/sap/sailing/gwt/ui/client/images/boatclass/X_99.png")
@ImageOptions(preventInlining = true)
ImageResource X99Icon();
@@ -1,14 +1,17 @@
@def SHADOW_COLOR #21a0e4;
.help-icon {
width: 23px;
width: 17px;
position: absolute;
height: auto;
margin: 3px;
box-shadow: 0 0 5px SHADOW_COLOR;
background-color: rgba(33, 160, 228, .4);
border-radius: 100%;
}
.help-popup {
background-color: white;
box-shadow: 0 0 10px SHADOW_COLOR;
border: 0 none;
}
@@ -1,6 +1,7 @@
package com.sap.sailing.gwt.common.client.help;
import com.google.gwt.dom.client.Style.Cursor;
import com.google.gwt.uibinder.client.UiConstructor;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.Image;
@@ -8,6 +9,10 @@ public class HelpButton extends Composite {
private final Image icon;
private final HelpButtonPopup popup;
public @UiConstructor HelpButton(final String description, final String url) {
this(HelpButtonResources.INSTANCE, description, url);
}
public HelpButton(final HelpButtonResources resources, final String description, final String url) {
resources.style().ensureInjected();
this.icon = new Image(resources.icon());
Binary file not shown.

Before

Width:  |  Height:  |  Size: 784 B

After

Width:  |  Height:  |  Size: 1.1 KiB

@@ -30,6 +30,7 @@ public class SailorProfileDTO implements Result, Serializable {
private ArrayList<BadgeDTO> badges = new ArrayList<>();
private ArrayList<BoatClassDTO> boatclasses = new ArrayList<>();
@Deprecated //Used for GWT serialization only.
protected SailorProfileDTO() {
}
@@ -4,6 +4,13 @@
<div id="mainContent">
<h4 class="articleHeadline">What's New - SAP Sailing Race Manager</h4>
<div class="innerContent">
<h5 class="articleSubheadline">April 2023</h5>
<ul class="bulletList">
<li>Bug fix: added missing handler for Olympic mode Race Log events such as the "result are official"
confirmation event.</li>
<li>Backward compatibility fix: accidentally introduced dependency on newer Android API 24
which caused trouble on older Android versions, e.g., before Android 7.x.</li>
</ul>
<h5 class="articleSubheadline">June 2022</h5>
<ul class="bulletList">
<li>Bug fix: When starting the app after killing it, with unsent messages buffered, fixed
@@ -4,6 +4,19 @@
<div id="mainContent">
<h4 class="articleHeadline">What's New - SAP Sailing Analytics</h4>
<div class="innerContent">
<h5 class="articleSubheadline">May 2023</h5>
<ul class="bulletList">
<li>Fixed an issue with the plans shown as subscribed to based on the plan
categories. For premium users it would have seemed they implicitly got
a data mining plan although they did not. Please note that currently
you still require to get in touch (support@sapsailing.com) in order
to upgrade from a Premium to a data mining plan. This is a known
issue that we are working on currently.</li>
</ul>
<h5 class="articleSubheadline">April 2023</h5>
<ul class="bulletList">
<li>Added XTE (cross-track error) to data mining statistics on GPS fixes</li>
</ul>
<h5 class="articleSubheadline">March 2023</h5>
<ul class="bulletList">
<li>Added VMG to data mining statistics on GPS fixes</li>
@@ -2,7 +2,8 @@
<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder" xmlns:g="urn:import:com.google.gwt.user.client.ui"
xmlns:s="urn:import:com.sap.sailing.gwt.home.client.shared"
xmlns:mp="urn:import:com.sap.sailing.gwt.home.mobile.partials"
xmlns:sp="urn:import:com.sap.sailing.gwt.home.shared.partials">
xmlns:sp="urn:import:com.sap.sailing.gwt.home.shared.partials"
xmlns:h="urn:import:com.sap.sailing.gwt.common.client.help">
<ui:with field="i18n" type="com.sap.sailing.gwt.ui.client.StringMessages" />
<ui:with field="res" type="com.sap.sailing.gwt.common.client.SharedResources" />
<ui:style>
@@ -17,10 +18,17 @@
.filter > input {
width: 100%;
}
.help {
position: absolute;
left: 138px;
}
</ui:style>
<g:HTMLPanel>
<div class="{res.mediaCss.columns}">
<div class="{res.mediaCss.small12} {style.pageTitle}"><ui:text from="{i18n.racesAndTracking}" /></div>
<div class="{res.mediaCss.small12} {style.pageTitle}">
<ui:text from="{i18n.racesAndTracking}" />
<h:HelpButton addStyleNames= "{style.help}" ui:field='HelpButton' description='Video Guide' url='https://support.sapsailing.com/hc/en-us/articles/7275243525148-Tracking-Race-Player-Overview' />
</div>
<sp:filter.RacesByCompetitorTextBoxFilter addStyleNames="{res.mediaCss.small12} {style.filter}" ui:field="competitorFilterUi" />
</div>
<mp:regattacompetition.RegattaCompetition ui:field="regattaCompetitionUi" />
@@ -220,8 +220,8 @@ public class RaceContext {
lastFlagsDisplayedStateChanged = previousFlagState.hasPoleChanged(mostInterestingFlagPole);
}
}
switch (state.getStatus()) {
case FINISHED:
final RaceLogRaceStatus status = state.getStatus();
if (status == RaceLogRaceStatus.FINISHED) {
TimeRange protestTime = state.getProtestTime();
if (protestTime != null) {
lastUpperFlag = Flags.BRAVO;
@@ -229,8 +229,7 @@ public class RaceContext {
lastFlagsAreDisplayed = true;
lastFlagsDisplayedStateChanged = true;
}
break;
case UNSCHEDULED:
} else if (state.getStatus().isAbortingFlagFromPreviousPassValid()) {
// search for race aborting in last pass
AbortingFlagFinder abortingFlagFinder = new AbortingFlagFinder(raceLog);
RaceLogFlagEvent abortingFlagEvent = abortingFlagFinder.analyze();
@@ -240,20 +239,14 @@ public class RaceContext {
lastFlagsAreDisplayed = abortingFlagEvent.isDisplayed();
lastFlagsDisplayedStateChanged = true;
}
break;
case FINISHING:
case RUNNING:
case SCHEDULED:
case PRESCHEDULED:
case STARTPHASE:
case UNKNOWN:
break;
}
;
final FlagStateDTO result;
if (lastUpperFlag != null || lastLowerFlag != null) {
return new FlagStateDTO(lastUpperFlag, lastLowerFlag, lastFlagsAreDisplayed, lastFlagsDisplayedStateChanged);
result = new FlagStateDTO(lastUpperFlag, lastLowerFlag, lastFlagsAreDisplayed, lastFlagsDisplayedStateChanged);
} else {
result = null;
}
return null;
return result;
}
private String getCourseAreaOrNull() {
@@ -592,7 +585,7 @@ public class RaceContext {
RaceLogFlagEvent abortingFlagEvent = abortingFlagFinder.analyze();
if (abortingFlagEvent != null) {
RaceLogRaceStatus lastStatus = state.getStatus();
if (lastStatus.equals(RaceLogRaceStatus.UNSCHEDULED)) {
if (lastStatus.isAbortingFlagFromPreviousPassValid()) {
result = abortingFlagEvent;
}
}
@@ -91,7 +91,7 @@ public abstract class AbstractSubscriptionActivity extends AbstractActivity impl
} else {
groupDTO = new SubscriptionGroupDTO(plan.getGroup().getId(),
plan.isUserSubscribedToPlan(), plan.getPrices(), plan.getGroup(),
plan.isUserSubscribedToPlanCategory(), plan.getError(), type);
plan.isUserSubscribedToAllPlanCategories(), plan.getError(), type);
groupMap.put(plan.getGroup(), groupDTO);
}
});
@@ -147,7 +147,7 @@ public abstract class AbstractSubscriptionActivity extends AbstractActivity impl
}
private boolean checkIfUserIsSubscribedToPlanCategory(final SubscriptionPlanDTO plan) {
return plan.isUserSubscribedToPlanCategory();
return plan.isUserSubscribedToAllPlanCategories();
}
private boolean checkIfUserWasAlreadySubscripedToOneTimePlan(final SubscriptionPlanDTO plan) {
@@ -2426,4 +2426,8 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages,
String scoringSchemeHighPointsByWindTiesLastlyBrokenByOtherLeaderboard();
String scoringSchemeHighPointsByWindTiesLastlyBrokenByOtherLeaderboardDescription();
String helptextLinkingRaces();
String scoringSchemeLowPointA82Only();
String scoringSchemeLowPointA82OnlyDescription();
String scoringSchemeLowPointSystemFirstThreeWinsA82Only();
String scoringSchemeLowPointSystemFirstThreeWinsA82OnlyDescription();
}
@@ -2463,3 +2463,7 @@ otherTieBreakingLeaderboard=Other tie-breaking leaderboard
scoringSchemeHighPointsByWindTiesLastlyBrokenByOtherLeaderboard=High Point System, Match Racing, Tie-Break Based on other Leaderboard
scoringSchemeHighPointsByWindTiesLastlyBrokenByOtherLeaderboardDescription=The winner of a race gets 1 point, all others 0; ties are broken on last race, then based on another leaderboard
helptextLinkingRaces=In order to link races to tracked races, you must left-click a race in the table below and after that left-click the corresponding entry in the tracked races table on the right.
scoringSchemeLowPointA82Only=Low Point System; Tie-Break According to RRS A8.2 (Last Race)
scoringSchemeLowPointA82OnlyDescription=Low Point System; Tie-Break According to RRS A8.2; If a tie remains after looking at the medal race participation and scores, compare the scores in the last race (including scores excluded), then the second-to-last race, etc., until the tie is broken.
scoringSchemeLowPointSystemFirstThreeWinsA82Only=Low Point System, First with three wins in medals is winner; A8.2 tie-break (last race)
scoringSchemeLowPointSystemFirstThreeWinsA82OnlyDescription=Low Point System. The first in the medal series to win three races wins the medal series. A carry-forward column in the medal series can be used to model wins carried over. Tie-breaking in the opening series is based on A8.2 (last race, then second-to-last, etc.).
@@ -2457,3 +2457,7 @@ otherTieBreakingLeaderboard=Jiná výsledková tabule pořadí při shodě
scoringSchemeHighPointsByWindTiesLastlyBrokenByOtherLeaderboard=Vysokobodový systém, souboj, shoda založená na jiné výsledkové tabuli
scoringSchemeHighPointsByWindTiesLastlyBrokenByOtherLeaderboardDescription=Vítěz závodu získává 1 bod, všichni ostatní 0; při shodě rozhoduje poslední rozjížďka, potom údaje jiné výsledkové tabule.
helptextLinkingRaces=Chcete-li propojit rozjížďky s trasovanými rozjížďkami, musíte kliknout levým tlačítkem na rozjížďku v tabulce níže a poté klikněte levým tlačítkem na odpovídající záznam v tabulce trasovaných rozjížděk vpravo.
scoringSchemeLowPointA82Only=Nízkobodový systém; shoda podle závodních pravidel jachtingu A8.2 (poslední rozjížďka)
scoringSchemeLowPointA82OnlyDescription=Nízkobodový systém; shoda podle závodních pravidel jachtingu A8.2; Pokud shoda přetrvá po kontrole účasti a skóre ve finálové rozjížďce, porovnejte skóre v poslední rozjížďce (včetně vyloučených skóre), potom v předposlední atd., dokud shoda nebude rozhodnuta.=scoringSchemeLowPointSystemFirstThreeWinsA82Only
scoringSchemeLowPointSystemFirstThreeWinsA82Only=Nízkobodový systém; první se třemi vítězstvími ve finálovém závodu je vítěz; shoda A8.2 (poslední rozjížďka)
scoringSchemeLowPointSystemFirstThreeWinsA82OnlyDescription=Nízkobodový systém. První ve finálovém závodu, který vyhraje tři rozjížďky, vyhrává finálový závod. Sloupec přenosu ve finálovém závodu lze použít k modelování přenášených vítězství. Shoda v úvodních závodech je založena na A8.2 (poslední rozjížďka, pak předposlední atd.).
@@ -2457,3 +2457,7 @@ otherTieBreakingLeaderboard=Anden afgørende rangliste
scoringSchemeHighPointsByWindTiesLastlyBrokenByOtherLeaderboard=Højpointsystem, matchracing, tie-break baseret på anden rangliste
scoringSchemeHighPointsByWindTiesLastlyBrokenByOtherLeaderboardDescription=Vinderen af en kapsejlads får 1 point, alle andre 0 point; uafgjort kapsejladser afgøres ved sidste løb og derefter på grundlag af en anden rangliste
helptextLinkingRaces=For at knytte kapsejladser til sporede kapsejladser skal du venstreklikke på en kapsejlads i tabellen nedenfor og derefter venstreklikke på den tilhørende entry i tabellen for sporede kapsejladser i højre side.
scoringSchemeLowPointA82Only=Lavpointsystem; tie-break iht. kapsejladsreglerne A8.2 (sidste kapsejlads)
scoringSchemeLowPointA82OnlyDescription=Lavpointsystem; tie-break iht. kapsejladsreglerne A8.2; hvis der stadig er pointlighed efter at have kigget på medaljesejladsdeltagelsen og pointene, skal du sammenligne pointene i den sidste kapsejlads (herunder udelukkede point); derefter den næstsidste kapsejlads osv., indtil pointligheden er afgjort.=scoringSchemeLowPointSystemFirstThreeWinsA82Only
scoringSchemeLowPointSystemFirstThreeWinsA82Only=Lavpointsystem, første med tre sejre i medaljekapsejladser er vinderen; A8.2 tie-break (sidste kapsejlads)
scoringSchemeLowPointSystemFirstThreeWinsA82OnlyDescription=Lavpointsystem. Den første i medaljeserien, der vinder tre kapsejladser, vinder medaljeserien. En kolonne til fremførsel i medaljeserien kan bruges til at modellere overførte sejre. Tie-break i åbningsserien er baseret på A8.2 (sidste kapsejlads, derefter næstsidste osv.).
@@ -2458,4 +2458,8 @@ goToSelfServicePortalDialogText=Self-Service Portal des Abonementanbieters in ei
failedFetchingSelfServicePortalSession=Self-Service Portal konnte nicht geöffnet werden.
oneAlwaysStaysOne=Wertung 1 bleibt 1, unabhängig vom Spalten-Faktor
otherTieBreakingLeaderboard=Andere Rangliste zum Tie-Breaking
helptextLinkingRaces=Um unverknüpfte Wettfahrten mit getrackten Rennen zu verknüpfen, müssen sie in der untenstehenden Tabelle eine Wettfahrt linksklicken und danach das entsprechende getrackte Rennen in der rechten Ansicht linksklicken.
helptextLinkingRaces=Um unverknüpfte Wettfahrten mit getrackten Rennen zu verknüpfen, müssen sie in der untenstehenden Tabelle eine Wettfahrt linksklicken und danach das entsprechende getrackte Rennen in der rechten Ansicht linksklicken.
scoringSchemeLowPointA82Only=Low Point System; Tie-Break gemäß RRS A8.2 (Letztes Rennen)
scoringSchemeLowPointA82OnlyDescription=Low Point System; Tie-Break gemäß RRS A8.2; Falls ein Unentschieden nach dem Vergleich der Medal-Rennen verbleibt, vergleiche die Wertungen im letzten non-Medal Rennen, dann dem zweitletzten Rennen usw., bis ein Unterschied auftritt. Gestrichene Ergebnisse werden dabei ebenfalls berücksichtigt.
scoringSchemeLowPointSystemFirstThreeWinsA82Only=Low Point System, Erster mit drei Siegen in Medaillen-Rennen gewinnt; Tie-Break nach A8.2 (Letztes Rennen)
scoringSchemeLowPointSystemFirstThreeWinsA82OnlyDescription=Low Point System. Der Erste, der drei Rennen in den Medaillen-Rennen gewinnt, zählt als Sieger der Medaillen-Serie. Eine Übertragsspalte in der Medaillen-Serie kann für vorgetragene Siege verwendet werden; Tie-Break in der Eröffnungs-Serie gemäß A8.2 (letztes Rennen, dann zweitletztes Rennen usw.)
@@ -2457,3 +2457,7 @@ otherTieBreakingLeaderboard=Otra tabla de clasificación de desempate
scoringSchemeHighPointsByWindTiesLastlyBrokenByOtherLeaderboard=Sistema de puntuación alta, match racing, desempate en base a otra tabla de clasificación
scoringSchemeHighPointsByWindTiesLastlyBrokenByOtherLeaderboardDescription=El ganador de una prueba obtiene 1 punto, todos los demás 0; se desempata en la última prueba, y luego se basan en otra tabla de clasificación
helptextLinkingRaces=Con el fin de enlazar las pruebas con las pruebas rastreadas, debe hacer clic con el botón izquierdo del ratón en una prueba de la tabla siguiente y, a continuación, hacer clic con el botón izquierdo del ratón en la entrada correspondiente de la tabla de pruebas rastreadas de la derecha.
scoringSchemeLowPointA82Only=Sistema de puntuación baja; desempate según RRS A8.2 (última prueba)
scoringSchemeLowPointA82OnlyDescription=Sistema de puntuación baja; desempate según RRS A8.2; Si el empate se mantiene tras la participación en la prueba de medalla y las puntuaciones, compare las puntuaciones de la última prueba (incluyendo puntuaciones excluidas), luego la penúltima prueba, etc. Hasta que el empate se deshaga.scoringSchemeLowPointSystemFirstThreeWinsA82Only
scoringSchemeLowPointSystemFirstThreeWinsA82Only=Sistema de puntuación baja, el primero con tres victorias en medallas es el ganador; desempate A8.2 (última prueba)
scoringSchemeLowPointSystemFirstThreeWinsA82OnlyDescription=Sistema de puntuación baja. El primero de la serie de medallas en ganar tres pruebas gana la serie de medallas. Se puede utilizar una columna de arrastre en la serie de medallas para modelar las victorias arrastradas. El desempate en la serie inicial se basa en A8.2 (última prueba, luego la penúltima, etc.).
@@ -2457,3 +2457,7 @@ otherTieBreakingLeaderboard=Autre palmarès pour départager les égalités
scoringSchemeHighPointsByWindTiesLastlyBrokenByOtherLeaderboard=Système de points avec bonus, Match racing, égalité départagée en fonction d''un autre palmarès
scoringSchemeHighPointsByWindTiesLastlyBrokenByOtherLeaderboardDescription=Le vainqueur d''une course obtient 1 point, tous les autres participants 0 ; en cas d''égalité, l''égalité est départagée en fonction du résultat de la dernière course, puis en fonction d''un autre palmarès.
helptextLinkingRaces=Pour relier des courses à des courses suivies, vous devez sélectionner une course (clic gauche) dans la table ci-dessous puis sélectionner l''entrée correspondante (clic gauche) dans la table des courses suivies sur la droite.
scoringSchemeLowPointA82Only=Système de points a minima ; égalité départagée selon RRS A8.2 (dernière course)
scoringSchemeLowPointA82OnlyDescription=Système de points a minima ; égalité départagée selon RRS A8.2 ; Si l''égalité subsiste après avoir pris en compte la participation et les scores obtenus à la course médaillée, comparez les scores de la dernière course (y compris les scores exclus), puis de l''avant-dernière course, etc., jusqu''à pouvoir départager l''égalité.=scoringSchemeLowPointSystemFirstThreeWinsA82Only
scoringSchemeLowPointSystemFirstThreeWinsA82Only=Système de points a minima ; le premier ayant gagné trois médailles gagne la compétition ; égalité départagé selon A8.2 (dernière course)
scoringSchemeLowPointSystemFirstThreeWinsA82OnlyDescription=Système de points a minima. Le premier ayant gagné trois courses gagne la série médaillée. Une colonne de report dans la série médaillée peut être utilisée pour modéliser un report de victoires. Égalité dans la première série départagée selon A8.2 (dernière course, puis avant-dernière, etc.).
@@ -2457,3 +2457,7 @@ otherTieBreakingLeaderboard=Altra classifica di spareggio
scoringSchemeHighPointsByWindTiesLastlyBrokenByOtherLeaderboard=Sistema di punteggio massimo, match race, spareggio basato su altra classifica
scoringSchemeHighPointsByWindTiesLastlyBrokenByOtherLeaderboardDescription=Il vincitore della gara ottiene 1 punto, tutti gli altri 0; i pareggi vengono risolti nell''ultima gara, quindi in base a un''altra classifica
helptextLinkingRaces=Per collegare le gare a quelle tracciate, è necessario fare clic con il pulsante sinistro del mouse su una gara nella tabella sottostante e, successivamente, fare clic con il pulsante sinistro del mouse sulla voce corrispondente nella tabella delle gare tracciate a destra.
scoringSchemeLowPointA82Only=Sistema di punteggio minimo. Spareggio basato su RRS A8.2 (ultima gara)
scoringSchemeLowPointA82OnlyDescription=Sistema di punteggio minimo. Spareggio basato su RRS A8.2; se dopo l''esame della partecipazione e dei punteggi della Medal Race permane un pareggio, confrontare i punteggi dell''ultima gara (compresi i punteggi esclusi), quindi i punteggi della penultima gara, e così via, fino a che non si risolve il pareggio.=scoringSchemeLowPointSystemFirstThreeWinsA82Only
scoringSchemeLowPointSystemFirstThreeWinsA82Only=Sistema di punteggio minimo; il primo a conseguire tre vittorie da medaglia è il vincitore; spareggio A8.2 (ultima gara)
scoringSchemeLowPointSystemFirstThreeWinsA82OnlyDescription=Sistema di punteggio minimo. Il primo nella Medal Series a conseguire tre vittorie vince la Medal Series. Una colonna di riporto nella Medal Series può essere utilizzata per modellare le vittorie riportate. Lo spareggio nella serie iniziale si basa su A8.2 (ultima gara, quindi penultima, e così via).
@@ -2457,3 +2457,7 @@ otherTieBreakingLeaderboard=タイブレークのための別リーダーボー
scoringSchemeHighPointsByWindTiesLastlyBrokenByOtherLeaderboard=高得点方式、マッチレース、別リーダーボードに基づくタイブレーク
scoringSchemeHighPointsByWindTiesLastlyBrokenByOtherLeaderboardDescription=レースの勝者が 1 点を獲得し、その他はすべて 0 点。同順位はまず最終レース、次に別リーダーボードに基づいてタイブレーク
helptextLinkingRaces=レースを追跡レースにリンクするためには、下の表でレースを左クリックしてから、右側の追跡レース表で該当するエントリを左クリックする必要があります。
scoringSchemeLowPointA82Only=低得点方式、RRS A8.2 によるタイブレーク (最終レース)
scoringSchemeLowPointA82OnlyDescription=低得点方式、RRS A8.2 によるタイブレーク。メダルレースに参加しての得点を見ても同順位のままである場合は、最終レースの得点 (除外された得点を含む) を比較し、それでも同順位ならば最後から 2 番目のレースを比較するといったように、均衡が破れるまでこれを続けます。=scoringSchemeLowPointSystemFirstThreeWinsA82Only
scoringSchemeLowPointSystemFirstThreeWinsA82Only=低得点方式、メダルシリーズで最初に 3 勝した者が勝者、A8.2 タイブレーク (最終レース)
scoringSchemeLowPointSystemFirstThreeWinsA82OnlyDescription=低得点方式。メダルシリーズで最初に 3 レースに勝利した者をメダルシリーズの勝者とします。持ち越し列をメダルシリーズに使用して、持ち越される勝利のモデル化を行うことができます。オープニングシリーズでのタイブレークは A8.2 に基づきます (最終レース、次に最後から 2 番目のレース、など)。
@@ -2457,3 +2457,7 @@ otherTieBreakingLeaderboard=Outro painel de classificação de tie-break
scoringSchemeHighPointsByWindTiesLastlyBrokenByOtherLeaderboard=Sistema de bônus, "Match Racing", tie-break com base noutro painel de classificação
scoringSchemeHighPointsByWindTiesLastlyBrokenByOtherLeaderboardDescription=O vencedor de uma corrida obtém 1 ponto, todos os outros 0; os desempates são efetuados com base na última corrida, depois com base noutro painel de classificação
helptextLinkingRaces=Para vincular corridas a corridas rastreadas, deve clicar com o botão esquerdo em uma corrida na tabela embaixo e depois disso clicar com o botão esquerdo na entrada correspondente na tabela de corridas rastreadas à direita.
scoringSchemeLowPointA82Only=Sistema linear; tie-break de acordo com RRS A8.2 (última corrida)
scoringSchemeLowPointA82OnlyDescription=Sistema linear; tie-break de acordo com RRS A8.2; se continuar existindo um empate depois de consultar a participação e as pontuações da corrida para medalha, compare as pontuações na última corrida (incluindo pontuações excluídas), depois a penúltima corrida, etc., até chegar ao desempate.=scoringSchemeLowPointSystemFirstThreeWinsA82Only
scoringSchemeLowPointSystemFirstThreeWinsA82Only=Sistema linear, o primeiro com três vitórias em medalhas é o vencedor; A8.2 tie-break (última corrida)
scoringSchemeLowPointSystemFirstThreeWinsA82OnlyDescription=Sistema linear. O primeiro na série para medalha a vencer três corridas vence a série para medalha. É possível utilizar uma coluna de transferência na série para medalha para modelar vitórias transferidas. Um tie-break na série de abertura é baseado no A8.2 (última corrida, depois a penúltima, etc.).
@@ -2455,5 +2455,9 @@ failedFetchingSelfServicePortalSession=Не удалось открыть пор
oneAlwaysStaysOne=Балл 1 остается 1 независимо от коэффициента столбца
otherTieBreakingLeaderboard=Другая таблица лидеров с равенством
scoringSchemeHighPointsByWindTiesLastlyBrokenByOtherLeaderboard=Система высоких баллов, матчевая гонка, равенство на основе другой таблицы лидеров
scoringSchemeHighPointsByWindTiesLastlyBrokenByOtherLeaderboardDescription=Победитель гонки получает 1 балл, остальные 0; при равенстве баллов победитель определяется в последней гонке, а затем на основе другой таблица лидеров
scoringSchemeHighPointsByWindTiesLastlyBrokenByOtherLeaderboardDescription=Победитель гонки получает 1 балл, остальные 0; при равенстве баллов победитель определяется в последней гонке, а затем на основе другой таблицы лидеров
helptextLinkingRaces=Чтобы соединить гонки с отслеживаемыми гонками, щелкните левой клавишей мыши гонку в таблице и после этого щелкните левой клавишей мыши соответствующую запись в таблице отслеживаемых гонок справа.
scoringSchemeLowPointA82Only=Система низких баллов; разрешение равенства по правилу парусных гонок A8.2 (последняя гонка)
scoringSchemeLowPointA82OnlyDescription=Система низких баллов; разрешение равенства по правилу парусных гонок A8.2. Если остаются равные очки у двух или более яхт, то они должны быть расположены в порядке их очков в последней гонке (включая исключенные); если все еще остаются равные очки, то преимущество должно быть отдано с использованием очков за предпоследнюю гонку и т.д. до тех пор, пока все равенства не будут разрешены.=scoringSchemeLowPointSystemFirstThreeWinsA82Only
scoringSchemeLowPointSystemFirstThreeWinsA82Only=Система низких баллов, первый с тремя победами в гонках на медали является победителем; разрешение равенства по правилу A8.2 (последняя гонка)
scoringSchemeLowPointSystemFirstThreeWinsA82OnlyDescription=Система низких баллов. Первый с тремя победами в гонках на медали, выигрывает серию на медали. Столбец для переноса в серии на медали можно использовать для переноса побед в гонках на медали. Разрешение равенства в открывающих сериях основано на правиле A8.2 (последняя гонка, предпоследняя гонка и т.д.).
@@ -2457,3 +2457,7 @@ otherTieBreakingLeaderboard=Druga lestvica vodilnih z neodločenim rezultatom
scoringSchemeHighPointsByWindTiesLastlyBrokenByOtherLeaderboard=Napredno točkovanje, regatni dvoboj, neodločen rezultat na osnovi druge lestvice vodilnih
scoringSchemeHighPointsByWindTiesLastlyBrokenByOtherLeaderboardDescription=Zmagovalec plova prejme 1 točko, vsi ostali 0 točk; v primeru neodločenega rezultata odloča zadnji plov, nato pa na podlagi druge lestvice vodilnih
helptextLinkingRaces=Če želite plove povezati s sledenimi plovi, morate z levim gumbom miške klikniti plov v spodnji tabeli in nato ustrezen vnos v tabeli sledenih plovov na desni.
scoringSchemeLowPointA82Only=Enostavno točkovanje; neodločen rezultat glede na RRS A8.2 (zadnji plov)
scoringSchemeLowPointA82OnlyDescription=Enostavno točkovanje; neodločen rezultat glede na RRS A8.2; Če po pregledu udeležbe plova za kolajne in točk rezultat ostaja neodločen, se primerja točke zadnjega plova (vključno z odbitimi točkami), nato predzadnjega plova itn., dokler rezultat ni več neodločen.=scoringSchemeLowPointSystemFirstThreeWinsA82Only
scoringSchemeLowPointSystemFirstThreeWinsA82Only=Enostavno točkovanje; prvi s tremi zmagami za kolajne je zmagovalec; A8.2 neodločen rezultat (zadnji plov)
scoringSchemeLowPointSystemFirstThreeWinsA82OnlyDescription=Enostavno točkovanje; prvi v seriji za kolajne, ki zmaga na treh plovih, je zmagovalec serije za kolajne. Stolpec za prenos v seriji za kolajne je mogoče uporabiti za prikaz prenesenih zmag. Neodločen rezultat v začetni seriji temelji na A8.2 (zadnji plov, nato predzadnji plov itn.).
@@ -2457,3 +2457,7 @@ otherTieBreakingLeaderboard=其他打破平局的积分榜
scoringSchemeHighPointsByWindTiesLastlyBrokenByOtherLeaderboard=高分系统,对抗赛,基于其他积分榜的决胜局
scoringSchemeHighPointsByWindTiesLastlyBrokenByOtherLeaderboardDescription=比赛轮次的获胜者得 1 分,所有其他人得 0 分;在最后一个比赛轮次中打破平局,然后根据其他积分榜打破平局
helptextLinkingRaces=为了在比赛轮次之间建立链接,您必须左键点击下表中的比赛轮次,然后左键点击右侧跟踪比赛轮次表中的相应条目。
scoringSchemeLowPointA82Only=低分系统;基于 RRS A8.2 的决胜局(最后一个比赛轮次)
scoringSchemeLowPointA82OnlyDescription=低分系统;基于 RRS A8.2 的决胜局;如果在查看奖牌轮参与情况和得分后仍保持平局,则比较最后一个比赛轮次的得分(包括排除的得分),然后是倒数第二场比赛等,直到打破平局。=scoringSchemeLowPointSystemFirstThreeWinsA82Only
scoringSchemeLowPointSystemFirstThreeWinsA82Only=低分系统,奖牌轮中取得三场胜利的第一名为获胜者;A8.2 决胜局(最后一个比赛轮次)
scoringSchemeLowPointSystemFirstThreeWinsA82OnlyDescription=低分系统。在奖牌轮系列中,在三个比赛轮次中获胜的第一名在奖牌轮系列中获胜。奖牌轮系列中的得分列可用于为累计取得的胜利建模。开场系列赛的决胜局基于 A8.2(最后一个比赛轮次,然后是倒数第二场,依此类推)。
@@ -53,7 +53,8 @@ public class BoatClassVectorGraphicsResolver {
BoatClassMasterdata.SPAEKHUGGER, BoatClassMasterdata.SCAN_KAP_99,
BoatClassMasterdata.BB10M, BoatClassMasterdata.WAYFARER, BoatClassMasterdata.X_332,
BoatClassMasterdata.BRASSFAHRT_I, BoatClassMasterdata.BRASSFAHRT_II, BoatClassMasterdata.BRASSFAHRT_III,
BoatClassMasterdata.BRASSFAHRT_IV, BoatClassMasterdata.BRASSFAHRT_V);
BoatClassMasterdata.BRASSFAHRT_IV, BoatClassMasterdata.BRASSFAHRT_V,
BoatClassMasterdata.VO60, BoatClassMasterdata.VO65, BoatClassMasterdata.IMOCA);
BoatClassVectorGraphics dinghyWithSpinnaker = new DinghyWithSpinnakerVectorGraphics(BoatClassMasterdata._420,
BoatClassMasterdata._470, BoatClassMasterdata._5O5, BoatClassMasterdata.CADET, BoatClassMasterdata.FLYING_DUTCHMAN,
BoatClassMasterdata.FOLKBOAT, BoatClassMasterdata.DYAS, BoatClassMasterdata.DRAGON_INT,
@@ -2803,26 +2803,28 @@ public class RaceMap extends AbstractCompositeComponent<RaceMapSettings> impleme
.get(fixesAndTails.getFirstShownFix(competitorDTO)).timepoint);
final TimePoint to = new MillisecondsTimePoint(getBoatFix(competitorDTO, timer.getTime()).timepoint);
timeRange.put(competitorDTO, new TimeRangeImpl(from, to, true));
sailingService.getDouglasPoints(race, timeRange, 3,
new AsyncCallback<Map<CompetitorDTO, List<GPSFixDTOWithSpeedWindTackAndLegType>>>() {
@Override
public void onFailure(Throwable caught) {
errorReporter.reportError("Error obtaining douglas positions: " + caught.getMessage(), true /*silentMode */);
}
@Override
public void onSuccess(Map<CompetitorDTO, List<GPSFixDTOWithSpeedWindTackAndLegType>> result) {
lastDouglasPeuckerResult = result;
if (douglasMarkers != null) {
removeAllMarkDouglasPeuckerpoints();
if (settings.isShowDouglasPeuckerPoints()) {
sailingService.getDouglasPoints(race, timeRange, 3,
new AsyncCallback<Map<CompetitorDTO, List<GPSFixDTOWithSpeedWindTackAndLegType>>>() {
@Override
public void onFailure(Throwable caught) {
errorReporter.reportError("Error obtaining douglas positions: " + caught.getMessage(), true /*silentMode */);
}
if (!(timer.getPlayState() == PlayStates.Playing)) {
if (settings.isShowDouglasPeuckerPoints()) {
showMarkDouglasPeuckerPoints(result);
@Override
public void onSuccess(Map<CompetitorDTO, List<GPSFixDTOWithSpeedWindTackAndLegType>> result) {
lastDouglasPeuckerResult = result;
if (douglasMarkers != null) {
removeAllMarkDouglasPeuckerpoints();
}
if (!(timer.getPlayState() == PlayStates.Playing)) {
if (settings.isShowDouglasPeuckerPoints()) {
showMarkDouglasPeuckerPoints(result);
}
}
}
}
});
});
}
maneuverMarkersAndLossIndicators.getAndShowManeuvers(race, timeRange);
}
}
@@ -52,6 +52,10 @@ public class ScoringSchemeTypeFormatter {
return stringMessages.scoringSchemeLowPointSystemFirstThreeWins();
case HIGH_POINT_BY_WINS_TIES_LASTLY_BROKEN_BY_OTHER_LEADERBOARD:
return stringMessages.scoringSchemeHighPointsByWindTiesLastlyBrokenByOtherLeaderboard();
case LOW_POINT_A82_ONLY:
return stringMessages.scoringSchemeLowPointA82Only();
case LOW_POINT_FIRST_TO_WIN_THREE_RACES_A82_ONLY:
return stringMessages.scoringSchemeLowPointSystemFirstThreeWinsA82Only();
}
return null;
}
@@ -84,6 +88,10 @@ public class ScoringSchemeTypeFormatter {
return stringMessages.scoringSchemeLowPointSystemFirstThreeWinsDescription();
case HIGH_POINT_BY_WINS_TIES_LASTLY_BROKEN_BY_OTHER_LEADERBOARD:
return stringMessages.scoringSchemeHighPointsByWindTiesLastlyBrokenByOtherLeaderboardDescription();
case LOW_POINT_A82_ONLY:
return stringMessages.scoringSchemeLowPointA82OnlyDescription();
case LOW_POINT_FIRST_TO_WIN_THREE_RACES_A82_ONLY:
return stringMessages.scoringSchemeLowPointSystemFirstThreeWinsA82OnlyDescription();
default:
return format(scoringSchemeType, stringMessages);
}
@@ -49,6 +49,8 @@ import com.sap.sailing.domain.common.media.MediaTrackWithSecurityDTO;
import com.sap.sailing.domain.common.security.SecuredDomainType.TrackedRaceActions;
import com.sap.sailing.gwt.common.authentication.SailingAuthenticationEntryPointLinkFactory;
import com.sap.sailing.gwt.common.client.NavigatorUtil;
import com.sap.sailing.gwt.common.client.help.HelpButton;
import com.sap.sailing.gwt.common.client.help.HelpButtonResources;
import com.sap.sailing.gwt.settings.client.leaderboard.SingleRaceLeaderboardSettings;
import com.sap.sailing.gwt.settings.client.raceboard.RaceBoardPerspectiveOwnSettings;
import com.sap.sailing.gwt.settings.client.raceboard.RaceboardContextDefinition;
@@ -117,6 +119,7 @@ import com.sap.sse.common.settings.Settings;
import com.sap.sse.gwt.client.ErrorReporter;
import com.sap.sse.gwt.client.async.AsyncActionsExecutor;
import com.sap.sse.gwt.client.controls.slider.TimeSlider.BarOverlay;
import com.sap.sse.gwt.client.formfactor.DeviceDetector;
import com.sap.sse.gwt.client.panels.ResizableFlowPanel;
import com.sap.sse.gwt.client.player.TimeRangeWithZoomModel;
import com.sap.sse.gwt.client.player.Timer;
@@ -802,6 +805,14 @@ public class RaceBoardPanel
final Label raceTimeLabel = computeRaceInformation(raceColumn, fleet);
raceTimeLabel.setStyleName("RaceTime-Label");
regattaAndRaceTimeInformationHeader.clear();
final FlowPanel helpButtonPanel = new FlowPanel();
final HelpButton helpButton = new HelpButton(HelpButtonResources.INSTANCE,
stringMessages.videoGuide(), "https://support.sapsailing.com/hc/en-us/articles/7275243525148-Tracking-Race-Player-Overview");
if (!DeviceDetector.isMobile()) {
helpButtonPanel.add(helpButton);
helpButtonPanel.setStyleName("HelpButton");
regattaAndRaceTimeInformationHeader.add(helpButtonPanel);
}
regattaAndRaceTimeInformationHeader.add(regattaNameAnchor);
regattaAndRaceTimeInformationHeader.add(raceTimeLabel);
final DataByLogo dataByLogo = new DataByLogo();
@@ -947,5 +958,6 @@ public class RaceBoardPanel
public String getId() {
return RaceBoardPerspectiveLifecycle.ID;
}
}
@@ -1062,8 +1062,7 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet
if (abortingFlagEvent != null) {
raceInfoDTO.isRaceAbortedInPassBefore = true;
raceInfoDTO.abortingTimeInPassBefore = abortingFlagEvent.getLogicalTimePoint().asDate();
if (raceInfoDTO.lastStatus == RaceLogRaceStatus.UNSCHEDULED || raceInfoDTO.lastStatus == RaceLogRaceStatus.PRESCHEDULED) {
if (raceInfoDTO.lastStatus.isAbortingFlagFromPreviousPassValid()) {
raceInfoDTO.lastUpperFlag = abortingFlagEvent.getUpperFlag();
raceInfoDTO.lastLowerFlag = abortingFlagEvent.getLowerFlag();
raceInfoDTO.lastFlagsAreDisplayed = abortingFlagEvent.isDisplayed();
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 784 B

After

Width:  |  Height:  |  Size: 1.1 KiB

@@ -128,6 +128,7 @@ public class LandscapeManagementPanel extends SimplePanel {
//Spacing for Setupbar (AWS/SSH/REGION)
private static final int DEFAULT_SETUPBAR_HEIGHT = 300;
private static final int DEFAULT_AMOUNT_SSHKEYS_PER_PAGE = 3;
public LandscapeManagementPanel(StringMessages stringMessages, UserService userService,
AdminConsoleTableResources tableResources, ErrorReporter errorReporter) {
@@ -155,7 +156,7 @@ public class LandscapeManagementPanel extends SimplePanel {
}
};
sshKeyManagementPanel = new SshKeyManagementPanel(stringMessages, userService,
landscapeManagementService, tableResources, errorReporter, /* access key provider */ mfaLoginWidget, regionsTable.getSelectionModel());
landscapeManagementService, tableResources, errorReporter, /* access key provider */ mfaLoginWidget, regionsTable.getSelectionModel(), DEFAULT_AMOUNT_SSHKEYS_PER_PAGE);
final CaptionPanel sshKeysCaptionPanel = new CaptionPanel(stringMessages.sshKeys());
sshKeysCaptionPanel.setHeight("" + DEFAULT_SETUPBAR_HEIGHT + "px");
regionsTable.addColumn(new TextColumn<String>() {
@@ -55,7 +55,7 @@ public class SshKeyManagementPanel extends VerticalPanel {
public SshKeyManagementPanel(StringMessages stringMessages, UserService userService,
LandscapeManagementWriteServiceAsync landscapeManagementService, AdminConsoleTableResources tableResources,
ErrorReporter errorReporter, AwsAccessKeyProvider awsAccessKeyProvider,
RefreshableSingleSelectionModel<String> regionSelectionModel) {
RefreshableSingleSelectionModel<String> regionSelectionModel, int countKeysPerPage) {
this.regionSelectionModel = regionSelectionModel;
this.landscapeManagementService = landscapeManagementService;
this.errorReporter = errorReporter;
@@ -92,6 +92,7 @@ public class SshKeyManagementPanel extends VerticalPanel {
return Arrays.asList(t.getName(), t.getRegionId(), t.getCreatorName());
}
};
sshKeyTable.getTable().setPageSize(countKeysPerPage);
sshKeyTable.addColumn(object->object.getRegionId(), stringMessages.region());
sshKeyTable.addColumn(object->object.getName(), stringMessages.name());
sshKeyTable.addColumn(object->object.getCreatorName(), stringMessages.creator());
@@ -152,3 +152,4 @@ sameAsExistingHost=Stejně jako existující hostitel
moveTheFollowingMasterAndReplicaProcessesAway=Přesunování hlavních procesů [{1}] a procesů replik [{2}] z hostitele s ID {0} do nového hostitele.
processOfReplicaSet=na portu {0} (aplikační sada replik {1})
successfullyMovedAllProcessesAwayFromHost=Všechny procesy byly úspěšně přesunuty z hostitele {0}
dnsNameAlreadyInUse=Název DNS se již používá
@@ -152,3 +152,4 @@ sameAsExistingHost=Samme som eksisterende vært
moveTheFollowingMasterAndReplicaProcessesAway=Flytter overordnede processer [{1}] og replikaprocesser [{2}] fra vært med ID {0} til en ny vært.
processOfReplicaSet=på port {0} (applikationsreplikasæt {1})
successfullyMovedAllProcessesAwayFromHost=Alle processer blev flyttet væk fra vært {0} uden fejl
dnsNameAlreadyInUse=DNS-navn er allerede i brug
@@ -152,3 +152,4 @@ sameAsExistingHost=Igual que el host existente
moveTheFollowingMasterAndReplicaProcessesAway=Movimiento de procesos principales [{1}] y procesos de réplica [{2}] del host con ID {0} a un host nuevo.
processOfReplicaSet=en el puerto {0} (conjunto de réplicas de aplicación {1})
successfullyMovedAllProcessesAwayFromHost=Todos los procesos movidos correctamente fuera del host {0}
dnsNameAlreadyInUse=Nombre de DNS ya en uso
@@ -152,3 +152,4 @@ sameAsExistingHost=Identique à l''hôte existant
moveTheFollowingMasterAndReplicaProcessesAway=Déplacement des processus principaux [{1}] et des processus de réplicas [{2}) de l''hôte avec l''ID {0} vers un nouvel hôte.
processOfReplicaSet=sur le port {0} (ensemble de réplicas d''application {1})
successfullyMovedAllProcessesAwayFromHost=Processus correctement déplacés depuis l''hôte {0}
dnsNameAlreadyInUse=Nom DNS déjà utilisé
@@ -152,3 +152,4 @@ sameAsExistingHost=Uguale all''host esistente
moveTheFollowingMasterAndReplicaProcessesAway=Spostamento in corso dei processi principali [{1}] e dei processi di replica [{2}] dall''host con ID {0} a un nuovo host.
processOfReplicaSet=sulla porta {0} (set di replica dell''applicazione {1})
successfullyMovedAllProcessesAwayFromHost=Spostamento di tutti i processi dall''host {0} avvenuto correttamente
dnsNameAlreadyInUse=Nome DNS già in uso
@@ -152,3 +152,4 @@ sameAsExistingHost=既存ホストと同じ
moveTheFollowingMasterAndReplicaProcessesAway=マスタプロセス [{1}] および複製プロセス [{2}] を ID {0} のホストから新しいホストに移動。
processOfReplicaSet=ポート {0} (アプリケーション複製セット {1}) 上
successfullyMovedAllProcessesAwayFromHost=すべてのプロセスがホスト {0} から移動しました
dnsNameAlreadyInUse=DNS 名はすでに使用中です
@@ -152,3 +152,4 @@ sameAsExistingHost=Igual ao host existente
moveTheFollowingMasterAndReplicaProcessesAway=Movendo processos mestre [{1}] e processos replicados [{2}] do host com o ID {0} para um novo host.
processOfReplicaSet=na porta {0} (conjunto de réplicas de aplicação {1})
successfullyMovedAllProcessesAwayFromHost=Todos os processos movidos com êxito do host {0}
dnsNameAlreadyInUse=Nome de DNS já em utilização
@@ -152,3 +152,4 @@ sameAsExistingHost=Совпадает с существующим хостом
moveTheFollowingMasterAndReplicaProcessesAway=Перемещение основных процессов [{1}] и процессов реплик [{2}] с хоста с ид. {0} но новый хост.
processOfReplicaSet=на порт {0} (набор реплик приложений {1})
successfullyMovedAllProcessesAwayFromHost=Все процессы успешно перемещены с хоста {0}
dnsNameAlreadyInUse=Имя DNS уже используется
@@ -152,3 +152,4 @@ sameAsExistingHost=Enako kot obstoječi gostitelj
moveTheFollowingMasterAndReplicaProcessesAway=Premik glavnih procesov [{1}] in procesov replike [{2}] iz gostitelja z ID-jem {0} v novega gostitelja.
processOfReplicaSet=vrata {0} (niz replik aplikacije {1})
successfullyMovedAllProcessesAwayFromHost=Vsi procesi uspešno premaknjeni iz gostitelja {0}
dnsNameAlreadyInUse=Ime DNS je že v uporabi
@@ -152,3 +152,4 @@ sameAsExistingHost=与现有主机相同
moveTheFollowingMasterAndReplicaProcessesAway=将主流程 [{1}] 和复本流程 [{2}] 从具有编号 {0} 的主机移动到新主机。
processOfReplicaSet=在端口 {0} 上(应用程序复本集 {1})
successfullyMovedAllProcessesAwayFromHost=已成功将所有流程移离主机 {0}
dnsNameAlreadyInUse=DNS 名称已在使用中
@@ -703,6 +703,7 @@ public class LandscapeManagementWriteServiceImpl extends ResultCachingProxiedRem
.setProcess(master)
.setHostname(masterHostname)
.setTargetGroupNamePrefix(LandscapeService.SAILING_TARGET_GROUP_NAME_PREFIX)
.setSecurityGroupForVpc(getLandscape().getDefaultSecurityGroupForApplicationHosts(new AwsRegion(regionId, getLandscape())))
.setLandscape(getLandscape())
.build();
createLoadBalancerMapping.run();
@@ -862,6 +862,7 @@ public class LandscapeServiceImpl implements LandscapeService {
.setProcess(master)
.setHostname(masterHostname)
.setTargetGroupNamePrefix(SAILING_TARGET_GROUP_NAME_PREFIX)
.setSecurityGroupForVpc(landscape.getDefaultSecurityGroupForApplicationHosts(region))
.setLandscape(landscape)
.build();
createLoadBalancerMapping.run();
@@ -1724,7 +1725,7 @@ public class LandscapeServiceImpl implements LandscapeService {
.setRegion(region)
.setPathPrefixForShardingKey(RemoteServiceMappingConstants.pathPrefixForShardingKey)
.setShardingKeys(shardingKeys)
.setReplicaset(applicationReplicaSet)
.setReplicaSet(applicationReplicaSet)
.setShardName(shardName)
.build()
.run();
@@ -1746,7 +1747,7 @@ public class LandscapeServiceImpl implements LandscapeService {
.setRegion(region)
.setPathPrefixForShardingKey(RemoteServiceMappingConstants.pathPrefixForShardingKey)
.setShardingKeys(shardingkeys)
.setReplicaset(applicationReplicaSet)
.setReplicaSet(applicationReplicaSet)
.setShardName(shardName)
.build()
.run();
@@ -1779,7 +1780,7 @@ public class LandscapeServiceImpl implements LandscapeService {
.setLandscape(getLandscape())
.setTargetGroupNamePrefix(LandscapeService.SAILING_TARGET_GROUP_NAME_PREFIX)
.setShardingKeys(shardingkeys)
.setReplicaset(applicationReplicaSet)
.setReplicaSet(applicationReplicaSet)
.setRegion(region)
.setPathPrefixForShardingKey(RemoteServiceMappingConstants.pathPrefixForShardingKey)
.setShardName(shardName)
@@ -95,10 +95,10 @@ public class IncrementalAnyOrderLeastSquaresImpl implements IncrementalLeastSqua
}
public IncrementalAnyOrderLeastSquaresImpl(int polynomialOrder, boolean hasIntercept,
boolean useSymbollicInversionIfPossible) {
boolean useSymbolicInversionIfPossible) {
this.hasIntercept = hasIntercept;
this.polynomialOrder = polynomialOrder;
this.useSymbollicInversionIfPossible = useSymbollicInversionIfPossible;
this.useSymbollicInversionIfPossible = useSymbolicInversionIfPossible;
if (hasIntercept) {
matrixOfXSums = new double[polynomialOrder + 1][polynomialOrder + 1];
vectorOfXYMultSums = new double[polynomialOrder + 1];
@@ -9,7 +9,7 @@ import com.sap.sailing.selenium.pages.datamining.DataMiningPage;
import com.sap.sailing.selenium.pages.datamining.DataMiningPanelPO;
import com.sap.sailing.selenium.test.AbstractSeleniumTest;
public class SmokeTestDataminingEntrypoint extends AbstractSeleniumTest {
public class DataminingEntrypointSmokeTest extends AbstractSeleniumTest {
@Override
@Before
public void setUp() {
@@ -38,6 +38,7 @@ import com.sap.sailing.server.gateway.serialization.racelog.impl.RaceLogPathfind
import com.sap.sailing.server.gateway.serialization.racelog.impl.RaceLogProtestStartTimeEventSerializer;
import com.sap.sailing.server.gateway.serialization.racelog.impl.RaceLogRaceStatusEventSerializer;
import com.sap.sailing.server.gateway.serialization.racelog.impl.RaceLogRegisterCompetitorEventSerializer;
import com.sap.sailing.server.gateway.serialization.racelog.impl.RaceLogResultsAreOfficialEventSerializer;
import com.sap.sailing.server.gateway.serialization.racelog.impl.RaceLogRevokeEventSerializer;
import com.sap.sailing.server.gateway.serialization.racelog.impl.RaceLogStartOfTrackingEventSerializer;
import com.sap.sailing.server.gateway.serialization.racelog.impl.RaceLogStartProcedureChangedEventSerializer;
@@ -97,7 +98,8 @@ public class RaceLogEventDeserializer implements JsonDeserializer<RaceLogEvent>
new RaceLogORCScratchBoatEventDeserializer(competitorDeserializer),
new RaceLogORCCertificateAssignmentEventDeserializer(competitorDeserializer),
new RaceLogORCImpliedWindSourceEventDeserializer(competitorDeserializer),
new RaceLogExcludeWindSourceEventDeserializer(competitorDeserializer));
new RaceLogExcludeWindSourceEventDeserializer(competitorDeserializer),
new RaceLogResultsAreOfficialEventDeserializer(competitorDeserializer));
}
protected final JsonDeserializer<RaceLogEvent> flagEventDeserializer;
@@ -129,6 +131,7 @@ public class RaceLogEventDeserializer implements JsonDeserializer<RaceLogEvent>
protected final JsonDeserializer<RaceLogEvent> orcCertificateAssignmentEventDeserializer;
protected final JsonDeserializer<RaceLogEvent> orcSetImpliedWindEventDeserializer;
protected final JsonDeserializer<RaceLogEvent> excludeWindSourceEventSerializer;
protected final JsonDeserializer<RaceLogEvent> resultsAreOfficialEventDeserializer;
public RaceLogEventDeserializer(JsonDeserializer<RaceLogEvent> flagEventDeserializer,
JsonDeserializer<RaceLogEvent> startTimeEventDeserializer,
@@ -158,7 +161,8 @@ public class RaceLogEventDeserializer implements JsonDeserializer<RaceLogEvent>
JsonDeserializer<RaceLogEvent> orcScratchBoatEventDeserializer,
JsonDeserializer<RaceLogEvent> orcCertificateAssignmentEventDeserializer,
JsonDeserializer<RaceLogEvent> orcSetImpliedWindEventDeserializer,
JsonDeserializer<RaceLogEvent> excludeWindSourceEventSerializer) {
JsonDeserializer<RaceLogEvent> excludeWindSourceEventSerializer,
JsonDeserializer<RaceLogEvent> resultsAreOfficialEventDeserializer) {
this.flagEventDeserializer = flagEventDeserializer;
this.startTimeEventDeserializer = startTimeEventDeserializer;
this.dependentStartTimeEventDeserializer = dependentStartTimeEventDeserializer;
@@ -188,6 +192,7 @@ public class RaceLogEventDeserializer implements JsonDeserializer<RaceLogEvent>
this.orcCertificateAssignmentEventDeserializer = orcCertificateAssignmentEventDeserializer;
this.orcSetImpliedWindEventDeserializer = orcSetImpliedWindEventDeserializer;
this.excludeWindSourceEventSerializer = excludeWindSourceEventSerializer;
this.resultsAreOfficialEventDeserializer = resultsAreOfficialEventDeserializer;
}
protected JsonDeserializer<RaceLogEvent> getDeserializer(JSONObject object) throws JsonDeserializationException {
@@ -251,6 +256,8 @@ public class RaceLogEventDeserializer implements JsonDeserializer<RaceLogEvent>
return orcSetImpliedWindEventDeserializer;
} else if (type.equals(RaceLogExcludeWindSourceEventSerializer.VALUE_CLASS)) {
return excludeWindSourceEventSerializer;
} else if (type.equals(RaceLogResultsAreOfficialEventSerializer.VALUE_CLASS)) {
return resultsAreOfficialEventDeserializer;
}
throw new JsonDeserializationException(String.format("There is no deserializer defined for event type %s.",
type));
@@ -0,0 +1,34 @@
package com.sap.sailing.server.gateway.deserialization.racelog.impl;
import java.io.Serializable;
import java.util.List;
import org.json.simple.JSONObject;
import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor;
import com.sap.sailing.domain.abstractlog.race.RaceLogEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogResultsAreOfficialEvent;
import com.sap.sailing.domain.abstractlog.race.impl.RaceLogResultsAreOfficialEventImpl;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.impl.DynamicCompetitor;
import com.sap.sse.common.TimePoint;
import com.sap.sse.shared.json.JsonDeserializationException;
import com.sap.sse.shared.json.JsonDeserializer;
/**
* Deserializer for {@link RaceLogResultsAreOfficialEvent}.
*
* @author Axel Uhl (d043530)
*/
public class RaceLogResultsAreOfficialEventDeserializer extends BaseRaceLogEventDeserializer {
public RaceLogResultsAreOfficialEventDeserializer(JsonDeserializer<DynamicCompetitor> competitorDeserializer) {
super(competitorDeserializer);
}
@Override
protected RaceLogEvent deserialize(JSONObject object, Serializable id, TimePoint createdAt,
AbstractLogEventAuthor author, TimePoint timePoint, int passId, List<Competitor> competitors)
throws JsonDeserializationException {
return new RaceLogResultsAreOfficialEventImpl(createdAt, timePoint, author, id, passId);
}
}
@@ -49,6 +49,7 @@ import com.sap.sailing.server.gateway.deserialization.racelog.impl.RaceLogFlagEv
import com.sap.sailing.server.gateway.deserialization.racelog.impl.RaceLogORCCertificateAssignmentEventDeserializer;
import com.sap.sailing.server.gateway.deserialization.racelog.impl.RaceLogORCImpliedWindSourceEventDeserializer;
import com.sap.sailing.server.gateway.deserialization.racelog.impl.RaceLogORCLegDataEventDeserializer;
import com.sap.sailing.server.gateway.deserialization.racelog.impl.RaceLogResultsAreOfficialEventDeserializer;
import com.sap.sailing.server.gateway.deserialization.racelog.impl.RaceLogTagEventDeserializer;
import com.sap.sailing.server.gateway.deserialization.racelog.impl.RaceLogUseCompetitorsFromRaceLogEventDeserializer;
import com.sap.sailing.server.gateway.serialization.impl.CompetitorJsonSerializer;
@@ -76,7 +77,8 @@ public class RaceLogEventDeserializerTest {
new RaceLogORCLegDataEventDeserializer(competitorDeserializer),
null, new RaceLogORCCertificateAssignmentEventDeserializer(competitorDeserializer),
new RaceLogORCImpliedWindSourceEventDeserializer(competitorDeserializer),
new RaceLogExcludeWindSourceEventDeserializer(competitorDeserializer));
new RaceLogExcludeWindSourceEventDeserializer(competitorDeserializer),
new RaceLogResultsAreOfficialEventDeserializer(competitorDeserializer));
}
}
@@ -34,292 +34,288 @@
<stringAttribute key="profilingTraceType-PERFORMANCE_HOTSPOT_TRACE" value="KEY_IGNORE_SLEEPING_THREADS%CTX_KEY%false%CTX_ENTRY%KEY_APPLICATION_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_SESSION_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_ENABLEMENT%CTX_KEY%true%CTX_ENTRY%KEY_USER_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_REQUEST_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_TENANT_FILTER%CTX_KEY%*%CTX_ENTRY%"/>
<stringAttribute key="profilingTraceType-SYNCHRONIZATION_TRACE" value="KEY_APPLICATION_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_SESSION_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_ENABLEMENT%CTX_KEY%false%CTX_ENTRY%KEY_USER_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_REQUEST_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_TENANT_FILTER%CTX_KEY%*%CTX_ENTRY%"/>
<setAttribute key="selected_target_bundles">
<setEntry value="routeconverter@default:default"/>
<setEntry value="org.apache.commons.codec@default:default"/>
<setEntry value="org.apache.poi@default:default"/>
<setEntry value="org.apache.poi.ooxml@default:default"/>
<setEntry value="org.apache.poi.ooxml.schemas@default:default"/>
<setEntry value="org.apache.commons.collections4@default:default"/>
<setEntry value="org.apache.commons.compress@default:default"/>
<setEntry value="org.apache.poi.source@default:default"/>
<setEntry value="org.apache.poi.ooxml.source@default:default"/>
<setEntry value="org.apache.commons.collections4.source@default:default"/>
<setEntry value="org.apache.commons.compress.source@default:default"/>
<setEntry value="org.dom4j@default:default"/>
<setEntry value="org.apache.xmlbeans@default:default"/>
<setEntry value="org.apache.commons.math@default:default"/>
<setEntry value="org.apache.httpcomponents.httpclient@default:default"/>
<setEntry value="org.apache.httpcomponents.httpcore@default:default"/>
<setEntry value="org.hyperic.sigar@default:default"/>
<setEntry value="com.sun.jersey.contribs.jersey-multipart@default:default"/>
<setEntry value="javax.validation@default:default"/>
<setEntry value="org.apache.commons.fileupload@default:default"/>
<setEntry value="org.jvnet.mimepull@default:default"/>
<setEntry value="org.apache.commons.math3@default:default"/>
<setEntry value="org.mongodb.driver-core@default:default"/>
<setEntry value="org.mongodb.driver-core.source@default:default"/>
<setEntry value="org.mongodb.bson@default:default"/>
<setEntry value="org.mongodb.bson.source@default:default"/>
<setEntry value="org.mongodb.driver-sync@default:default"/>
<setEntry value="org.mongodb.driver-sync@default:default"/>
<setEntry value="org.eclipse.jetty.osgi.boot@3:true"/>
<setEntry value="org.eclipse.jetty.osgi.boot.warurl@default:default"/>
<setEntry value="org.hyperic.sigar@default:default"/>
<setEntry value="slf4j.jdk14@default:default"/>
<setEntry value="lz4-java@default:default"/>
<setEntry value="org.apache.felix.gogo.command@default:default"/>
<setEntry value="org.apache.felix.gogo.runtime@default:default"/>
<setEntry value="org.apache.felix.gogo.shell@default:default"/>
<setEntry value="org.eclipse.jetty.deploy@default:default"/>
<setEntry value="org.eclipse.jetty.deploy.source@default:default"/>
<setEntry value="org.eclipse.jetty.http@3:true"/>
<setEntry value="org.eclipse.jetty.http.source@default:default"/>
<setEntry value="org.eclipse.jetty.io@default:default"/>
<setEntry value="org.eclipse.jetty.io.source@default:default"/>
<setEntry value="org.eclipse.jetty.jmx@default:default"/>
<setEntry value="org.eclipse.jetty.jmx.source@default:default"/>
<setEntry value="org.eclipse.jetty.security@default:default"/>
<setEntry value="org.eclipse.jetty.security.source@default:default"/>
<setEntry value="org.eclipse.jetty.server@default:default"/>
<setEntry value="org.eclipse.jetty.server.source@default:default"/>
<setEntry value="org.eclipse.jetty.servlet@default:default"/>
<setEntry value="org.eclipse.jetty.servlet.source@default:default"/>
<setEntry value="org.eclipse.jetty.util@default:default"/>
<setEntry value="org.eclipse.jetty.util.source@default:default"/>
<setEntry value="org.eclipse.jetty.util.ajax@default:default"/>
<setEntry value="org.eclipse.jetty.util.ajax.source@default:default"/>
<setEntry value="org.eclipse.jetty.webapp@default:default"/>
<setEntry value="org.eclipse.jetty.webapp.source@default:default"/>
<setEntry value="org.eclipse.jetty.websocket.api@default:default"/>
<setEntry value="org.eclipse.jetty.websocket.api.source@default:default"/>
<setEntry value="org.eclipse.jetty.websocket.client@default:default"/>
<setEntry value="org.eclipse.jetty.websocket.client.source@default:default"/>
<setEntry value="org.eclipse.jetty.websocket.common@default:default"/>
<setEntry value="org.eclipse.jetty.websocket.common.source@default:default"/>
<setEntry value="org.eclipse.jetty.xml@default:default"/>
<setEntry value="org.eclipse.jetty.xml.source@default:default"/>
<setEntry value="slf4j.api@default:default"/>
<setEntry value="org.apache.servicemix.bundles.zxing@default:default"/>
<setEntry value="org.apache.commons.io@default:default"/>
<setEntry value="jcl.over.slf4j@default:default"/>
<setEntry value="com.sun.mail.javax.mail@default:default"/>
<setEntry value="com.rabbitmq.client@default:default"/>
<setEntry value="com.rabbitmq.client.source@default:default"/>
<setEntry value="org.apache.commons.lang@default:default"/>
<setEntry value="org.apache.commons.logging@default:default"/>
<setEntry value="jackson-jaxrs@default:default"/>
<setEntry value="com.sun.jersey@default:default"/>
<setEntry value="javax.ws.rs@default:default"/>
<setEntry value="org.apache.commons.beanutils@default:default"/>
<setEntry value="org.apache.commons.beanutils.source@default:default"/>
<setEntry value="org.apache.servicemix.bundles.ehcache@default:default"/>
<setEntry value="org.apache.servicemix.bundles.scribe@default:default"/>
<setEntry value="org.owasp.encoder@default:default"/>
<setEntry value="org.owasp.encoder.source@default:default"/>
<setEntry value="org.apache.shiro.core@default:default"/>
<setEntry value="org.apache.shiro.core.source@default:default"/>
<setEntry value="org.apache.shiro.ehcache@default:default"/>
<setEntry value="org.apache.shiro.ehcache.source@default:default"/>
<setEntry value="org.apache.shiro.web@default:default"/>
<setEntry value="org.apache.shiro.web.source@default:default"/>
<setEntry value="jackson-core-asl@default:default"/>
<setEntry value="jackson-mapper-asl@default:default"/>
<setEntry value="org.apache.commons.collections@default:default"/>
<setEntry value="org.eclipse.jetty.client@default:default"/>
<setEntry value="javax.xml@default:default"/>
<setEntry value="com.sun.activation.javax.activation@default:default"/>
<setEntry value="org.eclipse.equinox.common@2:true"/>
<setEntry value="org.eclipse.equinox.console@default:default"/>
<setEntry value="org.eclipse.equinox.launcher@default:default"/>
<setEntry value="org.eclipse.equinox.simpleconfigurator@2:true"/>
<setEntry value="org.eclipse.osgi@-1:true"/>
<setEntry value="org.eclipse.osgi.source@default:default"/>
<setEntry value="org.osgi.util.promise@default:default"/>
<setEntry value="org.osgi.util.promise.source@default:default"/>
<setEntry value="org.osgi.util.function@default:default"/>
<setEntry value="org.osgi.util.function.source@default:default"/>
<setEntry value="org.osgi.util.measurement@default:default"/>
<setEntry value="org.osgi.util.measurement.source@default:default"/>
<setEntry value="org.osgi.util.position@default:default"/>
<setEntry value="org.osgi.util.position.source@default:default"/>
<setEntry value="org.osgi.util.xml@default:default"/>
<setEntry value="org.osgi.util.xml.source@default:default"/>
<setEntry value="org.eclipse.osgi.services@default:default"/>
<setEntry value="org.eclipse.osgi.services.source@default:default"/>
<setEntry value="org.eclipse.equinox.cm@default:default"/>
<setEntry value="com.sun.istack.commons-runtime@default:default"/>
<setEntry value="jaxb-api@default:default"/>
<setEntry value="com.sun.xml.bind.jaxb-impl@default:default"/>
<setEntry value="javax.xml.stream@default:default"/>
<setEntry value="javax.xml.ws@default:default"/>
<setEntry value="javax.xml.soap@default:default"/>
<setEntry value="org.eclipse.osgi.util@default:default"/>
<setEntry value="org.eclipse.osgi.util.source@default:default"/>
<setEntry value="com.chargebee.chargebee-java@default:default"/>
<setEntry value="com.github.mwiede.jsch@default:default"/>
<setEntry value="com.github.mwiede.jsch.source@default:default"/>
<setEntry value="bcprov-ext@default:default"/>
<setEntry value="com.amazon.aws.aws-java-api@default:default"/>
<setEntry value="com.amazon.aws.aws-java-api.source@default:default"/>
<setEntry value="org.objectweb.asm@default:default"/>
<setEntry value="org.objectweb.asm.commons@default:default"/>
<setEntry value="org.objectweb.asm.analysis@default:default"/>
<setEntry value="org.objectweb.asm.tree@default:default"/>
<setEntry value="org.objectweb.asm.util@default:default"/>
<setEntry value="org.objectweb.asm.source@default:default"/>
<setEntry value="org.objectweb.asm.analysis.source@default:default"/>
<setEntry value="org.objectweb.asm.commons.source@default:default"/>
<setEntry value="org.objectweb.asm.tree.source@default:default"/>
<setEntry value="org.objectweb.asm.util.source@default:default"/>
<setEntry value="org.eclipse.jetty.apache-jsp@4:true"/>
<setEntry value="org.eclipse.jetty.apache-jsp.source@default:default"/>
<setEntry value="org.eclipse.jetty.osgi.boot.jsp@default:default"/>
<setEntry value="org.eclipse.jetty.osgi.boot.jsp.source@default:default"/>
<setEntry value="org.apache.geronimo.specs.geronimo-jta_1.1_spec@default:default"/>
<setEntry value="org.apache.aries.spifly.dynamic.bundle@3:true"/>
<setEntry value="org.apache.aries.spifly.dynamic.bundle.source@default:default"/>
<setEntry value="org.mortbay.jasper.apache-el@default:default"/>
<setEntry value="org.mortbay.jasper.apache-jsp@default:default"/>
<setEntry value="org.eclipse.jdt.core.compiler.batch@default:default"/>
<setEntry value="org.apache.taglibs.standard-impl@default:default"/>
<setEntry value="org.apache.taglibs.taglibs-standard-spec@default:default"/>
<setEntry value="javax.annotation@default:default"/>
<setEntry value="org.eclipse.jetty.osgi-servlet-api@default:default"/>
<setEntry value="org.apache.xalan@default:default"/>
<setEntry value="org.apache.xml.serializer@default:default"/>
<setEntry value="org.eclipse.equinox.event@default:default"/>
<setEntry value="org.eclipse.jetty.annotations@4:true"/>
<setEntry value="org.eclipse.jetty.jndi@default:default"/>
<setEntry value="org.eclipse.jetty.plus@default:default"/>
<setEntry value="com.sap.db.jdbc@default:default"/>
<setEntry value="com.sap.db.jdbc.source@default:default"/>
<setEntry value="org.osgi.service.packageadmin@default:default"/>
<setEntry value="org.osgi.service.url@default:default"/>
<setEntry value="com.github.haifengl.smile-core@default:default"/>
<setEntry value="com.github.haifengl.smile-core.source@default:default"/>
<setEntry value="com.github.haifengl.smile-data@default:default"/>
<setEntry value="com.github.haifengl.smile-data.source@default:default"/>
<setEntry value="com.github.haifengl.smile-graph@default:default"/>
<setEntry value="com.github.haifengl.smile-graph.source@default:default"/>
<setEntry value="com.github.haifengl.smile-math@default:default"/>
<setEntry value="com.github.haifengl.smile-math.source@default:default"/>
<setEntry value="com.google.gson@default:default"/>
<setEntry value="com.google.gson.source@default:default"/>
</setAttribute>
<setEntry value="bcprov-ext@default:default"/>
<setEntry value="com.amazon.aws.aws-java-api.source"/>
<setEntry value="com.amazon.aws.aws-java-api@default:default"/>
<setEntry value="com.chargebee.chargebee-java@default:default"/>
<setEntry value="com.github.haifengl.smile-core.source"/>
<setEntry value="com.github.haifengl.smile-core@default:default"/>
<setEntry value="com.github.haifengl.smile-data.source"/>
<setEntry value="com.github.haifengl.smile-data@default:default"/>
<setEntry value="com.github.haifengl.smile-graph.source"/>
<setEntry value="com.github.haifengl.smile-graph@default:default"/>
<setEntry value="com.github.haifengl.smile-math.source"/>
<setEntry value="com.github.haifengl.smile-math@default:default"/>
<setEntry value="com.github.mwiede.jsch.source"/>
<setEntry value="com.github.mwiede.jsch@default:default"/>
<setEntry value="com.google.gson.source"/>
<setEntry value="com.google.gson@default:default"/>
<setEntry value="com.rabbitmq.client.source"/>
<setEntry value="com.rabbitmq.client@default:default"/>
<setEntry value="com.sap.db.jdbc.source"/>
<setEntry value="com.sap.db.jdbc@default:default"/>
<setEntry value="com.sun.activation.javax.activation@default:default"/>
<setEntry value="com.sun.istack.commons-runtime@default:default"/>
<setEntry value="com.sun.jersey.contribs.jersey-multipart@default:default"/>
<setEntry value="com.sun.jersey@default:default"/>
<setEntry value="com.sun.mail.javax.mail@default:default"/>
<setEntry value="com.sun.xml.bind.jaxb-impl@default:default"/>
<setEntry value="jackson-core-asl@default:default"/>
<setEntry value="jackson-jaxrs@default:default"/>
<setEntry value="jackson-mapper-asl@default:default"/>
<setEntry value="javax.annotation@default:default"/>
<setEntry value="javax.validation@default:default"/>
<setEntry value="javax.ws.rs@default:default"/>
<setEntry value="javax.xml.soap@default:default"/>
<setEntry value="javax.xml.stream@default:default"/>
<setEntry value="javax.xml.ws@default:default"/>
<setEntry value="javax.xml@default:default"/>
<setEntry value="jaxb-api@default:default"/>
<setEntry value="jcl.over.slf4j@default:default"/>
<setEntry value="lz4-java@default:default"/>
<setEntry value="org.apache.aries.spifly.dynamic.bundle.source"/>
<setEntry value="org.apache.aries.spifly.dynamic.bundle@3:true"/>
<setEntry value="org.apache.commons.beanutils.source@default:default"/>
<setEntry value="org.apache.commons.beanutils@default:default"/>
<setEntry value="org.apache.commons.codec@default:default"/>
<setEntry value="org.apache.commons.collections4.source"/>
<setEntry value="org.apache.commons.collections4@default:default"/>
<setEntry value="org.apache.commons.collections@default:default"/>
<setEntry value="org.apache.commons.compress.source"/>
<setEntry value="org.apache.commons.compress@default:default"/>
<setEntry value="org.apache.commons.fileupload@default:default"/>
<setEntry value="org.apache.commons.io@default:default"/>
<setEntry value="org.apache.commons.lang@default:default"/>
<setEntry value="org.apache.commons.logging@default:default"/>
<setEntry value="org.apache.commons.math3@default:default"/>
<setEntry value="org.apache.commons.math@default:default"/>
<setEntry value="org.apache.felix.gogo.command@default:default"/>
<setEntry value="org.apache.felix.gogo.runtime@default:default"/>
<setEntry value="org.apache.felix.gogo.shell@default:default"/>
<setEntry value="org.apache.geronimo.specs.geronimo-jta_1.1_spec@default:default"/>
<setEntry value="org.apache.httpcomponents.httpclient@default:default"/>
<setEntry value="org.apache.httpcomponents.httpcore@default:default"/>
<setEntry value="org.apache.poi.ooxml.schemas@default:default"/>
<setEntry value="org.apache.poi.ooxml.source"/>
<setEntry value="org.apache.poi.ooxml@default:default"/>
<setEntry value="org.apache.poi.source"/>
<setEntry value="org.apache.poi@default:default"/>
<setEntry value="org.apache.servicemix.bundles.ehcache@default:default"/>
<setEntry value="org.apache.servicemix.bundles.scribe@default:default"/>
<setEntry value="org.apache.servicemix.bundles.zxing@default:default"/>
<setEntry value="org.apache.shiro.core.source"/>
<setEntry value="org.apache.shiro.core@default:default"/>
<setEntry value="org.apache.shiro.ehcache.source"/>
<setEntry value="org.apache.shiro.ehcache@default:default"/>
<setEntry value="org.apache.shiro.web.source"/>
<setEntry value="org.apache.shiro.web@default:default"/>
<setEntry value="org.apache.taglibs.standard-impl@default:default"/>
<setEntry value="org.apache.taglibs.taglibs-standard-spec@default:default"/>
<setEntry value="org.apache.xalan@default:default"/>
<setEntry value="org.apache.xml.serializer@default:default"/>
<setEntry value="org.apache.xmlbeans@default:default"/>
<setEntry value="org.dom4j@default:default"/>
<setEntry value="org.eclipse.equinox.cm@default:default"/>
<setEntry value="org.eclipse.equinox.common@2:true"/>
<setEntry value="org.eclipse.equinox.console@default:default"/>
<setEntry value="org.eclipse.equinox.event@default:default"/>
<setEntry value="org.eclipse.equinox.launcher@default:default"/>
<setEntry value="org.eclipse.equinox.simpleconfigurator@2:true"/>
<setEntry value="org.eclipse.jdt.core.compiler.batch@default:default"/>
<setEntry value="org.eclipse.jetty.annotations@4:true"/>
<setEntry value="org.eclipse.jetty.apache-jsp.source"/>
<setEntry value="org.eclipse.jetty.apache-jsp@4:true"/>
<setEntry value="org.eclipse.jetty.client@default:default"/>
<setEntry value="org.eclipse.jetty.deploy.source"/>
<setEntry value="org.eclipse.jetty.deploy@default:default"/>
<setEntry value="org.eclipse.jetty.http.source"/>
<setEntry value="org.eclipse.jetty.http@3:true"/>
<setEntry value="org.eclipse.jetty.io.source"/>
<setEntry value="org.eclipse.jetty.io@default:default"/>
<setEntry value="org.eclipse.jetty.jmx.source"/>
<setEntry value="org.eclipse.jetty.jmx@default:default"/>
<setEntry value="org.eclipse.jetty.jndi@default:default"/>
<setEntry value="org.eclipse.jetty.osgi-servlet-api@default:default"/>
<setEntry value="org.eclipse.jetty.osgi.boot.jsp.source"/>
<setEntry value="org.eclipse.jetty.osgi.boot.jsp@default:false"/>
<setEntry value="org.eclipse.jetty.osgi.boot.warurl@default:default"/>
<setEntry value="org.eclipse.jetty.osgi.boot@3:true"/>
<setEntry value="org.eclipse.jetty.plus@default:default"/>
<setEntry value="org.eclipse.jetty.security.source"/>
<setEntry value="org.eclipse.jetty.security@default:default"/>
<setEntry value="org.eclipse.jetty.server.source"/>
<setEntry value="org.eclipse.jetty.server@default:default"/>
<setEntry value="org.eclipse.jetty.servlet.source"/>
<setEntry value="org.eclipse.jetty.servlet@default:default"/>
<setEntry value="org.eclipse.jetty.util.ajax.source"/>
<setEntry value="org.eclipse.jetty.util.ajax@default:default"/>
<setEntry value="org.eclipse.jetty.util.source"/>
<setEntry value="org.eclipse.jetty.util@default:default"/>
<setEntry value="org.eclipse.jetty.webapp.source"/>
<setEntry value="org.eclipse.jetty.webapp@default:default"/>
<setEntry value="org.eclipse.jetty.websocket.api.source"/>
<setEntry value="org.eclipse.jetty.websocket.api@default:default"/>
<setEntry value="org.eclipse.jetty.websocket.client.source"/>
<setEntry value="org.eclipse.jetty.websocket.client@default:default"/>
<setEntry value="org.eclipse.jetty.websocket.common.source"/>
<setEntry value="org.eclipse.jetty.websocket.common@default:default"/>
<setEntry value="org.eclipse.jetty.xml.source"/>
<setEntry value="org.eclipse.jetty.xml@default:default"/>
<setEntry value="org.eclipse.osgi.services.source"/>
<setEntry value="org.eclipse.osgi.services@default:default"/>
<setEntry value="org.eclipse.osgi.source"/>
<setEntry value="org.eclipse.osgi.util.source"/>
<setEntry value="org.eclipse.osgi.util@default:default"/>
<setEntry value="org.eclipse.osgi@-1:true"/>
<setEntry value="org.hyperic.sigar@default:default"/>
<setEntry value="org.jvnet.mimepull@default:default"/>
<setEntry value="org.mongodb.bson.source"/>
<setEntry value="org.mongodb.bson@default:default"/>
<setEntry value="org.mongodb.driver-core.source"/>
<setEntry value="org.mongodb.driver-core@default:default"/>
<setEntry value="org.mongodb.driver-sync@default:default"/>
<setEntry value="org.mortbay.jasper.apache-el@default:default"/>
<setEntry value="org.mortbay.jasper.apache-jsp@default:default"/>
<setEntry value="org.objectweb.asm.analysis.source"/>
<setEntry value="org.objectweb.asm.analysis@default:default"/>
<setEntry value="org.objectweb.asm.commons.source"/>
<setEntry value="org.objectweb.asm.commons@default:default"/>
<setEntry value="org.objectweb.asm.source"/>
<setEntry value="org.objectweb.asm.tree.source"/>
<setEntry value="org.objectweb.asm.tree@default:default"/>
<setEntry value="org.objectweb.asm.util.source"/>
<setEntry value="org.objectweb.asm.util@default:default"/>
<setEntry value="org.objectweb.asm@default:default"/>
<setEntry value="org.osgi.service.packageadmin@default:default"/>
<setEntry value="org.osgi.service.url@default:default"/>
<setEntry value="org.osgi.util.function.source"/>
<setEntry value="org.osgi.util.function@default:default"/>
<setEntry value="org.osgi.util.measurement.source"/>
<setEntry value="org.osgi.util.measurement@default:default"/>
<setEntry value="org.osgi.util.position.source"/>
<setEntry value="org.osgi.util.position@default:default"/>
<setEntry value="org.osgi.util.promise.source"/>
<setEntry value="org.osgi.util.promise@default:default"/>
<setEntry value="org.osgi.util.xml.source"/>
<setEntry value="org.osgi.util.xml@default:default"/>
<setEntry value="org.owasp.encoder.source"/>
<setEntry value="org.owasp.encoder@default:default"/>
<setEntry value="routeconverter@default:default"/>
<setEntry value="slf4j.api@default:default"/>
<setEntry value="slf4j.jdk14@default:false"/>
</setAttribute>
<setAttribute key="selected_workspace_bundles">
<setEntry value="com.sap.sailing.geocoding@default:default"/>
<setEntry value="com.sap.sailing.domain.common@default:default"/>
<setEntry value="com.sap.sailing.domain@default:default"/>
<setEntry value="com.sap.sailing.news@4:true"/>
<setEntry value="com.sap.sailing.domain.tractracadapter@5:true"/>
<setEntry value="com.sap.sailing.expeditionconnector@default:default"/>
<setEntry value="com.sap.sailing.domain.windfinderadapter@4:true"/>
<setEntry value="com.sap.sailing.server@5:true"/>
<setEntry value="com.sap.sailing.server.gateway@5:true"/>
<setEntry value="com.sap.sailing.server.gateway.interfaces@default:default"/>
<setEntry value="com.tractrac.clientmodule@default:default"/>
<setEntry value="com.sap.sailing.declination@default:default"/>
<setEntry value="com.sap.sailing.domain.persistence@default:default"/>
<setEntry value="com.sap.sailing.domain.swisstimingadapter@5:true"/>
<setEntry value="com.sap.sailing.domain.swisstimingadapter.persistence@4:true"/>
<setEntry value="com.sap.sailing.domain.swisstimingreplayadapter@4:true"/>
<setEntry value="com.sap.sailing.domain.tractracadapter.persistence@4:true"/>
<setEntry value="com.sap.sailing.gwt.ui@6:true"/>
<setEntry value="com.sap.sailing.udpconnector@default:default"/>
<setEntry value="com.sap.sailing.simulator@default:default"/>
<setEntry value="com.sap.sailing.www@5:true"/>
<setEntry value="com.sap.sailing.resultimport@4:true"/>
<setEntry value="com.sap.sailing.kiworesultimport@4:true"/>
<setEntry value="com.sap.sailing.ess40.resultimport@4:true"/>
<setEntry value="com.sap.sailing.freg.resultimport@4:true"/>
<setEntry value="com.sap.sailing.barbados.resultimport@4:true"/>
<setEntry value="com.sap.sailing.sailwave.resultimport@4:true"/>
<setEntry value="com.sap.sailing.manage2sail.resultimport@4:true"/>
<setEntry value="com.sap.sailing.sailti.resultimport@4:true"/>
<setEntry value="com.sap.sailing.yachtscoring.resultimport@4:true"/>
<setEntry value="com.sap.sailing.velum.resultimport@4:true"/>
<setEntry value="com.sap.sailing.monitoring@7:true"/>
<setEntry value="com.sap.sailing.xrr.resultimport@4:true"/>
<setEntry value="com.sap.sailing.domain.igtimiadapter@4:true"/>
<setEntry value="com.sap.sailing.domain.igtimiadapter.persistence@default:default"/>
<setEntry value="com.sap.sailing.domain.racelogtrackingadapter@4:true"/>
<setEntry value="com.sap.sailing.domain.deckmanadapter@5:true"/>
<setEntry value="com.sap.sailing.domain.oceanraceadapter@5:true"/>
<setEntry value="com.sap.sailing.domain.yellowbrickadapter@5:true"/>
<setEntry value="com.sap.sailing.domain.yellowbrickadapter.persistence@4:true"/>
<setEntry value="com.sap.sailing.xrr.structureimport@default:default"/>
<setEntry value="com.sap.sailing.server.gateway.serialization.shared.android@default:default"/>
<setEntry value="com.sap.sailing.server.gateway.serialization@default:default"/>
<setEntry value="com.sap.sailing.dashboards.gwt@6:true"/>
<setEntry value="com.sap.sailing.dashboards.gwt@6:true"/>
<setEntry value="com.sap.sailing.datamining@5:true"/>
<setEntry value="com.sap.sailing.datamining.shared@default:default"/>
<setEntry value="com.sap.sailing.polars@5:true"/>
<setEntry value="com.sap.sailing.windestimation@5:true"/>
<setEntry value="com.sap.sailing.polars.datamining@5:true"/>
<setEntry value="com.sap.sailing.domain.shared.android@default:default"/>
<setEntry value="com.sap.sailing.manage2sail@default:default"/>
<setEntry value="com.sap.sailing.polars.datamining.shared@default:default"/>
<setEntry value="com.sap.sailing.xrr.schema@default:default"/>
<setEntry value="com.sap.sailing.server.trackfiles@default:default"/>
<setEntry value="com.sap.sailing.competitorimport@default:default"/>
<setEntry value="com.sap.sailing.datamining.provider@default:default"/>
<setEntry value="com.sap.sailing.grib@default:default"/>
<setEntry value="com.sap.sailing.nmeaconnector@default:default"/>
<setEntry value="com.sap.sailing.domain.expeditionadapter@5:true"/>
<setEntry value="com.sap.sailing.expeditionconnector.persistence@4:true"/>
<setEntry value="com.sap.sailing.expeditionconnector.common@default:default"/>
<setEntry value="com.sap.sailing.domain.bravoadapter@5:true"/>
<setEntry value="net.sf.marineapi@default:default"/>
<setEntry value="com.sap.sailing.routeconverterjava11extension@default:default"/>
<setEntry value="com.sap.sailing.server.interface@default:default"/>
<setEntry value="com.sap.sse.datamining.ui@default:default"/>
<setEntry value="com.sap.sailing.domain.igtimiadapter.gateway@5:true"/>
<setEntry value="com.sap.sailing.shared.server@5:true"/>
<setEntry value="com.sap.sailing.shared.server.gateway@5:true"/>
<setEntry value="com.sap.sailing.shared.persistence@default:default"/>
<setEntry value="com.sap.sailing.landscape@default:default"/>
<setEntry value="com.sap.sailing.landscape.gateway@5:true"/>
<setEntry value="com.sap.sailing.landscape.common@default:default"/>
<setEntry value="com.sap.sailing.landscape.ui@default:default"/>
<setEntry value="com.sap.sailing.hanaexport@5:true"/>
<setEntry value="com.tractrac.clientmodule@default:default"/>
<setEntry value="com.sap.sse.gwt@default:default"/>
<setEntry value="com.google.gwt.servlet@default:default"/>
<setEntry value="com.sap.sse.security@default:default"/>
<setEntry value="com.sap.sse.security.ui@6:true"/>
<setEntry value="com.sap.sse.security.userstore.mongodb@4:true"/>
<setEntry value="com.sap.sse@default:default"/>
<setEntry value="com.sap.sse.common@default:default"/>
<setEntry value="com.sap.sse.datamining@default:default"/>
<setEntry value="com.sap.sse.datamining.annotations@default:default"/>
<setEntry value="com.sap.sse.datamining.shared@default:default"/>
<setEntry value="com.sap.sse.gwt.adminconsole@default:default"/>
<setEntry value="com.sap.sse.mongodb@default:default"/>
<setEntry value="com.sap.sse.operationaltransformation@default:default"/>
<setEntry value="com.sap.sse.replication@6:true"/>
<setEntry value="com.sap.sse.filestorage@4:true"/>
<setEntry value="com.sap.sse.shared.android@default:default"/>
<setEntry value="com.sap.sse.mail@5:true"/>
<setEntry value="com.sap.sse.threadmanager@default:default"/>
<setEntry value="com.sap.sse.security.common@default:default"/>
<setEntry value="org.json.simple@default:default"/>
<setEntry value="org.moxieapps.gwt.highcharts@default:default"/>
<setEntry value="com.googlecode.java-diff-utils@default:default"/>
<setEntry value="org.mp4parser.isoparser@default:default"/>
<setEntry value="com.sap.sse.replication.interfaces@default:default"/>
<setEntry value="com.sap.sse.security.datamining@5:true"/>
<setEntry value="com.sap.sse.security.persistence@default:default"/>
<setEntry value="com.sap.sse.security.interface@default:default"/>
<setEntry value="com.sap.sse.replication.persistence@default:default"/>
<setEntry value="com.sap.sse.landscape.common@default:default"/>
<setEntry value="com.sap.sse.landscape@default:default"/>
<setEntry value="com.sap.sse.landscape.aws@4:true"/>
<setEntry value="com.sap.sse.landscape.aws.common@default:default"/>
<setEntry value="com.sap.sse.landscape.aws.persistence@default:default"/>
<setEntry value="com.sap.sse.debranding@default:default"/>
<setEntry value="elemental2@default:default"/>
</setAttribute>
<setEntry value="com.google.gwt.servlet@default:default"/>
<setEntry value="com.googlecode.java-diff-utils@default:default"/>
<setEntry value="com.sap.sailing.barbados.resultimport@4:true"/>
<setEntry value="com.sap.sailing.competitorimport@default:default"/>
<setEntry value="com.sap.sailing.dashboards.gwt@6:true"/>
<setEntry value="com.sap.sailing.datamining.provider@default:default"/>
<setEntry value="com.sap.sailing.datamining.shared@default:default"/>
<setEntry value="com.sap.sailing.datamining@5:true"/>
<setEntry value="com.sap.sailing.declination@default:default"/>
<setEntry value="com.sap.sailing.domain.bravoadapter@5:true"/>
<setEntry value="com.sap.sailing.domain.common@default:default"/>
<setEntry value="com.sap.sailing.domain.deckmanadapter@5:true"/>
<setEntry value="com.sap.sailing.domain.expeditionadapter@5:true"/>
<setEntry value="com.sap.sailing.domain.igtimiadapter.gateway@5:true"/>
<setEntry value="com.sap.sailing.domain.igtimiadapter.persistence@default:default"/>
<setEntry value="com.sap.sailing.domain.igtimiadapter@4:true"/>
<setEntry value="com.sap.sailing.domain.oceanraceadapter@5:true"/>
<setEntry value="com.sap.sailing.domain.persistence@default:default"/>
<setEntry value="com.sap.sailing.domain.racelogtrackingadapter@4:true"/>
<setEntry value="com.sap.sailing.domain.shared.android@default:default"/>
<setEntry value="com.sap.sailing.domain.swisstimingadapter.persistence@4:true"/>
<setEntry value="com.sap.sailing.domain.swisstimingadapter@5:true"/>
<setEntry value="com.sap.sailing.domain.swisstimingreplayadapter@4:true"/>
<setEntry value="com.sap.sailing.domain.tractracadapter.persistence@4:true"/>
<setEntry value="com.sap.sailing.domain.tractracadapter@5:true"/>
<setEntry value="com.sap.sailing.domain.windfinderadapter@4:true"/>
<setEntry value="com.sap.sailing.domain.yellowbrickadapter.persistence@4:true"/>
<setEntry value="com.sap.sailing.domain.yellowbrickadapter@5:true"/>
<setEntry value="com.sap.sailing.domain@default:default"/>
<setEntry value="com.sap.sailing.ess40.resultimport@4:true"/>
<setEntry value="com.sap.sailing.expeditionconnector.common@default:default"/>
<setEntry value="com.sap.sailing.expeditionconnector.persistence@4:true"/>
<setEntry value="com.sap.sailing.expeditionconnector@default:default"/>
<setEntry value="com.sap.sailing.freg.resultimport@4:true"/>
<setEntry value="com.sap.sailing.geocoding@default:default"/>
<setEntry value="com.sap.sailing.grib@default:default"/>
<setEntry value="com.sap.sailing.gwt.ui@6:true"/>
<setEntry value="com.sap.sailing.hanaexport@5:true"/>
<setEntry value="com.sap.sailing.kiworesultimport@4:true"/>
<setEntry value="com.sap.sailing.landscape.common@default:default"/>
<setEntry value="com.sap.sailing.landscape.gateway@5:true"/>
<setEntry value="com.sap.sailing.landscape.ui@default:default"/>
<setEntry value="com.sap.sailing.landscape@default:default"/>
<setEntry value="com.sap.sailing.manage2sail.resultimport@4:true"/>
<setEntry value="com.sap.sailing.manage2sail@default:default"/>
<setEntry value="com.sap.sailing.monitoring@7:true"/>
<setEntry value="com.sap.sailing.news@4:true"/>
<setEntry value="com.sap.sailing.nmeaconnector@default:default"/>
<setEntry value="com.sap.sailing.polars.datamining.shared@default:default"/>
<setEntry value="com.sap.sailing.polars.datamining@5:true"/>
<setEntry value="com.sap.sailing.polars@5:true"/>
<setEntry value="com.sap.sailing.resultimport@4:true"/>
<setEntry value="com.sap.sailing.routeconverterjava11extension@default:false"/>
<setEntry value="com.sap.sailing.sailti.resultimport@4:true"/>
<setEntry value="com.sap.sailing.sailwave.resultimport@4:true"/>
<setEntry value="com.sap.sailing.server.gateway.interfaces@default:default"/>
<setEntry value="com.sap.sailing.server.gateway.serialization.shared.android@default:default"/>
<setEntry value="com.sap.sailing.server.gateway.serialization@default:default"/>
<setEntry value="com.sap.sailing.server.gateway@5:true"/>
<setEntry value="com.sap.sailing.server.interface@default:default"/>
<setEntry value="com.sap.sailing.server.trackfiles@default:default"/>
<setEntry value="com.sap.sailing.server@5:true"/>
<setEntry value="com.sap.sailing.shared.persistence@default:default"/>
<setEntry value="com.sap.sailing.shared.server.gateway@5:true"/>
<setEntry value="com.sap.sailing.shared.server@5:true"/>
<setEntry value="com.sap.sailing.simulator@default:default"/>
<setEntry value="com.sap.sailing.udpconnector@default:default"/>
<setEntry value="com.sap.sailing.velum.resultimport@4:true"/>
<setEntry value="com.sap.sailing.windestimation@5:true"/>
<setEntry value="com.sap.sailing.www@5:true"/>
<setEntry value="com.sap.sailing.xrr.resultimport@4:true"/>
<setEntry value="com.sap.sailing.xrr.schema@default:default"/>
<setEntry value="com.sap.sailing.xrr.structureimport@default:default"/>
<setEntry value="com.sap.sailing.yachtscoring.resultimport@4:true"/>
<setEntry value="com.sap.sse.common@default:default"/>
<setEntry value="com.sap.sse.datamining.annotations@default:default"/>
<setEntry value="com.sap.sse.datamining.shared@default:default"/>
<setEntry value="com.sap.sse.datamining.ui@default:default"/>
<setEntry value="com.sap.sse.datamining@default:default"/>
<setEntry value="com.sap.sse.debranding@default:default"/>
<setEntry value="com.sap.sse.filestorage@4:true"/>
<setEntry value="com.sap.sse.gwt.adminconsole@default:default"/>
<setEntry value="com.sap.sse.gwt@default:default"/>
<setEntry value="com.sap.sse.landscape.aws.common@default:default"/>
<setEntry value="com.sap.sse.landscape.aws.persistence@default:default"/>
<setEntry value="com.sap.sse.landscape.aws@4:true"/>
<setEntry value="com.sap.sse.landscape.common@default:default"/>
<setEntry value="com.sap.sse.landscape@default:default"/>
<setEntry value="com.sap.sse.mail@5:true"/>
<setEntry value="com.sap.sse.mongodb@default:default"/>
<setEntry value="com.sap.sse.operationaltransformation@default:default"/>
<setEntry value="com.sap.sse.replication.interfaces@default:default"/>
<setEntry value="com.sap.sse.replication.persistence@default:default"/>
<setEntry value="com.sap.sse.replication@6:true"/>
<setEntry value="com.sap.sse.security.common@default:default"/>
<setEntry value="com.sap.sse.security.datamining@5:true"/>
<setEntry value="com.sap.sse.security.interface@default:default"/>
<setEntry value="com.sap.sse.security.persistence@default:default"/>
<setEntry value="com.sap.sse.security.ui@6:true"/>
<setEntry value="com.sap.sse.security.userstore.mongodb@4:true"/>
<setEntry value="com.sap.sse.security@default:default"/>
<setEntry value="com.sap.sse.shared.android@default:default"/>
<setEntry value="com.sap.sse.threadmanager@default:default"/>
<setEntry value="com.sap.sse@default:default"/>
<setEntry value="com.tractrac.clientmodule@default:default"/>
<setEntry value="elemental2@default:default"/>
<setEntry value="net.sf.marineapi@default:default"/>
<setEntry value="org.json.simple@default:default"/>
<setEntry value="org.moxieapps.gwt.highcharts@default:default"/>
<setEntry value="org.mp4parser.isoparser@default:default"/>
</setAttribute>
<booleanAttribute key="show_selected_only" value="false"/>
<booleanAttribute key="tracing" value="false"/>
<booleanAttribute key="useCustomFeatures" value="false"/>
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?pde version="3.8"?>
<target name="Race Analysis Target" sequenceNumber="218">
<target name="Race Analysis Target" sequenceNumber="219">
<locations>
<location includeAllPlatforms="false" includeConfigurePhase="false" includeMode="slicer" includeSource="true" type="InstallableUnit">
<unit id="javax.ws.rs" version="1.1.1.v20130318-1750"/>
@@ -147,7 +147,7 @@
<repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20210223232630/repository"/>
</location>
<location includeAllPlatforms="false" includeConfigurePhase="false" includeMode="slicer" includeSource="true" type="InstallableUnit">
<unit id="com.amazon.aws.aws-java-api.feature.group" version="2.18.8"/>
<unit id="com.amazon.aws.aws-java-api.feature.group" version="2.20.59"/>
<repository location="https://p2.sapsailing.com/p2/aws-sdk/"/>
</location>
<location includeAllPlatforms="false" includeConfigurePhase="false" includeMode="slicer" includeSource="true" type="InstallableUnit">
@@ -1,13 +1,15 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<launchConfiguration type="org.eclipse.jdt.launching.localJavaApplication">
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
<listEntry value="/com.sap.sailing.windestimation.lab/src/com/sap/sailing/windestimation/model/SimpleModelsTrainingPart2.java"/>
</listAttribute>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
<listEntry value="1"/>
</listAttribute>
<booleanAttribute key="org.eclipse.jdt.launching.ATTR_EXCLUDE_TEST_CODE" value="true"/>
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.sap.sailing.windestimation.model.SimpleModelsTrainingPart2"/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="com.sap.sailing.windestimation.lab"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-ea -Dmongo.port=10202 -Dmongo.dbName=windestimation"/>
<booleanAttribute key="org.eclipse.debug.core.ATTR_FORCE_SYSTEM_CONSOLE_ENCODING" value="false"/>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
<listEntry value="/com.sap.sailing.windestimation.lab/src/com/sap/sailing/windestimation/model/SimpleModelsTrainingPart2.java"/>
</listAttribute>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
<listEntry value="1"/>
</listAttribute>
<booleanAttribute key="org.eclipse.jdt.launching.ATTR_EXCLUDE_TEST_CODE" value="true"/>
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.sap.sailing.windestimation.model.SimpleModelsTrainingPart2"/>
<stringAttribute key="org.eclipse.jdt.launching.MODULE_NAME" value="com.sap.sailing.windestimation.lab"/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="com.sap.sailing.windestimation.lab"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-ea -Dmongo.port=10200 -Dmongo.dbName=windestimation"/>
</launchConfiguration>
@@ -23,6 +23,14 @@
<div class="mainContent">
<h2 class="releaseHeadline">Release Notes - Administration Console</h2>
<div class="innerContent">
<h2 class="articleSubheadline">April 2023</h2>
<ul class="bulletList">
<li>Added boat classes "IMOCA", "VO65" and "VO60"</li>
<li>Implemented a new scoring scheme that ignores A8.1 tie-breaking and only uses A8.2 (last race,
second-to-last, etc.)</li>
<li>Implemented a new scoring scheme that supports the Formula Kite medal series format but
ignores A8.1 tie-breaking and only uses A8.2 (last race, second-to-last, etc.)</li>
</ul>
<h2 class="articleSubheadline">March 2023</h2>
<ul class="bulletList">
<li>Users with administrative permissions on a server replica set will now be notified by e-mail about important
@@ -58,6 +66,22 @@
identifying a host in one region and the host name is determined from within another region then the
internal DNS name chosen is that of the other region and therefore incorrect. Instead, now the IP address's
numeric representation is used instead.</li>
<li>The scoring scheme "Low Point System, First with three wins in medals is winner" has been adjusted to meet the
Olympic requirements; see official ORIS document OG2024_SAL_ORIS_R8_V1.2_20230330.pdf. It should be used as follows:
An opening series, optionally split into qualification and final series in case the fleet is split, is followed
by two medal series: one for the semi-final, and another for the grand final. Both of these medal series should
start with a carry-forward which will be interpreted by the scoring scheme as the "number of wins carried forward."
These carried wins need to be entered manually into the leaderboard (two wins for the best-seeded, one for the
second-best seeded competitor for each fleet in those medal series). The semi-final series should consist of
two fleets (A/B, equal rank 0) and will need seven race columns in addition to the carry-forward column because
four competitors will race for the first to take three wins (defined as score 1.0 by the document), with a total
of three carried wins may take six races at most, but due to a standard penalty (STP) no competitor may score a
win in the first race of a semi-final fleet.<p>
The grand-finalists also race for the first to take three wins (including the carried-forward wins). They are
ranked by wins, then by the last race rank, then by the second-to-last race rank and so on, up to the first
grand-final race, and then by the opening series rank. Likewise, the semi-finalists will be ranked in their
respective semi-final fleet. For their overall rank of the competitors ranking 2nd to 4th in their semi-final
the two equally-ranked competitors from fleets A and B will be compared based on their opening series rank.</li>
</ul>
<h2 class="articleSubheadline">February 2023</h2>
<ul class="bulletList">
@@ -0,0 +1,42 @@
package com.sap.sse.common.test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.util.Arrays;
import org.junit.Test;
import com.sap.sse.common.Util;
public class UtilTest {
@Test
public void testFilterIterable() {
final Iterable<Integer> ints = Arrays.asList(1, 2, 3, 9, 8, 7, 4, 5, 6);
assertEquals(Arrays.asList(1, 2, 3, 4, 5, 6), Util.asList(Util.filter(ints, i->i<7)));
}
@Test
public void testFilterLast() {
final Iterable<Integer> ints = Arrays.asList(1, 2, 3, 9, 8, 7, 4, 5, 6);
assertEquals(Arrays.asList(1, 2, 3, 9, 8, 7, 4, 5), Util.asList(Util.filter(ints, i->i!=6)));
}
@Test
public void testFilterFirst() {
final Iterable<Integer> ints = Arrays.asList(1, 2, 3, 9, 8, 7, 4, 5, 6);
assertEquals(Arrays.asList(2, 3, 9, 8, 7, 4, 5, 6), Util.asList(Util.filter(ints, i->i!=1)));
}
@Test
public void testFilterNone() {
final Iterable<Integer> ints = Arrays.asList(1, 2, 3, 9, 8, 7, 4, 5, 6);
assertEquals(ints, Util.asList(Util.filter(ints, i->true)));
}
@Test
public void testFilterAll() {
final Iterable<Integer> ints = Arrays.asList(1, 2, 3, 9, 8, 7, 4, 5, 6);
assertTrue(Util.isEmpty(Util.asList(Util.filter(ints, i->false))));
}
}
@@ -14,11 +14,11 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.SortedSet;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
@@ -379,9 +379,39 @@ public class Util {
addAll(map(iterable, mapper), result);
return result;
}
@FunctionalInterface
public static interface UtilPredicate<T> {
boolean test(T t);
}
public static <T> Iterable<T> filter(final Iterable<T> iterable, final Predicate<T> predicate) {
return ()->StreamSupport.stream(iterable.spliterator(), /* parallel */ false).filter(predicate).iterator();
public static <T> Iterable<T> filter(final Iterable<T> iterable, final UtilPredicate<T> predicate) {
return ()->new Iterator<T>() {
private boolean hasNext = true;
private Iterator<T> iterator = iterable.iterator();
private T next = advance();
@Override
public boolean hasNext() {
return hasNext;
}
private T advance() {
while ((hasNext=iterator.hasNext()) && !predicate.test(next=iterator.next())) {
}
return next;
}
@Override
public T next() {
if (!hasNext) {
throw new NoSuchElementException();
}
final T result = next;
advance();
return result;
}
};
}
/**
+2 -2
View File
@@ -742,14 +742,14 @@
id="com.amazon.aws.aws-java-api"
download-size="0"
install-size="0"
version="2.18.8"
version="2.20.59"
unpack="false"/>
<plugin
id="com.amazon.aws.aws-java-api.source"
download-size="0"
install-size="0"
version="2.18.8"
version="2.20.59"
unpack="false"/>
<plugin
@@ -7,6 +7,8 @@ Bundle-Vendor: SAP
Fragment-Host: com.sap.sse.landscape.aws.common
Import-Package: org.hamcrest;version="2.2.0",
org.junit;version="4.13.2",
org.junit.function;version="4.13.2"
org.junit.function;version="4.13.2",
software.amazon.awssdk.services.elasticloadbalancingv2.model
Automatic-Module-Name: com.sap.sse.landscape.aws.common.test
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Require-Bundle: com.sap.sse.landscape.aws
@@ -0,0 +1,232 @@
package com.sap.sse.landscape.aws.common.shared;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.IntStream;
import org.junit.Before;
import org.junit.Test;
import com.sap.sse.common.Util;
import com.sap.sse.common.Util.Pair;
import com.sap.sse.landscape.aws.ApplicationLoadBalancer;
import com.sap.sse.landscape.aws.impl.LoadBalancerRuleInserter;
import com.sap.sse.landscape.aws.impl.LoadBalancerRuleInserter.LoadBalancerAdapter;
import com.sap.sse.landscape.aws.impl.LoadBalancerRuleInserter.RuleAdapter;
public class LoadBalancerRuleInserterTest {
private static final String RULE_NAME_PREFIX = "Rule ";
private static final String DEFAULT_RULE_NAME = "default";
private LoadBalancerAdapter<TestRuleAdapter> loadBalancerAdapter;
private LoadBalancerRuleInserter<String, TestRuleAdapter> ruleInserter;
private static class TestRuleAdapter implements RuleAdapter<TestRuleAdapter> {
private final boolean isDefault;
private final int priority;
private final String ruleArn;
public TestRuleAdapter(boolean isDefault, int priority, String ruleArn) {
super();
this.isDefault = isDefault;
this.priority = priority;
this.ruleArn = ruleArn;
}
@Override
public boolean isDefault() {
return isDefault;
}
@Override
public String priority() {
return "" + priority;
}
@Override
public String ruleArn() {
return ruleArn;
}
@Override
public TestRuleAdapter copyWithNewPriority(int priorityToUseForRuleCopy) {
return new TestRuleAdapter(isDefault, priorityToUseForRuleCopy, ruleArn);
}
}
private static class TestLoadBalancerAdapter implements LoadBalancerAdapter<TestRuleAdapter> {
private static final long serialVersionUID = -4853040303405813921L;
private Iterable<TestRuleAdapter> rules;
@Override
public String getName() {
return "Test Load Balancer Adapter";
}
@Override
public Iterable<TestRuleAdapter> getRules() {
return rules;
}
@Override
public void updateLoadBalancerListenerRulePriorities(
List<Pair<Integer, TestRuleAdapter>> newPrioritiesForExistingRules) {
rules = Util.map(newPrioritiesForExistingRules, p -> p.getB().copyWithNewPriority(p.getA()));
}
@Override
public void addRules(List<TestRuleAdapter> rulesToAdd) {
rules = Util.concat(Arrays.asList(rules, rulesToAdd));
}
}
@Before
public void setUp() {
loadBalancerAdapter = new TestLoadBalancerAdapter();
loadBalancerAdapter.addRules(Arrays.asList(new TestRuleAdapter(/* isDefault */ true, 0, DEFAULT_RULE_NAME)));
ruleInserter = new LoadBalancerRuleInserter<>(loadBalancerAdapter, ApplicationLoadBalancer.MAX_PRIORITY,
ApplicationLoadBalancer.MAX_RULES_PER_LOADBALANCER);
assertUniqueAscendingPriorities();
}
@Test
public void simpleRuleInsertionTest() {
assertEquals(1, Util.size(getRulesSortedByPriority()));
addRules(1, true, 1);
assertEquals(2, Util.size(getRulesSortedByPriority()));
assertUniqueAscendingPriorities();
}
@Test
public void additionalRuleInsertionTest() {
assertEquals(1, Util.size(getRulesSortedByPriority()));
addRules(1, true, 1);
assertEquals(2, Util.size(getRulesSortedByPriority()));
addRules(1, true, 2);
assertUniqueAscendingPriorities();
}
@Test
public void testMassInsert() {
addRules(1, true, 1, 3, 2);
assertEquals(4, Util.size(getRulesSortedByPriority()));
assertUniqueAscendingPriorities();
assertRuleOrder(1, 3, 2);
}
@Test
public void testInsertBefore() {
addRules(1, true, 1, 2);
addRulesBefore(1, true, /* insert before: */ 2, /* insert rule numbers: */ 3, 4);
assertRuleOrder(1, 3, 4, 2);
}
@Test
public void testContiguousInsertBeforeWithEnoughSpace() {
addRules(1, true, 1, 2);
loadBalancerAdapter.addRules(Arrays.asList(new TestRuleAdapter(/* isDefault */ false, 5, RULE_NAME_PREFIX+5)));
addRulesBefore(1, true, /* insert before: */ 5, /* insert rule numbers: */ 3, 4);
assertRuleOrder(1, 2, 3, 4, 5);
}
@Test
public void testNonContiguousInsertBeforeWithoutEnoughSpace() {
addRules(1, true, 1);
loadBalancerAdapter.addRules(Arrays.asList(new TestRuleAdapter(/* isDefault */ false, 3, RULE_NAME_PREFIX+3)));
loadBalancerAdapter.addRules(Arrays.asList(new TestRuleAdapter(/* isDefault */ false, 5, RULE_NAME_PREFIX+5)));
addRulesBefore(1, false, /* insert before: */ 5, /* insert rule numbers: */ 2, 4);
assertRuleOrder(1, 2, 3, 4, 5);
}
@Test
public void testInsertInHole() {
addRules(1, true, 1);
loadBalancerAdapter.addRules(Arrays.asList(new TestRuleAdapter(/* isDefault */ false, 4, RULE_NAME_PREFIX+4)));
addRules(2, true, 2, 3);
assertRuleOrder(1, 2, 3, 4);
}
@Test
public void testInsertAfterTooSmallAHole() {
addRules(1, true, 1);
loadBalancerAdapter.addRules(Arrays.asList(new TestRuleAdapter(/* isDefault */ false, 4, RULE_NAME_PREFIX+4)));
addRules(2, true, 2, 3, 5);
assertRuleOrder(1, 4, 2, 3, 5);
}
@Test
public void testNonContiguousInsertWithTooSmallAHole() {
addRules(1, true, 1);
loadBalancerAdapter.addRules(Arrays.asList(new TestRuleAdapter(/* isDefault */ false, 4, RULE_NAME_PREFIX+4)));
addRules(2, false, 2, 3, 5);
assertRuleOrder(1, 2, 3, 4, 5);
}
@Test
public void testExceptionForTooManyRules() {
try {
addRules(1, true, Util.toArray(IntStream.range(0, 101).boxed()::iterator, new Integer[0]));
fail("Expected IllegalArgumentException");
} catch (IllegalArgumentException e) {
// expected
}
}
@Test
public void testExceptionForTooManyRulesWhenAdding() {
addRules(1, true, Util.toArray(IntStream.range(0, 50).boxed()::iterator, new Integer[0]));
try {
addRules(1, true, Util.toArray(IntStream.range(50, 101).boxed()::iterator, new Integer[0]));
fail("Expected IllegalArgumentException");
} catch (IllegalArgumentException e) {
// expected
}
}
@Test
public void testExceptionForRuleToInsertBeforeNotFound() {
addRules(1, true, 1);
try {
addRulesBefore(1, true, Optional.of(new TestRuleAdapter(/* isDefault */ false, 2, RULE_NAME_PREFIX+2)), 2);
fail("Expected IllegalArgumentException");
} catch (IllegalArgumentException e) {
// expected
}
}
private void addRules(int priority, boolean forceContiguous, Integer... ruleNumbers) {
addRulesBefore(priority, forceContiguous, /* insertBefore */ Optional.empty(), ruleNumbers);
}
private void addRulesBefore(int priority, boolean forceContiguous, int insertBeforeRuleNumber, Integer... ruleNumbers) {
addRulesBefore(priority, forceContiguous, Util.stream(getRulesSortedByPriority())
.filter(r -> r.ruleArn().equals(RULE_NAME_PREFIX + insertBeforeRuleNumber)).findFirst(), ruleNumbers);
}
private void addRulesBefore(int priority, boolean forceContiguous, Optional<TestRuleAdapter> insertBefore, Integer... ruleNumbers) {
ruleInserter.addRulesAssigningUnusedPriorities(forceContiguous, insertBefore,
Util.map(Arrays.asList(ruleNumbers), ruleNumber->new TestRuleAdapter(/* isDefault */ false, priority, RULE_NAME_PREFIX + ruleNumber)));
}
private void assertRuleOrder(Integer... ruleNumbers) {
assertEquals(Util.asList(Util.map(Arrays.asList(ruleNumbers), ruleNumber->RULE_NAME_PREFIX+ruleNumber)),
// ignore default rule at index 0 in comparison; look at numbered rules only
Util.asList(Util.map(getRulesSortedByPriority(), TestRuleAdapter::ruleArn)).subList(1, Util.size(getRulesSortedByPriority())));
}
private Iterable<TestRuleAdapter> getRulesSortedByPriority() {
return ruleInserter.getRulesSortedByPriority();
}
private void assertUniqueAscendingPriorities() {
int lastPriority = -1;
for (final TestRuleAdapter rule : getRulesSortedByPriority()) {
assertTrue(Integer.valueOf(rule.priority()) > lastPriority);
lastPriority = Integer.valueOf(rule.priority());
}
}
}
@@ -467,7 +467,8 @@ public class ConnectivityTest<ProcessT extends AwsApplicationProcess<String, Sai
@Test
public void createEmptyLoadBalancerTest() throws InterruptedException, ExecutionException {
final String albName = "MyAlb"+new Random().nextInt();
final ApplicationLoadBalancer<String> alb = landscape.createLoadBalancer(albName, region);
final ApplicationLoadBalancer<String> alb = landscape.createLoadBalancer(albName, region,
/* securityGroupForVpc null means use default VPC which is OK for this test */ null);
try {
assertNotNull(alb);
assertEquals(albName, alb.getName());
@@ -491,7 +492,7 @@ public class ConnectivityTest<ProcessT extends AwsApplicationProcess<String, Sai
public void createAndDeleteTargetGroupTest() {
final String targetGroupName = "TestTargetGroup-"+new Random().nextInt();
final TargetGroup<String> targetGroup = landscape.createTargetGroup(region, targetGroupName, 80, ApplicationProcess.HEALTH_CHECK_PATH, 80,
/* loadBalancerArn */ null);
/* loadBalancerArn */ null, /* vpcId */ null);
try {
final TargetGroup<String> fetchedTargetGroup = landscape.getTargetGroup(region, targetGroupName,
targetGroup.getTargetGroupArn(), targetGroup.getLoadBalancerArn(), targetGroup.getProtocol(),
@@ -1,5 +1,6 @@
package com.sap.sse.landscape.aws;
import java.util.List;
import java.util.Optional;
import java.util.regex.Pattern;
@@ -52,6 +53,8 @@ public interface ApplicationLoadBalancer<ShardingKey> extends Named {
*/
public static final int MAX_PRIORITY = 50000;
AwsLandscape<ShardingKey> getLandscape();
/**
* The DNS name of this load balancer; can be used, e.g., to set a CNAME DNS record pointing
* to this load balancer.
@@ -68,6 +71,10 @@ public interface ApplicationLoadBalancer<ShardingKey> extends Named {
Region getRegion();
String getArn();
String getVpcId();
List<String> getSecurityGroupIds();
default String getId() {
return getArn().substring(getArn().lastIndexOf('/')+1);
@@ -101,21 +108,17 @@ public interface ApplicationLoadBalancer<ShardingKey> extends Named {
* original order to the resulting sequence. Otherwise, the existing rules are "compressed" by re-numbering their
* priorities to make space for the new rules at the end of the list.
*
* @param insertBefore
* if empty, the new {@code rules} can be inserted anywhere in the list of rules; if a {@link Rule} is
* specified and that rule is part of this ALB's {@link #getRules() rules},
* {@link #shiftRulesToMakeSpaceAt(int) make space} at that point and insert the new {@code rules} there.
* If the rule specified cannot be found in this ALB's {@link #getRules()}, an {@link IllegalArgumentException}
* is thrown. The rules are compared by their {@link Rule#ruleArn() ARN}, not by their Java object identity.
*
* @return copies of the original rules, with unused {@link Rule#priority() priorities} assigned, as passed already
* to {@link #addRules(Rule...)}.
*/
Iterable<Rule> addRulesAssigningUnusedPriorities(boolean forceContiguous, Rule... rules);
/**
* For inserting e.g. Shard at a specific priority, we must ensure that the priority is unique in the ruleset.
* This function shifts every priority starting at the highest priority one higher for making the priority at {@code index}
* free.
* @param index
* index supposed to be free
* @throws IllegalStateException
* gets thrown if shifting exceeds the limit of priorities
*/
void shiftRulesToMakeSpaceAt(int index) throws IllegalStateException;
Iterable<Rule> addRulesAssigningUnusedPriorities(boolean forceContiguous, Optional<Rule> insertBefore, Rule... rules);
/**
* Returns the priority which should be used as the next sharding priority.
@@ -423,15 +423,21 @@ public interface AwsLandscape<ShardingKey> extends Landscape<ShardingKey> {
ApplicationLoadBalancer<ShardingKey> getLoadBalancerByName(String name, Region region);
/**
* Creates an application load balancer with the name and in the region specified. The method returns once the request
* has been responded to. The load balancer may still be in a pre-ready state. Use {@link #getApplicationLoadBalancerStatus(ApplicationLoadBalancer)}
* to find out more.<p>
* Creates an application load balancer with the name and in the region specified. The method returns once the
* request has been responded to. The load balancer may still be in a pre-ready state. Use
* {@link #getApplicationLoadBalancerStatus(ApplicationLoadBalancer)} to find out more.
* <p>
*
* The load balancer features two listeners: an HTTP listener for port 80 that redirects all requests to HTTPS port 443 with host, path, and query
* left unchanged; and an HTTPS listener that forwards to a default target group to which the default central reverse proxy of the {@code region}
* is added as a target.
* The load balancer features two listeners: an HTTP listener for port 80 that redirects all requests to HTTPS port
* 443 with host, path, and query left unchanged; and an HTTPS listener that forwards to a default target group to
* which the default central reverse proxy of the {@code region} is added as a target.
*
* @param securityGroupForVpc
* if provided, the security group's VPC association will be used to constrain the subnets for the AZs to
* that VPC; if {@code null}, the default subnet for each respective AZ is used
*/
ApplicationLoadBalancer<ShardingKey> createLoadBalancer(String name, Region region) throws InterruptedException, ExecutionException;
ApplicationLoadBalancer<ShardingKey> createLoadBalancer(String name, Region region,
SecurityGroup securityGroupForVpc) throws InterruptedException, ExecutionException;
Iterable<Listener> getListeners(ApplicationLoadBalancer<ShardingKey> alb);
@@ -477,15 +483,15 @@ public interface AwsLandscape<ShardingKey> extends Landscape<ShardingKey> {
* Creates a target group with a default configuration that includes a health check URL. Stickiness is enabled with
* the default duration of one day. The load balancing algorithm is set to {@code least_outstanding_requests}. The
* protocol (HTTP or HTTPS) is inferred from the port: 443 means HTTPS; anything else means HTTP.
*
* @param loadBalancerArn
* will be set as the resulting target group's {@link TargetGroup#getLoadBalancerArn() load balancer
* ARN}. This is helpful if you already know to which load balancer you will add rules in a moment that
* will forward to this target group. Just created, the target group's load balancer ARN in AWS will still
* be {@code null}, so cannot be discovered.
* @param vpcId if {@code null}, the {@code region}'s default VPC will be used
*/
TargetGroup<ShardingKey> createTargetGroup(Region region, String targetGroupName, int port,
String healthCheckPath, int healthCheckPort, String loadBalancerArn);
String healthCheckPath, int healthCheckPort, String loadBalancerArn, String vpcId);
/**
* Copies a target group from an existing target group. The name gets extended with {@code suffix}
* @param parent
@@ -564,6 +570,8 @@ public interface AwsLandscape<ShardingKey> extends Landscape<ShardingKey> {
SecurityGroup getSecurityGroup(String securityGroupId, Region region);
Optional<SecurityGroup> getSecurityGroupByName(String securityGroupName, Region region);
void addTargetsToTargetGroup(
TargetGroup<ShardingKey> targetGroup,
Iterable<AwsInstance<ShardingKey>> targets);
@@ -605,9 +613,14 @@ public interface AwsLandscape<ShardingKey> extends Landscape<ShardingKey> {
* only a wildcard DNS record exists for the domain. There is a naming rule in place such that
* {@link #getNonDNSMappedLoadBalancer(Region, String)}, when called with an equal {@code wildcardDomain} and
* {@code region}, will deliver the load balancer created by this call.
*
* @param securityGroupForVpc
* if provided, the security group's VPC association will be used to constrain the subnets for the AZs to
* that VPC; if {@code null}, the default subnet for each respective AZ is used
*/
ApplicationLoadBalancer<ShardingKey> createNonDNSMappedLoadBalancer(Region region, String wildcardDomain) throws InterruptedException, ExecutionException;
ApplicationLoadBalancer<ShardingKey> createNonDNSMappedLoadBalancer(Region region, String wildcardDomain,
SecurityGroup securityGroupForVpc) throws InterruptedException, ExecutionException;
/**
* Looks up the hostname in the DNS and assumes to get a load balancer CNAME record for it that exists in the {@code region}
* specified. The load balancer is then looked up by its {@link ApplicationLoadBalancer#getDNSName() host name}.
@@ -796,7 +809,7 @@ public interface AwsLandscape<ShardingKey> extends Landscape<ShardingKey> {
*/
void updateInstanceTypeInAutoScalingGroup(Region region, Iterable<AwsAutoScalingGroup> autoScalingGroups, String replicaSetName, InstanceType instanceType);
TargetGroup<ShardingKey> createTargetGroupWithoutLoadbalancer(Region region, String targetGroupName, int port);
TargetGroup<ShardingKey> createTargetGroupWithoutLoadbalancer(Region region, String targetGroupName, int port, String vpcId);
/**
* Creates a new auto-scaling group, using an existing one as a template and only deriving a new name for the auto-scaling group
@@ -1,18 +1,17 @@
package com.sap.sse.landscape.aws.impl;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map.Entry;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.Optional;
import java.util.Set;
import java.util.TreeMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.sap.sse.common.Duration;
import com.sap.sse.common.Util;
@@ -21,6 +20,7 @@ import com.sap.sse.landscape.aws.ApplicationLoadBalancer;
import com.sap.sse.landscape.aws.AwsLandscape;
import com.sap.sse.landscape.aws.TargetGroup;
import com.sap.sse.landscape.aws.common.shared.PlainRedirectDTO;
import com.sap.sse.landscape.aws.impl.LoadBalancerRuleInserter.ALBRuleAdapter;
import software.amazon.awssdk.services.elasticloadbalancingv2.model.Action;
import software.amazon.awssdk.services.elasticloadbalancingv2.model.ActionTypeEnum;
@@ -32,7 +32,6 @@ import software.amazon.awssdk.services.elasticloadbalancingv2.model.RedirectActi
import software.amazon.awssdk.services.elasticloadbalancingv2.model.RedirectActionStatusCodeEnum;
import software.amazon.awssdk.services.elasticloadbalancingv2.model.Rule;
import software.amazon.awssdk.services.elasticloadbalancingv2.model.RuleCondition;
import software.amazon.awssdk.services.elasticloadbalancingv2.model.RulePriorityPair;
import software.amazon.awssdk.services.elasticloadbalancingv2.model.TargetGroupTuple;
public class ApplicationLoadBalancerImpl<ShardingKey>
@@ -52,6 +51,11 @@ implements ApplicationLoadBalancer<ShardingKey> {
this.loadBalancer = loadBalancer;
this.landscape = landscape;
}
@Override
public AwsLandscape<ShardingKey> getLandscape() {
return landscape;
}
@Override
public String getName() {
@@ -72,6 +76,16 @@ implements ApplicationLoadBalancer<ShardingKey> {
public Region getRegion() {
return region;
}
@Override
public String getVpcId() {
return loadBalancer.vpcId();
}
@Override
public List<String> getSecurityGroupIds() {
return loadBalancer.securityGroups();
}
@Override
public Iterable<Rule> getRules() {
@@ -94,51 +108,6 @@ implements ApplicationLoadBalancer<ShardingKey> {
return landscape.createLoadBalancerListenerRules(region, getListener(ProtocolEnum.HTTPS), rulesToAdd);
}
/**
* Returns the priority if the priority is not higher than {@code MAX_PRIORITY}
* @param priority
* requested priority
* @return
* Priority if it's valid
* @throws IllegalStateException
* If the requested priority is higher than {@code MAX_PRIORITY}
*/
private int checkNewPriority(int priority) throws IllegalStateException {
if (priority < MAX_PRIORITY) {
return priority;
} else {
throw new IllegalStateException("Priority was greater than " + MAX_PRIORITY +"!");
}
}
@Override
public void shiftRulesToMakeSpaceAt(int targetPrio) throws IllegalStateException {
final Iterable<Rule> rules = getRules();
final TreeMap<Integer, Rule> rulesSorted = getRulesSorted(rules);
int lastPrio = targetPrio;
boolean skipNext = false;
final Collection<RulePriorityPair> result = new ArrayList<>();
if (rulesSorted.get(targetPrio) != null) {// if there is a rule on prio
for (Entry<Integer, Rule> entry : rulesSorted.entrySet()) {
final Integer priority = entry.getKey();
if (priority >= targetPrio && !skipNext) {
// if prio is higher than target prio and is not supposed to be skipped
if (priority - lastPrio > 0) {
// if there is a gap between current prio and the last one. -> so this one is not supposed to be
// skipped and every rule after this
skipNext = true;
break;
} else {
lastPrio = priority + 1;
result.add(RulePriorityPair.builder().ruleArn(entry.getValue().ruleArn())
.priority(checkNewPriority(lastPrio)).build());
}
}
}
landscape.updateLoadBalancerListenerRulePriorities(getRegion(), result);
}
}
@Override
public int getFirstShardingPriority(String hostname) throws IllegalStateException {
final Iterable<Rule> rules = getRules();
@@ -192,65 +161,11 @@ implements ApplicationLoadBalancer<ShardingKey> {
}
@Override
public Iterable<Rule> addRulesAssigningUnusedPriorities(boolean forceContiguous, Rule... rules) {
final Iterable<Rule> existingRules = getRules();
if (Util.size(existingRules)-1 + rules.length > MAX_PRIORITY) { // -1 due to the default rule being part of existingRules
throw new IllegalArgumentException("The "+rules.length+" new rules won't find enough unused priority numbers because there are already "+
(Util.size(existingRules)-1)+" of them and together they would exceed the maximum of "+MAX_PRIORITY+" by "+
(Util.size(existingRules)-1 + rules.length - MAX_PRIORITY));
}
final List<Rule> result = new ArrayList<>(rules.length);
final List<Rule> sortedExistingNonDefaultRules = new ArrayList<>(Util.size(existingRules)-1);
Util.addAll(Util.filter(existingRules, r->!r.isDefault()), sortedExistingNonDefaultRules);
Collections.sort(sortedExistingNonDefaultRules, (r1, r2)->Integer.valueOf(r1.priority()).compareTo(Integer.valueOf(r2.priority())));
final int stepwidth;
if (forceContiguous) {
stepwidth = rules.length;
} else {
stepwidth = 1;
}
int rulesIndex = 0;
int previousPriority = 0;
final Iterator<Rule> existingRulesIter = sortedExistingNonDefaultRules.iterator();
while (rulesIndex < rules.length) {
// find next available slot
int nextPriority = MAX_PRIORITY+1; // if no further rule exists, the usable gap ends after MAX_PRIORITY
while (existingRulesIter.hasNext() && (nextPriority=Integer.valueOf(existingRulesIter.next().priority())) <= previousPriority+stepwidth) {
// not enough space for stepwidth many rules; keep on searching
previousPriority = nextPriority;
if (!existingRulesIter.hasNext()) {
nextPriority = MAX_PRIORITY+1;
}
}
if (previousPriority+stepwidth > MAX_PRIORITY) {
if (forceContiguous) {
previousPriority = squeezeExistingRulesAndReturnLastUsedPriority(sortedExistingNonDefaultRules);
nextPriority = MAX_PRIORITY+1;
// we previously checked already that there is enough room for the new set of rules
assert previousPriority + rules.length <= MAX_PRIORITY;
} else {
throw new IllegalStateException(
"The " + rules.length + " new rules don't fit into the existing rule set of load balancer "
+ getName() + " without exceeding the maximum priority of " + MAX_PRIORITY);
}
}
while (rulesIndex < rules.length && ++previousPriority < nextPriority) {
final int priorityToUseForNextRule = previousPriority;
result.add(rules[rulesIndex++].copy(b->b.priority(""+priorityToUseForNextRule)));
}
}
addRules(result.toArray(new Rule[0]));
return result;
}
private int squeezeExistingRulesAndReturnLastUsedPriority(final List<Rule> sortedExistingNonDefaultRules) {
final List<RulePriorityPair> newPrioritiesForExistingRules = new LinkedList<>();
int priority = 0;
for (final Rule existingRule : sortedExistingNonDefaultRules) {
newPrioritiesForExistingRules.add(RulePriorityPair.builder().ruleArn(existingRule.ruleArn()).priority(++priority).build());
}
landscape.updateLoadBalancerListenerRulePriorities(getRegion(), newPrioritiesForExistingRules);
return priority;
public Iterable<Rule> addRulesAssigningUnusedPriorities(boolean forceContiguous, Optional<Rule> insertBefore, Rule... rules) {
final List<Rule> rulesAsList = Arrays.asList(rules);
return Util.map(LoadBalancerRuleInserter.create(this, MAX_PRIORITY, MAX_RULES_PER_LOADBALANCER).addRulesAssigningUnusedPriorities(
forceContiguous, insertBefore.map(LoadBalancerRuleInserter::createRuleAdapter),
Util.map(rulesAsList, LoadBalancerRuleInserter::createRuleAdapter)), ALBRuleAdapter::getRule);
}
@Override
@@ -304,11 +219,26 @@ implements ApplicationLoadBalancer<ShardingKey> {
return Util.stream(getRules()).filter(r->isDefaultRedirectRule(r, hostname)).findAny()
.map(defaultRedirectRule->updateDefaultRedirectRule(defaultRedirectRule.ruleArn(), hostname, pathWithLeadingSlash, query))
.orElseGet(()->{
final Rule defaultRedirectRule = createDefaultRedirectRule(hostname, pathWithLeadingSlash, query);
addRulesAssigningUnusedPriorities(/* forceContiguous */ false, defaultRedirectRule);
return defaultRedirectRule;
return insertAndReturnDefaultRedirectRule(hostname, pathWithLeadingSlash, query);
});
}
/**
* Checks the load balancer rule set size. If already at its maximum, an {@link IllegalStateException} is thrown because
* adding a default rule is not possible, and this method does not handle moving an appplication replica set to a different
* load balancer. If there is space for at least one more rule, searches this load balancer's rule set for the first rule
* for hostname header {@code hostname}. It then uses {@link #shiftRulesToMakeSpaceAt(int)} for that position to make
* space for the new default redirect rule.
*
* @return the new default redirect rule that was inserted into this load balancer's HTTPS listener's rule set
*/
private Rule insertAndReturnDefaultRedirectRule(String hostname, String pathWithLeadingSlash, Optional<String> query) {
final Rule defaultRedirectRule = createDefaultRedirectRule(hostname, pathWithLeadingSlash, query);
addRulesAssigningUnusedPriorities(/* forceContiguous */ false,
Util.stream(getRules()).filter(rule->rule.conditions().stream().anyMatch(condition->condition.field().equals("host-header") && condition.hostHeaderConfig().values().contains(hostname))).findFirst(),
defaultRedirectRule);
return defaultRedirectRule;
}
@Override
public Rule getDefaultRedirectRule(String hostName, PlainRedirectDTO redirect) {

Some files were not shown because too many files have changed in this diff Show More