From 1f1b3b8fef977e700e4b18ef1d591431d30e48ad Mon Sep 17 00:00:00 2001 From: Finn Huelsbusch Date: Mon, 3 Oct 2022 09:53:35 +0200 Subject: [PATCH] merge master into bug 5147 --- .../domain/common/BoatClassMasterdata.java | 4 +- .../domain/common/LeaderboardType.java | 7 +- .../domain/common/ScoringSchemeType.java | 11 +- .../common/dto/AbstractLeaderboardDTO.java | 27 ++- .../dto/MetaLeaderboardRaceColumnDTO.java | 2 +- .../domain/common/dto/RaceColumnDTO.java | 13 +- .../common/dto/RaceColumnDTOFactory.java | 13 +- .../common/dto/RaceColumnInSeriesDTO.java | 4 +- .../dto/SeriesCreationParametersDTO.java | 9 +- .../domain/persistence/FieldNames.java | 3 +- .../impl/DomainObjectFactoryImpl.java | 31 ++- .../impl/MongoObjectFactoryImpl.java | 6 + .../test/LeaderboardDTODiffingTest.java | 2 +- .../ScoringSchemeTypeScoreScalingTest.java | 49 +++++ .../LeaderboardScoringAndRankingTest.java | 6 - ...oardScoringAndRankingTestForLowPoints.java | 7 - .../sap/sailing/domain/base/RaceColumn.java | 15 +- .../domain/base/RaceColumnListener.java | 2 + .../com/sap/sailing/domain/base/Series.java | 21 ++ .../domain/base/impl/DomainFactoryImpl.java | 3 + .../base/impl/RaceColumnInSeriesImpl.java | 5 + .../RaceColumnListenerWithDefaultAction.java | 5 + .../sailing/domain/base/impl/RegattaImpl.java | 5 + .../sailing/domain/base/impl/SeriesImpl.java | 66 ++++-- ...reCorrectionListenerWithDefaultAction.java | 44 ++++ .../leaderboard/FlexibleRaceColumn.java | 9 + .../domain/leaderboard/Leaderboard.java | 2 +- .../leaderboard/LeaderboardCacheManager.java | 39 +--- ...rboardWithOtherTieBreakingLeaderboard.java | 21 ++ .../domain/leaderboard/ScoringScheme.java | 27 ++- ...tHighPointExtremeSailingSeriesOverall.java | 4 +- ...ointFirstGetsFixedOr8AndLastBreaksTie.java | 5 +- .../impl/AbstractLeaderboardWithCache.java | 8 +- .../impl/AbstractScoringSchemeImpl.java | 14 +- .../impl/AbstractSimpleLeaderboardImpl.java | 14 +- .../leaderboard/impl/DelegateLeaderboard.java | 66 ++++++ ...aLeaderboardWithCompetitorElimination.java | 203 ++++++++---------- ...insTiesLastlyBrokenByOtherLeaderboard.java | 35 +++ .../impl/HighPointMatchRacing.java | 1 - .../impl/LeaderboardTotalRankComparator.java | 9 +- ...rdWithOtherTieBreakingLeaderboardImpl.java | 48 +++++ ...hresholdBasedResultDiscardingRuleImpl.java | 2 +- .../meta/MetaLeaderboardColumn.java | 10 + .../util/impl/RaceColumnListeners.java | 6 + .../geocoding/impl/ReverseGeocoderImpl.java | 83 ++++--- .../GWT Sailing SDM.launch | 2 +- .../common/client/BoatClassImageResolver.java | 8 +- .../client/BoatClassImageResources.java | 14 +- .../GetEventsForSailorProfileAction.java | 37 +--- .../AbstractLeaderboardDialog.java | 30 ++- .../CompetitorRegistrationsPanel.java | 3 +- .../adminconsole/CreateRegattaCallback.java | 18 +- .../adminconsole/LeaderboardConfigPanel.java | 64 +++++- ...riptorWithOtherTieBreakingLeaderboard.java | 23 ++ .../RaceColumnsInLeaderboardDialog.java | 2 +- .../adminconsole/RegattaDetailsComposite.java | 12 +- .../RegattaLeaderboardCreateDialog.java | 10 +- .../RegattaLeaderboardDialog.java | 24 +-- .../RegattaLeaderboardEditDialog.java | 8 +- ...attaLeaderboardWithEliminationsDialog.java | 25 --- ...LeaderboardWithEliminationsEditDialog.java | 2 +- ...herTieBreakingLeaderboardCreateDialog.java | 38 ++++ ...WithOtherTieBreakingLeaderboardDialog.java | 58 +++++ ...OtherTieBreakingLeaderboardEditDialog.java | 27 +++ .../ui/adminconsole/RegattaListComposite.java | 3 +- ...egattaWithSeriesAndFleetsCreateDialog.java | 2 +- .../gwt/ui/adminconsole/SeriesDescriptor.java | 9 +- .../gwt/ui/adminconsole/SeriesEditDialog.java | 10 +- .../SeriesWithFleetsCreateDialog.java | 7 +- .../gwt/ui/client/SailingServiceWrite.java | 7 +- .../ui/client/SailingServiceWriteAsync.java | 11 +- .../sailing/gwt/ui/client/StringMessages.java | 7 + .../gwt/ui/client/StringMessages.properties | 9 +- .../ui/client/StringMessages_de.properties | 7 +- .../BoatClassVectorGraphicsResolver.java | 3 +- .../ScoringSchemeTypeFormatter.java | 4 + .../EditableLeaderboardPanel.java | 4 +- .../MatchAndApplyScoreCorrectionsDialog.java | 5 +- .../gwt/ui/server/SailingServiceImpl.java | 15 +- .../ui/server/SailingServiceWriteImpl.java | 24 ++- .../sap/sailing/gwt/ui/shared/SeriesDTO.java | 14 +- .../images/boatclass/BENETEAU_FIRST_35.png | Bin 0 -> 5115 bytes .../images/boatclass/BENETEAU_FIRST_45.png | Bin 0 -> 4920 bytes ...estStoringAndLoadingEventsAndRegattas.java | 2 + .../GPSFixMovingJsonDeserializerTest.java | 35 +++ .../impl/GPSFixMovingJsonDeserializer.java | 2 +- .../gateway/jaxrs/api/EventsResource.java | 9 +- .../gateway/jaxrs/api/RegattasResource.java | 19 +- .../impl/ExpeditionAllInOneImporter.java | 26 +-- .../webservices/api/v1/gpsFixesPostDoc.html | 2 +- .../api/v1/regattaUpdateOrCreateSeries.html | 1 + .../server/interfaces/RacingEventService.java | 6 +- .../RacingEventServiceOperation.java | 5 + .../AbstractRacingEventServiceOperation.java | 10 + .../AddSpecificRegatta.java | 1 + ...ateRegattaLeaderboardWithEliminations.java | 2 +- ...rboardWithOtherTieBreakingLeaderboard.java | 50 +++++ .../UpdateSeries.java | 5 +- .../test/AbstractLogReplicationTest.java | 12 +- .../test/RegattaReplicationTest.java | 6 +- ...BoatCompetitorMetadataReplicationTest.java | 2 +- ...ticRetrackUponCompetitorSetChangeTest.java | 6 +- .../server/test/SearchServiceTest.java | 2 +- .../server/impl/RacingEventServiceImpl.java | 24 ++- .../release_notes_admin.html | 17 ++ .../xrr/structureimport/SeriesParameters.java | 8 +- 106 files changed, 1308 insertions(+), 456 deletions(-) create mode 100644 java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/common/test/ScoringSchemeTypeScoreScalingTest.java create mode 100644 java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/AbstractScoreCorrectionListenerWithDefaultAction.java create mode 100644 java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/RegattaLeaderboardWithOtherTieBreakingLeaderboard.java create mode 100644 java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/DelegateLeaderboard.java create mode 100644 java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/HighPointByWinsTiesLastlyBrokenByOtherLeaderboard.java create mode 100644 java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/RegattaLeaderboardWithOtherTieBreakingLeaderboardImpl.java create mode 100644 java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardDescriptorWithOtherTieBreakingLeaderboard.java create mode 100644 java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaLeaderboardWithOtherTieBreakingLeaderboardCreateDialog.java create mode 100644 java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaLeaderboardWithOtherTieBreakingLeaderboardDialog.java create mode 100644 java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaLeaderboardWithOtherTieBreakingLeaderboardEditDialog.java create mode 100644 java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/client/images/boatclass/BENETEAU_FIRST_35.png create mode 100644 java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/client/images/boatclass/BENETEAU_FIRST_45.png create mode 100644 java/com.sap.sailing.server.gateway.serialization.test/src/com/sap/sailing/server/gateway/deserialization/test/GPSFixMovingJsonDeserializerTest.java create mode 100644 java/com.sap.sailing.server.interface/src/com/sap/sailing/server/operationaltransformation/CreateRegattaLeaderboardWithOtherTieBreakingLeaderboard.java diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/BoatClassMasterdata.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/BoatClassMasterdata.java index 9eff04e2a83..cad0de6a069 100755 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/BoatClassMasterdata.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/BoatClassMasterdata.java @@ -30,6 +30,8 @@ public enum BoatClassMasterdata { BAVARIA_CRUISER_45 ("Bavaria Cruiser 45", true, 14.27, 4.35, BoatHullType.MONOHULL, true, "B45", "B 45", "BAVARIACRUISER45"), BAVARIA_CRUISER_46 ("Bavaria Cruiser 46", true, 14.27, 4.35, BoatHullType.MONOHULL, true, "B46", "B 46", "BAVARIACRUISER46"), BB10M ("BB 10m", true, 10.00, 2.30, BoatHullType.MONOHULL, true, "Dansk BB10M klub"), + BENETEAU_FIRST_35 ("Benetau First 35", true, 10.66, 3.636, BoatHullType.MONOHULL, true, "First 35"), + BENETEAU_FIRST_45 ("Benetau First 45", true, 13.68, 4.202, BoatHullType.MONOHULL, true, "First 45"), BRASSFAHRT_I ("Brassfahrt I", true, 12.00, 3.50, BoatHullType.MONOHULL, true, "Brassfahrt 1"), BRASSFAHRT_II ("Brassfahrt II", true, 12.00, 3.50, BoatHullType.MONOHULL, true, "Brassfahrt 2"), BRASSFAHRT_III ("Brassfahrt III", true, 12.00, 3.50, BoatHullType.MONOHULL, true, "Brassfahrt 3"), @@ -70,7 +72,7 @@ public enum BoatClassMasterdata { 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"), 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"), + 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"), IQFOIL_YOUTH("iQFOil Youth", true, 2.15, 0.85, BoatHullType.SURFERBOARD, false, "iQFOil 85 Youth"), IRC("IRC", true, 15.00, 4.00, BoatHullType.MONOHULL, false), diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/LeaderboardType.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/LeaderboardType.java index 1d987b76b6a..665cb929e5f 100644 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/LeaderboardType.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/LeaderboardType.java @@ -1,9 +1,12 @@ package com.sap.sailing.domain.common; public enum LeaderboardType { - RegattaLeaderboard(false, true), RegattaMetaLeaderboard(true, true), + RegattaLeaderboard(false, true), + RegattaMetaLeaderboard(true, true), RegattaLeaderboardWithEliminations(false, true), - FlexibleLeaderboard(false, false), FlexibleMetaLeaderboard(true, false); + RegattaLeaderboardWithOtherTieBreakingLeaderboard(false, true), + FlexibleLeaderboard(false, false), + FlexibleMetaLeaderboard(true, false); boolean isRegattaLeaderboard; boolean isMetaLeaderboard; diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/ScoringSchemeType.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/ScoringSchemeType.java index 0b2c8f26219..1b522e26b8f 100755 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/ScoringSchemeType.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/ScoringSchemeType.java @@ -5,5 +5,14 @@ public enum ScoringSchemeType { LOW_POINT, LOW_POINT_WITH_AUTOMATIC_RDG, HIGH_POINT, HIGH_POINT_ESS_OVERALL,HIGH_POINT_ESS_OVERALL_12, HIGH_POINT_LAST_BREAKS_TIE, HIGH_POINT_FIRST_GETS_TEN, 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 + 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; + + public static double getScaledScore(double columnFactor, double unscaledScore, boolean oneAlwaysStaysOne) { + return unscaledScore * columnFactor - (oneAlwaysStaysOne ? columnFactor-1 : 0); + } + + public static double getUnscaledScore(double columnFactor, double scaledScore, boolean oneAlwaysStaysOne) { + return (scaledScore + (oneAlwaysStaysOne ? columnFactor - 1 : 0)) / columnFactor; + } } diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/AbstractLeaderboardDTO.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/AbstractLeaderboardDTO.java index 0ab3cfecb31..59955995146 100755 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/AbstractLeaderboardDTO.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/AbstractLeaderboardDTO.java @@ -36,6 +36,10 @@ public abstract class AbstractLeaderboardDTO extends NamedDTO implements Secured * Set to the non-null regatta name if this DTO represents a RegattaLeaderboard. */ public String regattaName; + /** + * Set if this DTO represents a {@code RegattaLeaderboardWithOtherTieBreakingLeaderboard} + */ + private String otherTieBreakingLeaderboardName; public String displayName; public List courseAreas; public ScoringSchemeType scoringScheme; @@ -111,6 +115,14 @@ public abstract class AbstractLeaderboardDTO extends NamedDTO implements Secured return competitorDisplayNames.get(competitor); } } + + public String getOtherTieBreakingLeaderboardName() { + return otherTieBreakingLeaderboardName; + } + + public void setOtherTieBreakingLeaderboardName(String otherTieBreakingLeaderboardName) { + this.otherTieBreakingLeaderboardName = otherTieBreakingLeaderboardName; + } /** * If the race column whose name is specified in raceColumnName has at least one competitor who has valid @@ -196,7 +208,6 @@ public abstract class AbstractLeaderboardDTO extends NamedDTO implements Secured * ensures that a fleet named fleetName is present. If it's not present yet, it's added to the race * column's fleet name list. The trackedRaceIdentifier and race are associated with the * column for the fleet identified by fleetName. - * * @param explicitFactor * factor by which to multiply the race column's points for the overall score; if null, the * default will be determined by whether or not the column is marked as medal race @@ -212,15 +223,16 @@ public abstract class AbstractLeaderboardDTO extends NamedDTO implements Secured * {@link RaceColumnInSeriesDTO} * @param fleetDTO * must not be null + * @param oneAlwaysStaysOne TODO */ public RaceColumnDTO addRace(String raceColumnName, Double explicitFactor, double effectiveFactor, String regattaName, String seriesName, FleetDTO fleetDTO, boolean medalRace, - RegattaAndRaceIdentifier trackedRaceIdentifier, RaceDTO race, boolean isMetaLeaderboardColumn) { + RegattaAndRaceIdentifier trackedRaceIdentifier, RaceDTO race, boolean isMetaLeaderboardColumn, boolean oneAlwaysStaysOne) { assert fleetDTO != null; RaceColumnDTO raceColumnDTO = getRaceColumnByName(raceColumnName); if (raceColumnDTO == null) { raceColumnDTO = RaceColumnDTOFactory.INSTANCE.createRaceColumnDTO(raceColumnName, medalRace, - explicitFactor, regattaName, seriesName, isMetaLeaderboardColumn); + explicitFactor, regattaName, seriesName, isMetaLeaderboardColumn, oneAlwaysStaysOne); races.add(raceColumnDTO); } raceColumnDTO.setEffectiveFactor(effectiveFactor); @@ -240,9 +252,9 @@ public abstract class AbstractLeaderboardDTO extends NamedDTO implements Secured } public RaceColumnDTO createEmptyRaceColumn(String raceColumnName, boolean medalRace, String regattaName, - String seriesName, boolean isMetaLeaderboardColumn) { + String seriesName, boolean isMetaLeaderboardColumn, boolean oneAlwaysStaysOne) { final RaceColumnDTO raceColumn = RaceColumnDTOFactory.INSTANCE.createRaceColumnDTO(raceColumnName, - medalRace, /* explicit factor */ null, regattaName, seriesName, isMetaLeaderboardColumn); + medalRace, /* explicit factor */ null, regattaName, seriesName, isMetaLeaderboardColumn, oneAlwaysStaysOne); races.add(raceColumn); return raceColumn; } @@ -437,6 +449,11 @@ public abstract class AbstractLeaderboardDTO extends NamedDTO implements Secured return false; } else if (!getName().equals(other.getName())) return false; + if (getOtherTieBreakingLeaderboardName() == null) { + if (other.getOtherTieBreakingLeaderboardName() != null) + return false; + } else if (!getOtherTieBreakingLeaderboardName().equals(other.getOtherTieBreakingLeaderboardName())) + return false; if (races == null) { if (other.races != null) return false; diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/MetaLeaderboardRaceColumnDTO.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/MetaLeaderboardRaceColumnDTO.java index 11dab2ba77b..4a14f3d7739 100644 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/MetaLeaderboardRaceColumnDTO.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/MetaLeaderboardRaceColumnDTO.java @@ -7,7 +7,7 @@ public class MetaLeaderboardRaceColumnDTO extends RaceColumnDTO { MetaLeaderboardRaceColumnDTO() {} // for GWT RPC serialization only public MetaLeaderboardRaceColumnDTO(String name) { - super(name); + super(name, /* oneAlwaysStaysOne */ false); } private static final long serialVersionUID = 6152752963316150432L; diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/RaceColumnDTO.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/RaceColumnDTO.java index dc0db35bd45..f6209459bce 100755 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/RaceColumnDTO.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/RaceColumnDTO.java @@ -23,6 +23,7 @@ public class RaceColumnDTO extends NamedDTO implements Serializable { private Map racesPerFleet; private Double explicitFactor; private double effectiveFactor; + private boolean oneAlwaysStaysOne; private Map raceLogTrackingInfos; public enum RaceColumnLiveState { NOT_TRACKED, TRACKED, TRACKED_AND_LIVE }; @@ -30,12 +31,18 @@ public class RaceColumnDTO extends NamedDTO implements Serializable { @Deprecated RaceColumnDTO() {} // for GWT RPC serialization only - public RaceColumnDTO(String name) { + /** + * @param oneAlwaysStaysOne + * tells whether when scaling results with the {@link #getEffectiveFactor() effective column factor}, the + * 1.0 score shall always remain 1.0. + */ + public RaceColumnDTO(String name, boolean oneAlwaysStaysOne) { super(name); trackedRaceIdentifiersPerFleet = new HashMap(); raceLogTrackingInfos = new HashMap(); racesPerFleet = new HashMap(); fleets = new ArrayList(); + this.oneAlwaysStaysOne = oneAlwaysStaysOne; } public RaceColumnLiveState getLiveState(long serverTimePointAsMillis) { @@ -59,6 +66,10 @@ public class RaceColumnDTO extends NamedDTO implements Serializable { public void setExplicitFactor(Double explicitFactor) { this.explicitFactor = explicitFactor; } + + public boolean isOneAlwaysStaysOne() { + return oneAlwaysStaysOne; + } public boolean hasTrackedRace(RaceIdentifier raceIdentifier) { return trackedRaceIdentifiersPerFleet.values().contains(raceIdentifier); diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/RaceColumnDTOFactory.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/RaceColumnDTOFactory.java index 47df2bbce1c..0b8136bd748 100755 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/RaceColumnDTOFactory.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/RaceColumnDTOFactory.java @@ -10,14 +10,21 @@ public class RaceColumnDTOFactory { * when null, a regular {@link RaceColumnDTO} or {@link MetaLeaderboardRaceColumnDTO} will * be produced (depending on isMetaLeaderboardColumn) and initialized from the properties * passed; otherwise, a {@link RaceColumnInSeriesDTO} will be created. + * @param oneAlwaysStaysOne + * When scores in this column are scaled by some factor, either based on the {@link #getExplicitFactor() + * explicit factor} set for this column, or implicitly, e.g., because the {@link ScoringScheme} mandates + * the doubling of medal race scores and this column {@link #isMedalRace() represents a medal race}, then + * some configurations still want the 1.0 score still to be 1.0. For example, with a column factor of 2.0 + * scores 1, 2, 3 would end up as 1, 3, 5; or with a column factor of 3.0 scores 1, 2, 3 would end up as + * 1, 4, 7. This method tells whether this column shall apply such a scheme. */ public RaceColumnDTO createRaceColumnDTO(String columnName, boolean isMedal, Double explicitFactor, - String regattaName, String seriesName, boolean isMetaLeaderboardColumn) { + String regattaName, String seriesName, boolean isMetaLeaderboardColumn, boolean oneAlwaysStaysOne) { final RaceColumnDTO raceColumnDTO; if (seriesName != null) { - raceColumnDTO = new RaceColumnInSeriesDTO(columnName, seriesName, regattaName); + raceColumnDTO = new RaceColumnInSeriesDTO(columnName, seriesName, regattaName, oneAlwaysStaysOne); } else { - raceColumnDTO = isMetaLeaderboardColumn ? new MetaLeaderboardRaceColumnDTO(columnName) : new RaceColumnDTO(columnName); + raceColumnDTO = isMetaLeaderboardColumn ? new MetaLeaderboardRaceColumnDTO(columnName) : new RaceColumnDTO(columnName, oneAlwaysStaysOne); } fillRaceColumnDTO(raceColumnDTO, isMedal, explicitFactor); return raceColumnDTO; diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/RaceColumnInSeriesDTO.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/RaceColumnInSeriesDTO.java index bdd6720bde7..fb269d1b7aa 100644 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/RaceColumnInSeriesDTO.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/RaceColumnInSeriesDTO.java @@ -8,8 +8,8 @@ public class RaceColumnInSeriesDTO extends RaceColumnDTO { @Deprecated RaceColumnInSeriesDTO() {} // for GWT serialization - public RaceColumnInSeriesDTO(String name, String seriesName, String regattaName) { - super(name); + public RaceColumnInSeriesDTO(String name, String seriesName, String regattaName, boolean oneAlwaysStaysOne) { + super(name, oneAlwaysStaysOne); this.seriesName = seriesName; this.regattaName = regattaName; } diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/SeriesCreationParametersDTO.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/SeriesCreationParametersDTO.java index e3e761c4d2f..9c4c0a179a9 100755 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/SeriesCreationParametersDTO.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/SeriesCreationParametersDTO.java @@ -24,10 +24,12 @@ public class SeriesCreationParametersDTO implements Serializable { private Integer maximumNumberOfDiscards; + private boolean oneAlwaysStaysOne; + SeriesCreationParametersDTO() {} public SeriesCreationParametersDTO(List fleets, boolean isMedal, boolean isFleetsCanRunInParallel, boolean isStartsWithZeroScore, boolean firstColumnIsNonDiscardableCarryForward, - int[] discardingThresholds, boolean hasSplitFleetContiguousScoring, boolean hasCrossFleetMergedRanking, Integer maximumNumberOfDiscards) { + int[] discardingThresholds, boolean hasSplitFleetContiguousScoring, boolean hasCrossFleetMergedRanking, Integer maximumNumberOfDiscards, boolean oneAlwaysStaysOne) { super(); this.fleets = fleets; this.isMedal = isMedal; @@ -38,6 +40,7 @@ public class SeriesCreationParametersDTO implements Serializable { this.firstColumnIsNonDiscardableCarryForward = firstColumnIsNonDiscardableCarryForward; this.discardingThresholds = discardingThresholds; this.maximumNumberOfDiscards = maximumNumberOfDiscards; + this.oneAlwaysStaysOne = oneAlwaysStaysOne; } public List getFleets() { @@ -75,4 +78,8 @@ public class SeriesCreationParametersDTO implements Serializable { public Integer getMaximumNumberOfDiscards() { return maximumNumberOfDiscards; } + + public boolean isOneAlwaysStaysOne() { + return oneAlwaysStaysOne; + } } diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/FieldNames.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/FieldNames.java index 3f55109099f..75fae49f4c7 100644 --- a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/FieldNames.java +++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/FieldNames.java @@ -50,6 +50,7 @@ public enum FieldNames { SERIES_HAS_SPLIT_FLEET_CONTIGUOUS_SCORING, SERIES_HAS_CROSS_FLEET_MERGED_RANKING, SERIES_STARTS_WITH_NON_DISCARDABLE_CARRY_FORWARD, + SERIES_ONE_ALWAYS_STAYS_ONE, SERIES_FLEETS, SERIES_RACE_COLUMNS, SERIES_DISCARDING_THRESHOLDS, @@ -88,7 +89,7 @@ public enum FieldNames { LEADERBOARD_SCORE_CORRECTIONS, LEADERBOARD_DISCARDING_THRESHOLDS, LEADERBOARD_SCORE_CORRECTION_MAX_POINTS_REASON, LEADERBOARD_CORRECTED_SCORE, LEADERBOARD_SCORE_CORRECTION_TIMESTAMP, LEADERBOARD_RANK, LEADERBOARD_SCORE_CORRECTION_COMMENT, LEADERBOARD_SCORE_CORRECTION_MERGE_STATE, - LEADERBOARD_COLUMN_FACTORS, WRAPPED_REGATTA_LEADERBOARD_NAME, + LEADERBOARD_COLUMN_FACTORS, WRAPPED_REGATTA_LEADERBOARD_NAME, OTHER_TIEBREAKING_LEADERBOARD_NAME, ELMINATED_COMPETITORS, // leaderboard group diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/DomainObjectFactoryImpl.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/DomainObjectFactoryImpl.java index d22e3dfd8a7..d57538834b2 100644 --- a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/DomainObjectFactoryImpl.java +++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/DomainObjectFactoryImpl.java @@ -233,6 +233,7 @@ import com.sap.sailing.domain.leaderboard.impl.DelegatingRegattaLeaderboardWithC import com.sap.sailing.domain.leaderboard.impl.FlexibleLeaderboardImpl; import com.sap.sailing.domain.leaderboard.impl.LeaderboardGroupImpl; import com.sap.sailing.domain.leaderboard.impl.RegattaLeaderboardImpl; +import com.sap.sailing.domain.leaderboard.impl.RegattaLeaderboardWithOtherTieBreakingLeaderboardImpl; import com.sap.sailing.domain.leaderboard.impl.ThresholdBasedResultDiscardingRuleImpl; import com.sap.sailing.domain.leaderboard.meta.LeaderboardGroupMetaLeaderboard; import com.sap.sailing.domain.persistence.DomainObjectFactory; @@ -491,7 +492,7 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { result = leaderboardRegistry.getLeaderboardByName(leaderboardName); } if (result == null) { - String wrappedRegattaLeaderboardName = (String) dbLeaderboard + final String wrappedRegattaLeaderboardName = (String) dbLeaderboard .get(FieldNames.WRAPPED_REGATTA_LEADERBOARD_NAME.name()); if (wrappedRegattaLeaderboardName != null) { result = loadRegattaLeaderboardWithEliminations(dbLeaderboard, leaderboardName, @@ -510,7 +511,7 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { result = loadFlexibleLeaderboard(dbLeaderboard, resultDiscardingRule); } else { result = loadRegattaLeaderboard(leaderboardName, regattaName, dbLeaderboard, - resultDiscardingRule, regattaRegistry); + resultDiscardingRule, regattaRegistry, leaderboardRegistry); } } if (result != null) { @@ -590,14 +591,21 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { */ private RegattaLeaderboard loadRegattaLeaderboard(String leaderboardName, String regattaName, Document dbLeaderboard, ThresholdBasedResultDiscardingRule resultDiscardingRule, - RegattaRegistry regattaRegistry) { - RegattaLeaderboard result = null; + RegattaRegistry regattaRegistry, LeaderboardRegistry leaderboardRegistry) { + final RegattaLeaderboard result; Regatta regatta = regattaRegistry.getRegatta(new RegattaName(regattaName)); if (regatta == null) { logger.info("Couldn't find regatta " + regattaName + " for corresponding regatta leaderboard. Not loading regatta leaderboard."); + result = null; } else { - result = new RegattaLeaderboardImpl(regatta, resultDiscardingRule); + final String otherTieBreakingLeaderboardName = (String) dbLeaderboard.get(FieldNames.OTHER_TIEBREAKING_LEADERBOARD_NAME.name()); + if (otherTieBreakingLeaderboardName == null) { + result = new RegattaLeaderboardImpl(regatta, resultDiscardingRule); + } else { + result = new RegattaLeaderboardWithOtherTieBreakingLeaderboardImpl(regatta, resultDiscardingRule, + () -> (RegattaLeaderboard) leaderboardRegistry.getLeaderboardByName(otherTieBreakingLeaderboardName)); + } } return result; } @@ -1426,13 +1434,15 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { final Number maximumNumberOfDiscardsAsObject = (Number) dbSeries .get(FieldNames.SERIES_MAXIMUM_NUMBER_OF_DISCARDS.name()); final Integer maximumNumberOfDiscards = maximumNumberOfDiscardsAsObject == null ? null : maximumNumberOfDiscardsAsObject.intValue(); - Boolean startsWithZeroScore = (Boolean) dbSeries.get(FieldNames.SERIES_STARTS_WITH_ZERO_SCORE.name()); - Boolean hasSplitFleetContiguousScoring = (Boolean) dbSeries + final Boolean startsWithZeroScore = (Boolean) dbSeries.get(FieldNames.SERIES_STARTS_WITH_ZERO_SCORE.name()); + final Boolean hasSplitFleetContiguousScoring = (Boolean) dbSeries .get(FieldNames.SERIES_HAS_SPLIT_FLEET_CONTIGUOUS_SCORING.name()); - Boolean hasCrossFleetMergedRankingObject = (Boolean) dbSeries + + final Boolean hasCrossFleetMergedRankingObject = (Boolean) dbSeries .get(FieldNames.SERIES_HAS_CROSS_FLEET_MERGED_RANKING.name()); - Boolean firstColumnIsNonDiscardableCarryForward = (Boolean) dbSeries + final Boolean firstColumnIsNonDiscardableCarryForward = (Boolean) dbSeries .get(FieldNames.SERIES_STARTS_WITH_NON_DISCARDABLE_CARRY_FORWARD.name()); + final Boolean oneAlwaysStaysOne = (Boolean) dbSeries.get(FieldNames.SERIES_ONE_ALWAYS_STAYS_ONE.name()); @SuppressWarnings("unchecked") final Iterable dbFleets = (Iterable) dbSeries.get(FieldNames.SERIES_FLEETS.name()); List fleets = loadFleets(dbFleets); @@ -1459,6 +1469,9 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { if (firstColumnIsNonDiscardableCarryForward != null) { series.setFirstColumnIsNonDiscardableCarryForward(firstColumnIsNonDiscardableCarryForward); } + if (oneAlwaysStaysOne != null) { + series.setOneAlwaysStaysOne(oneAlwaysStaysOne); + } loadRaceColumnRaceLinks(dbRaceColumns, series); return series; } diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/MongoObjectFactoryImpl.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/MongoObjectFactoryImpl.java index ec681e0709d..cab59630d56 100644 --- a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/MongoObjectFactoryImpl.java +++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/MongoObjectFactoryImpl.java @@ -122,6 +122,7 @@ import com.sap.sailing.domain.leaderboard.Leaderboard; import com.sap.sailing.domain.leaderboard.LeaderboardGroup; import com.sap.sailing.domain.leaderboard.RegattaLeaderboard; import com.sap.sailing.domain.leaderboard.RegattaLeaderboardWithEliminations; +import com.sap.sailing.domain.leaderboard.RegattaLeaderboardWithOtherTieBreakingLeaderboard; import com.sap.sailing.domain.leaderboard.ResultDiscardingRule; import com.sap.sailing.domain.leaderboard.SettableScoreCorrection; import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule; @@ -399,6 +400,10 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory { private void storeRegattaLeaderboard(RegattaLeaderboard leaderboard, Document dbLeaderboard) { dbLeaderboard.put(FieldNames.REGATTA_NAME.name(), leaderboard.getRegatta().getName()); + if (leaderboard instanceof RegattaLeaderboardWithOtherTieBreakingLeaderboard) { + dbLeaderboard.put(FieldNames.OTHER_TIEBREAKING_LEADERBOARD_NAME.name(), + ((RegattaLeaderboardWithOtherTieBreakingLeaderboard) leaderboard).getOtherTieBreakingLeaderboard().getName()); + } } private void storeFlexibleLeaderboard(FlexibleLeaderboard leaderboard, Document dbLeaderboard) { @@ -799,6 +804,7 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory { dbSeries.put(FieldNames.SERIES_HAS_CROSS_FLEET_MERGED_RANKING.name(), s.hasCrossFleetMergedRanking()); dbSeries.put(FieldNames.SERIES_STARTS_WITH_ZERO_SCORE.name(), s.isStartsWithZeroScore()); dbSeries.put(FieldNames.SERIES_STARTS_WITH_NON_DISCARDABLE_CARRY_FORWARD.name(), s.isFirstColumnIsNonDiscardableCarryForward()); + dbSeries.put(FieldNames.SERIES_ONE_ALWAYS_STAYS_ONE.name(), s.isOneAlwaysStaysOne()); BasicDBList dbFleets = new BasicDBList(); for (Fleet fleet : s.getFleets()) { dbFleets.add(storeFleet(fleet)); diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/common/test/LeaderboardDTODiffingTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/common/test/LeaderboardDTODiffingTest.java index 5f5db19d233..c63aecd6d25 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/common/test/LeaderboardDTODiffingTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/common/test/LeaderboardDTODiffingTest.java @@ -410,7 +410,7 @@ public class LeaderboardDTODiffingTest { public void testPartialRaceColumnDTOCompaction() throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException { // create a modified R9 RaceDTO clone in newVersion to make sure that even changing a property in the RaceDTO will keep the RaceColumnDTO from being omitted RaceColumnDTO r9 = newVersion.getRaceColumnByName("R9"); - RaceColumnDTO clonedR9 = new RaceColumnDTO("R9"); + RaceColumnDTO clonedR9 = new RaceColumnDTO("R9", /* oneAlwaysStaysOne */ false); cloner.clone(r9, clonedR9); // also clone the racesPerFleet map, or else we'd be modifying the previous version's one too final Field racesPerFleetField = clonedR9.getClass().getDeclaredField("racesPerFleet"); diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/common/test/ScoringSchemeTypeScoreScalingTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/common/test/ScoringSchemeTypeScoreScalingTest.java new file mode 100644 index 00000000000..f0f1be0f04e --- /dev/null +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/common/test/ScoringSchemeTypeScoreScalingTest.java @@ -0,0 +1,49 @@ +package com.sap.sailing.domain.common.test; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +import com.sap.sailing.domain.common.ScoringSchemeType; + +public class ScoringSchemeTypeScoreScalingTest { + private static final double DELTA = 0.0000001; + + @Test + public void testScalingThreeWithThree() { + testScalingAndUnscaling(/* unscaled */ 3, /* factor */ 3, /* scaled */ 9, /* oneAlwaysStaysOne */ false); + } + + @Test + public void testScalingOneWithThree() { + testScalingAndUnscaling(/* unscaled */ 1, /* factor */ 3, /* scaled */ 3, /* oneAlwaysStaysOne */ false); + } + + @Test + public void testScalingThreeWithTwo() { + testScalingAndUnscaling(/* unscaled */ 3, /* factor */ 2, /* scaled */ 6, /* oneAlwaysStaysOne */ false); + } + + @Test + public void testScalingOneWithTwo() { + testScalingAndUnscaling(/* unscaled */ 1, /* factor */ 2, /* scaled */ 2, /* oneAlwaysStaysOne */ false); + } + + @Test + public void testScalingWithThreeWithOneStaysOne() { + testScalingAndUnscaling(/* unscaled */ 3, /* factor */ 3, /* scaled */ 7, /* oneAlwaysStaysOne */ true); + testScalingAndUnscaling(/* unscaled */ 1, /* factor */ 3, /* scaled */ 1, /* oneAlwaysStaysOne */ true); + } + + @Test + public void testScalingWithTwoWithOneStaysOne() { + testScalingAndUnscaling(/* unscaled */ 3, /* factor */ 2, /* scaled */ 5, /* oneAlwaysStaysOne */ true); + testScalingAndUnscaling(/* unscaled */ 1, /* factor */ 2, /* scaled */ 1, /* oneAlwaysStaysOne */ true); + } + + private void testScalingAndUnscaling(final double unscaled, final double factor, final double scaled, + final boolean oneAlwaysStaysOne) { + assertEquals(scaled, ScoringSchemeType.getScaledScore(factor, unscaled, oneAlwaysStaysOne), DELTA); + assertEquals(unscaled, ScoringSchemeType.getUnscaledScore(factor, scaled, oneAlwaysStaysOne), DELTA); + } +} diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LeaderboardScoringAndRankingTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LeaderboardScoringAndRankingTest.java index 847e90fc4d4..5b72025e4e2 100644 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LeaderboardScoringAndRankingTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LeaderboardScoringAndRankingTest.java @@ -59,7 +59,6 @@ import com.sap.sailing.domain.common.dto.LeaderboardDTO; import com.sap.sailing.domain.leaderboard.FlexibleLeaderboard; import com.sap.sailing.domain.leaderboard.Leaderboard; import com.sap.sailing.domain.leaderboard.LeaderboardGroup; -import com.sap.sailing.domain.leaderboard.impl.DelegatingRegattaLeaderboardWithCompetitorElimination; import com.sap.sailing.domain.leaderboard.impl.FlexibleLeaderboardImpl; import com.sap.sailing.domain.leaderboard.impl.HighPoint; import com.sap.sailing.domain.leaderboard.impl.HighPointExtremeSailingSeriesOverall; @@ -87,11 +86,6 @@ import com.sap.sse.common.impl.MillisecondsTimePoint; import com.sap.sse.shared.util.impl.ArrayListNavigableSet; public class LeaderboardScoringAndRankingTest extends LeaderboardScoringAndRankingTestBase { - protected DelegatingRegattaLeaderboardWithCompetitorElimination createDelegatingRegattaLeaderboardWithCompetitorElimination( - Regatta regatta, String leaderboardName, int[] discardingThresholds) { - return new DelegatingRegattaLeaderboardWithCompetitorElimination(()->createLeaderboard(regatta, discardingThresholds), leaderboardName); - } - @Test public void testOneStartedRaceWithDifferentScores() throws NoWindException { List competitors = createCompetitors(10); diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LeaderboardScoringAndRankingTestForLowPoints.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LeaderboardScoringAndRankingTestForLowPoints.java index d28c45afd9b..22829e179f9 100644 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LeaderboardScoringAndRankingTestForLowPoints.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LeaderboardScoringAndRankingTestForLowPoints.java @@ -23,7 +23,6 @@ import com.sap.sailing.domain.common.CompetitorRegistrationType; import com.sap.sailing.domain.common.NoWindException; import com.sap.sailing.domain.common.ScoringSchemeType; import com.sap.sailing.domain.leaderboard.Leaderboard; -import com.sap.sailing.domain.leaderboard.impl.DelegatingRegattaLeaderboardWithCompetitorElimination; import com.sap.sailing.domain.leaderboard.impl.LowPointFirstToWinTwoRaces; import com.sap.sailing.domain.ranking.OneDesignRankingMetric; import com.sap.sailing.domain.test.mock.MockedTrackedRaceWithStartTimeAndRanks; @@ -42,12 +41,6 @@ import com.sap.sse.common.impl.MillisecondsTimePoint; public class LeaderboardScoringAndRankingTestForLowPoints extends LeaderboardScoringAndRankingTestBase { private static final double EPSILON = 0.000001; - protected DelegatingRegattaLeaderboardWithCompetitorElimination createDelegatingRegattaLeaderboardWithCompetitorElimination( - Regatta regatta, String leaderboardName, int[] discardingThresholds) { - return new DelegatingRegattaLeaderboardWithCompetitorElimination( - () -> createLeaderboard(regatta, discardingThresholds), leaderboardName); - } - private void executePreSeries(List yellow, List blue, TimePoint now) { RaceColumn qColumn = series.get(0).getRaceColumnByName("Q"); TrackedRace qYellow = new MockedTrackedRaceWithStartTimeAndRanks(now, yellow); diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/RaceColumn.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/RaceColumn.java index 0db1b5f6e63..3b89d7e728b 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/RaceColumn.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/RaceColumn.java @@ -11,6 +11,7 @@ import com.sap.sailing.domain.common.abstractlog.NotRevokableException; import com.sap.sailing.domain.common.racelog.tracking.CompetitorRegistrationOnRaceLogDisabledException; import com.sap.sailing.domain.leaderboard.Leaderboard; import com.sap.sailing.domain.leaderboard.MetaLeaderboard; +import com.sap.sailing.domain.leaderboard.ScoringScheme; import com.sap.sailing.domain.racelog.RaceLogIdentifier; import com.sap.sailing.domain.racelog.RaceLogStore; import com.sap.sailing.domain.regattalike.RegattaLikeIdentifier; @@ -195,10 +196,20 @@ public interface RaceColumn extends Named { void setFactor(Double factor); /** - * If null, the {@link #getFactor() factor} defaults to 1 for non-medal and {@link #DEFAULT_MEDAL_RACE_FACTOR} for - * medal races. Otherwise, the explicit factor is used. + * {@link ScoringScheme}s shall prefer this explicit factor, if not null, and otherwise determine a default + * based on the rules of the scoring scheme, e.g., defaulting to 1 for non-medal and {@link #DEFAULT_MEDAL_RACE_FACTOR} for + * medal races, or similar. */ Double getExplicitFactor(); + + /** + * When scores in this column are scaled by some factor, either based on the {@link #getExplicitFactor() explicit factor} + * set for this column, or implicitly, e.g., because the {@link ScoringScheme} mandates the doubling of medal race scores + * and this column {@link #isMedalRace() represents a medal race}, then some configurations still want the 1.0 score still + * to be 1.0. For example, with a column factor of 2.0 scores 1, 2, 3 would end up as 1, 3, 5; or with a column factor of + * 3.0 scores 1, 2, 3 would end up as 1, 4, 7. This method tells whether this column shall apply such a scheme. + */ + boolean isOneAlwaysStaysOne(); /** * Returns the race log identifier associated with this fleet and race log diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/RaceColumnListener.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/RaceColumnListener.java index 8c93db7610e..aac3c7f17f0 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/RaceColumnListener.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/RaceColumnListener.java @@ -25,6 +25,8 @@ public interface RaceColumnListener extends Serializable { void hasCrossFleetMergedRankingChanged(RaceColumn raceColumn, boolean hasCrossFleetMergedRanking); + void oneAlwaysStaysOneChanged(RaceColumn raceColumn, boolean oneAlwaysStaysOne); + default boolean canAddRaceColumnToContainer(RaceColumn raceColumn) { return true; } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/Series.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/Series.java index cdff73ba048..68fa98a8147 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/Series.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/Series.java @@ -1,5 +1,6 @@ package com.sap.sailing.domain.base; +import com.sap.sailing.domain.leaderboard.ScoringScheme; import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule; import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.domain.tracking.TrackedRegatta; @@ -108,6 +109,9 @@ public interface Series extends SeriesBase { */ boolean isStartsWithZeroScore(); + /** + * @see #isStartsWithZeroScore() + */ void setStartsWithZeroScore(boolean startsWithZeroScore); boolean isFirstColumnIsNonDiscardableCarryForward(); @@ -122,6 +126,9 @@ public interface Series extends SeriesBase { */ boolean hasSplitFleetContiguousScoring(); + /** + * @see #hasSplitFleetContiguousScoring() + */ void setSplitFleetContiguousScoring(boolean hasSplitFleetScore); /** @@ -135,4 +142,18 @@ public interface Series extends SeriesBase { void setCrossFleetMergedRanking(boolean hasCrossFleetMergedRanking); + /** + * When scores in this series are scaled by some factor, either based on an {@link RaceColumn#getExplicitFactor() + * explicit column factor}, or implicitly, e.g., because the {@link ScoringScheme} mandates the + * doubling of medal race scores and this series {@link #isMedal() represents a medal series}, then some + * configurations still want the 1.0 score still to be 1.0. For example, with a column factor of 2.0 scores 1, 2, 3 + * would end up as 1, 3, 5; or with a column factor of 3.0 scores 1, 2, 3 would end up as 1, 4, 7. This method tells + * whether this column shall apply such a scheme. + */ + boolean isOneAlwaysStaysOne(); + + /** + * @see #isOneAlwaysStaysOne() + */ + void setOneAlwaysStaysOne(boolean oneAlwaysStaysOne); } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/DomainFactoryImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/DomainFactoryImpl.java index 3576fa1110c..6b3f43e23da 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/DomainFactoryImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/DomainFactoryImpl.java @@ -42,6 +42,7 @@ import com.sap.sailing.domain.common.tracking.GPSFixMoving; import com.sap.sailing.domain.leaderboard.Leaderboard; import com.sap.sailing.domain.leaderboard.ScoringScheme; import com.sap.sailing.domain.leaderboard.impl.HighPoint; +import com.sap.sailing.domain.leaderboard.impl.HighPointByWinsTiesLastlyBrokenByOtherLeaderboard; import com.sap.sailing.domain.leaderboard.impl.HighPointExtremeSailingSeriesOverall; import com.sap.sailing.domain.leaderboard.impl.HighPointExtremeSailingSeriesOverall12PointsMax; import com.sap.sailing.domain.leaderboard.impl.HighPointFirstGets10LastBreaksTie; @@ -155,6 +156,8 @@ public class DomainFactoryImpl extends SharedDomainFactoryImplfalse which is also the default. */ private boolean hasSplitFleetContiguousScoring; - + /** * When a column has more than one fleet there are two different options for scoring it when the fleets are of the * same rank. Either the scoring scheme is applied to both fleets at the same time and competitors compete across @@ -79,8 +80,8 @@ public class SeriesImpl extends RenamableImpl implements Series, RaceColumnListe * the column as many times as there are fleets in the column. For the latter case, this field is false * which is also the default. */ - private boolean hasCrossFleetMergedRanking; - + private boolean hasCrossFleetMergedRanking; + /** * @param fleets * must be non-empty @@ -106,7 +107,7 @@ public class SeriesImpl extends RenamableImpl implements Series, RaceColumnListe Collections.sort(fleetsInAscendingOrder); this.raceColumns = new ArrayList(); this.isMedal = isMedal; - this.isFleetsCanRunInParallel = isFleetsCanRunInParallel; + this.isFleetsCanRunInParallel = isFleetsCanRunInParallel; this.raceColumnListeners = new RaceColumnListeners(); for (String raceColumnName : raceColumnNames) { addRaceColumn(raceColumnName, trackedRegattaRegistry); @@ -122,7 +123,7 @@ public class SeriesImpl extends RenamableImpl implements Series, RaceColumnListe public void removeRaceColumnListener(RaceColumnListener listener) { raceColumnListeners.removeRaceColumnListener(listener); } - + @Override public Regatta getRegatta() { return regatta; @@ -167,7 +168,7 @@ public class SeriesImpl extends RenamableImpl implements Series, RaceColumnListe public RaceColumnInSeries addRaceColumn(String raceColumnName, TrackedRegattaRegistry trackedRegattaRegistry) { return addRaceColumn(raceColumns.size(), raceColumnName, trackedRegattaRegistry); } - + @Override public RaceColumnInSeries addRaceColumn(int insertIndex, String raceColumnName, TrackedRegattaRegistry trackedRegattaRegistry) { RaceColumnInSeriesImpl result = createRaceColumn(raceColumnName, trackedRegattaRegistry); @@ -191,11 +192,11 @@ public class SeriesImpl extends RenamableImpl implements Series, RaceColumnListe */ private RaceColumnInSeriesImpl createRaceColumn(String raceColumnName, TrackedRegattaRegistry trackedRegattaRegistry) { return new RaceColumnInSeriesImpl( - raceColumnName, - this, + raceColumnName, + this, trackedRegattaRegistry); } - + private void attachRaceExecutionOrderProviderToTrackedRacesInRaceColumns() { for (RaceColumnInSeries raceColumnInSeries : raceColumns) { for (Fleet fleet : raceColumnInSeries.getFleets()) { @@ -206,7 +207,7 @@ public class SeriesImpl extends RenamableImpl implements Series, RaceColumnListe } } } - + private void detachRaceExecutionOrderProviderFromTrackedRacesInRaceColumns() { for (RaceColumnInSeries raceColumnInSeries : raceColumns) { for (Fleet fleet : raceColumnInSeries.getFleets()) { @@ -334,6 +335,11 @@ public class SeriesImpl extends RenamableImpl implements Series, RaceColumnListe raceColumnListeners.notifyListenersAboutFactorChanged(raceColumn, oldFactor, newFactor); } + @Override + public void oneAlwaysStaysOneChanged(RaceColumn raceColumn, boolean oneAlwaysStaysOne) { + raceColumnListeners.notifyListenersAboutOneAlwaysStaysOneChanged(raceColumn, oneAlwaysStaysOne); + } + @Override public void competitorDisplayNameChanged(Competitor competitor, String oldDisplayName, String displayName) { raceColumnListeners.notifyListenersAboutCompetitorDisplayNameChanged(competitor, oldDisplayName, displayName); @@ -409,13 +415,13 @@ public class SeriesImpl extends RenamableImpl implements Series, RaceColumnListe } return result; } - + @Override public void setStartsWithZeroScore(boolean startsWithZeroScore) { boolean oldStartsWithZeroScore = this.startsWithZeroScore; if (oldStartsWithZeroScore != startsWithZeroScore) { this.startsWithZeroScore = startsWithZeroScore; - RaceColumn firstRaceColumnInSeries = getFirstRaceColumn(); + final RaceColumn firstRaceColumnInSeries = getFirstRaceColumn(); if (firstRaceColumnInSeries != null) { raceColumnListeners.notifyListenersAboutIsStartsWithZeroScoreChanged(firstRaceColumnInSeries, startsWithZeroScore); } @@ -441,7 +447,7 @@ public class SeriesImpl extends RenamableImpl implements Series, RaceColumnListe } } } - + @Override public boolean isFirstColumnIsNonDiscardableCarryForward() { return firstColumnIsNonDiscardableCarryForward; @@ -452,7 +458,7 @@ public class SeriesImpl extends RenamableImpl implements Series, RaceColumnListe boolean oldFirstColumnIsNonDiscardableCarryForward = this.firstColumnIsNonDiscardableCarryForward; if (oldFirstColumnIsNonDiscardableCarryForward != firstColumnIsNonDiscardableCarryForward) { this.firstColumnIsNonDiscardableCarryForward = firstColumnIsNonDiscardableCarryForward; - RaceColumn firstRaceColumnInSeries = getFirstRaceColumn(); + final RaceColumn firstRaceColumnInSeries = getFirstRaceColumn(); if (firstRaceColumnInSeries != null) { raceColumnListeners.notifyListenersAboutIsFirstColumnIsNonDiscardableCarryForwardChanged(firstRaceColumnInSeries, firstColumnIsNonDiscardableCarryForward); } @@ -469,4 +475,20 @@ public class SeriesImpl extends RenamableImpl implements Series, RaceColumnListe public boolean hasCrossFleetMergedRanking() { return hasCrossFleetMergedRanking; } + + @Override + public boolean isOneAlwaysStaysOne() { + return oneAlwaysStaysOne; + } + + @Override + public void setOneAlwaysStaysOne(boolean oneAlwaysStaysOne) { + boolean oldOneAlwaysStaysOne = this.oneAlwaysStaysOne; + if (oldOneAlwaysStaysOne != oneAlwaysStaysOne) { + this.oneAlwaysStaysOne = oneAlwaysStaysOne; + for (RaceColumn raceColumn : getRaceColumns()) { + raceColumnListeners.notifyListenersAboutOneAlwaysStaysOneChanged(raceColumn, oneAlwaysStaysOne); + } + } + } } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/AbstractScoreCorrectionListenerWithDefaultAction.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/AbstractScoreCorrectionListenerWithDefaultAction.java new file mode 100644 index 00000000000..109bc3ae9aa --- /dev/null +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/AbstractScoreCorrectionListenerWithDefaultAction.java @@ -0,0 +1,44 @@ +package com.sap.sailing.domain.leaderboard; + +import com.sap.sailing.domain.base.Competitor; +import com.sap.sailing.domain.base.RaceColumn; +import com.sap.sailing.domain.common.MaxPointsReason; +import com.sap.sse.common.TimePoint; + +public abstract class AbstractScoreCorrectionListenerWithDefaultAction implements ScoreCorrectionListener { + + @Override + public void correctedScoreChanged(Competitor competitor, RaceColumn raceColumn, Double oldCorrectedScore, + Double newCorrectedScore) { + defaultAction(); + } + + @Override + public void maxPointsReasonChanged(Competitor competitor, RaceColumn raceColumn, MaxPointsReason oldMaxPointsReason, + MaxPointsReason newMaxPointsReason) { + defaultAction(); + } + + @Override + public void carriedPointsChanged(Competitor competitor, Double oldCarriedPoints, Double newCarriedPoints) { + defaultAction(); + } + + @Override + public void isSuppressedChanged(Competitor competitor, boolean newIsSuppressed) { + defaultAction(); + } + + @Override + public void timePointOfLastCorrectionsValidityChanged(TimePoint oldTimePointOfLastCorrectionsValidity, + TimePoint newTimePointOfLastCorrectionsValidity) { + defaultAction(); + } + + @Override + public void commentChanged(String oldComment, String newComment) { + defaultAction(); + } + + abstract protected void defaultAction(); +} diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/FlexibleRaceColumn.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/FlexibleRaceColumn.java index 5bd22ab3b8b..7e80866dc2e 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/FlexibleRaceColumn.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/FlexibleRaceColumn.java @@ -6,5 +6,14 @@ import com.sap.sse.common.Renamable; public interface FlexibleRaceColumn extends RaceColumn, Renamable { void setIsMedalRace(boolean isMedalRace); + void setRegattaLikeHelper(IsRegattaLike regattaLikeHelper); + + /** + * Flexible race columns always apply scoring factors linearly and regularly. + */ + @Override + default boolean isOneAlwaysStaysOne() { + return false; + } } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/Leaderboard.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/Leaderboard.java index 79bb92e3ec0..e603e9a9240 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/Leaderboard.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/Leaderboard.java @@ -465,7 +465,7 @@ public interface Leaderboard extends LeaderboardBase, HasRaceColumns { * Returns the total rank of the given competitor or {@code 0} if no rank can be determined for * the {@code competitor} in this leaderboard. */ - int getTotalRankOfCompetitor(Competitor competitor, TimePoint timePoint) throws NoWindException; + int getTotalRankOfCompetitor(Competitor competitor, TimePoint timePoint); /** * Fetches all entries for all competitors of all races tracked by this leaderboard in one sweep. This saves some diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/LeaderboardCacheManager.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/LeaderboardCacheManager.java index eed2da720d6..f3c9813ad16 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/LeaderboardCacheManager.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/LeaderboardCacheManager.java @@ -20,21 +20,19 @@ import com.sap.sailing.domain.base.RaceColumn; import com.sap.sailing.domain.base.RaceColumnListener; import com.sap.sailing.domain.base.WithNationality; import com.sap.sailing.domain.base.impl.RaceColumnListenerWithDefaultAction; -import com.sap.sailing.domain.common.MaxPointsReason; import com.sap.sailing.domain.leaderboard.caching.LeaderboardCache; import com.sap.sailing.domain.tracking.RaceChangeListener; import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.domain.tracking.impl.AbstractRaceChangeListener; import com.sap.sse.common.Color; import com.sap.sse.common.Duration; -import com.sap.sse.common.TimePoint; import com.sap.sse.concurrent.ConcurrentWeakHashMap; import com.sap.sse.concurrent.LockUtil; import com.sap.sse.concurrent.NamedReentrantReadWriteLock; /** * Manages a {@link LeaderboardCache}. When a {@link Leaderboard} is {@link LeaderboardCache#add added} to the cache, it - * start observing the leaderboard for changes through the linked {@link TrackedRace}s as a {@link RaceChangeListener} + * starts observing the leaderboard for changes through the linked {@link TrackedRace}s as a {@link RaceChangeListener} * and through the race columns as a {@link RaceColumnListener}. When changes affecting a leaderboard occur, the * {@link LeaderboardCache#removeFromCache(Leaderboard)} method is called on the leaderboard cache. * @@ -200,7 +198,7 @@ public class LeaderboardCacheManager { } } - private class CacheInvalidationUponScoreCorrectionListener implements ScoreCorrectionListener { + private class CacheInvalidationUponScoreCorrectionListener extends AbstractScoreCorrectionListenerWithDefaultAction { private final Leaderboard leaderboard; public CacheInvalidationUponScoreCorrectionListener(Leaderboard leaderboard) { @@ -208,34 +206,7 @@ public class LeaderboardCacheManager { } @Override - public void correctedScoreChanged(Competitor competitor, RaceColumn raceColumn, Double oldCorrectedScore, Double newCorrectedScore) { - removeFromCache(leaderboard); - } - - @Override - public void maxPointsReasonChanged(Competitor competitor, RaceColumn raceColumn, - MaxPointsReason oldMaxPointsReason, MaxPointsReason newMaxPointsReason) { - removeFromCache(leaderboard); - } - - @Override - public void carriedPointsChanged(Competitor competitor, Double oldCarriedPoints, Double newCarriedPoints) { - removeFromCache(leaderboard); - } - - @Override - public void isSuppressedChanged(Competitor competitor, boolean newIsSuppressed) { - removeFromCache(leaderboard); - } - - @Override - public void timePointOfLastCorrectionsValidityChanged(TimePoint oldTimePointOfLastCorrectionsValidity, - TimePoint newTimePointOfLastCorrectionsValidity) { - removeFromCache(leaderboard); - } - - @Override - public void commentChanged(String oldComment, String newComment) { + public void defaultAction() { removeFromCache(leaderboard); } } @@ -374,12 +345,12 @@ public class LeaderboardCacheManager { trackedRace.addListener(listener); invalidationListeners = invalidationListenersPerLeaderboard.get(leaderboard); if (invalidationListeners == null) { - invalidationListeners = new ConcurrentHashMap>(); + invalidationListeners = new ConcurrentHashMap<>(); invalidationListenersPerLeaderboard.put(leaderboard, invalidationListeners); } Set listeners = invalidationListeners.get(trackedRace); if (listeners == null) { - listeners = Collections.synchronizedSet(new HashSet()); + listeners = Collections.synchronizedSet(new HashSet<>()); invalidationListeners.put(trackedRace, listeners); } listeners.add(listener); diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/RegattaLeaderboardWithOtherTieBreakingLeaderboard.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/RegattaLeaderboardWithOtherTieBreakingLeaderboard.java new file mode 100644 index 00000000000..b21d85f94ba --- /dev/null +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/RegattaLeaderboardWithOtherTieBreakingLeaderboard.java @@ -0,0 +1,21 @@ +package com.sap.sailing.domain.leaderboard; + +/** + * A regatta leaderboard that may have to resort to the competitor standings in another leaderboard + * for breaking specific ties. This {@link #getOtherTieBreakingLeaderboard() other leaderboard} can, + * e.g., be obtained by a {@link ScoringScheme} which then looks up scores or ranks in that leaderboard + * for tie-breaking purposes.

+ * + * Changes in the tie-breaking leaderboard may lead to tie-breaking changes in this leaderboard. However, + * this leaderboard assumes that by and large the tie-breaking leaderboard is complete and has no running + * races anymore. An exception to this assumption are score corrections applied to the tie-breaking + * leaderboard, e.g., by importing official results after the races in this leaderboard have started + * already. Therefore, this leaderboard acts as a {@link ScoreCorrectionListener} on the + * tie-breaking leaderboard and will forward those changes. + * + * @author Axel Uhl (d043530) + * + */ +public interface RegattaLeaderboardWithOtherTieBreakingLeaderboard extends RegattaLeaderboard { + RegattaLeaderboard getOtherTieBreakingLeaderboard(); +} diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/ScoringScheme.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/ScoringScheme.java index 66f10586dbc..bd879bc2f95 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/ScoringScheme.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/ScoringScheme.java @@ -117,7 +117,7 @@ public interface ScoringScheme extends Serializable { *

* * "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 + * 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." */ int compareByLastRace(List> o1Scores, List> o2Scores, boolean nullScoresAreBetter, Competitor o1, Competitor o2); @@ -140,6 +140,8 @@ public interface ScoringScheme extends Serializable { * @throws NoWindException */ int compareByLatestRegattaInMetaLeaderboard(Leaderboard leaderboard, Competitor o1, Competitor o2, TimePoint timePoint); + + int compareByOtherTieBreakingLeaderboard(RegattaLeaderboardWithOtherTieBreakingLeaderboard leaderboard, Competitor o1, Competitor o2, TimePoint timePoint); /** * Returning {@code true} makes the number of wins in a medal series the primary ranking criteria. @@ -187,4 +189,27 @@ public interface ScoringScheme extends Serializable { } return factor; } + + /** + * Computes a score corrected by a {@link #getScoreFactor(RaceColumn) column factor} and potentially other + * column-specific rules, such as that despite multiplying, the original score 1 is to map to 1 again. + * Respects {@link RaceColumn#isOneAlwaysStaysOne()} on the {@code raceColumn}. + * + * @see #getOriginalScoreFromScoreScaledByFactor(RaceColumn, double) + * @see ScoringSchemeType#getScaledScore(double, double, boolean) + */ + default double getScoreScaledByFactor(RaceColumn raceColumn, double originalScore) { + return ScoringSchemeType.getScaledScore(getScoreFactor(raceColumn), originalScore, raceColumn.isOneAlwaysStaysOne()); + } + + /** + * "Un-scales" a score; the inverse of {@link #getScoreScaledByFactor(RaceColumn, double)}. Respects + * {@link RaceColumn#isOneAlwaysStaysOne()} on the {@code raceColumn}. + * + * @see #getScoreScaledByFactor(RaceColumn, double) + * @see ScoringSchemeType#getUnscaledScore(double, double, boolean) + */ + default double getOriginalScoreFromScoreScaledByFactor(RaceColumn raceColumn, double scaledScore) { + return ScoringSchemeType.getUnscaledScore(getScoreFactor(raceColumn), scaledScore, raceColumn.isOneAlwaysStaysOne()); + } } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractHighPointExtremeSailingSeriesOverall.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractHighPointExtremeSailingSeriesOverall.java index 9cdfd4edc82..8cbcb4a7db2 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractHighPointExtremeSailingSeriesOverall.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractHighPointExtremeSailingSeriesOverall.java @@ -9,7 +9,6 @@ import java.util.concurrent.Callable; import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.RaceColumn; -import com.sap.sailing.domain.common.NoWindException; import com.sap.sailing.domain.leaderboard.Leaderboard; import com.sap.sailing.domain.leaderboard.MetaLeaderboard; import com.sap.sailing.domain.leaderboard.NumberOfCompetitorsInLeaderboardFetcher; @@ -74,7 +73,7 @@ public abstract class AbstractHighPointExtremeSailingSeriesOverall extends HighP private int getWins(List> scores) { int wins = 0; for (com.sap.sse.common.Util.Pair score : scores) { - if (Math.abs(score.getB() - maxPoints * getScoreFactor(score.getA())) < 0.0000001) { + if (Math.abs(score.getB() - getScoreScaledByFactor(score.getA(), maxPoints)) < 0.0000001) { wins++; } } @@ -109,7 +108,6 @@ public abstract class AbstractHighPointExtremeSailingSeriesOverall extends HighP /** * Notice of Race (NOR) section 13.5 specifies for the series score: "If a tie still remains, it shall be broken in * favor of the boat that had the better place at the last Regatta sailed." - * @throws NoWindException */ @Override public int compareByLatestRegattaInMetaLeaderboard(Leaderboard leaderboard, Competitor o1, Competitor o2, TimePoint timePoint) { diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractHighPointFirstGetsFixedOr8AndLastBreaksTie.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractHighPointFirstGetsFixedOr8AndLastBreaksTie.java index 5b62ef9bedf..b781c3ed0d8 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractHighPointFirstGetsFixedOr8AndLastBreaksTie.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractHighPointFirstGetsFixedOr8AndLastBreaksTie.java @@ -48,8 +48,9 @@ public abstract class AbstractHighPointFirstGetsFixedOr8AndLastBreaksTie extends private double getHighestScore(List> scores) { double highestScore = 0; for (com.sap.sse.common.Util.Pair score : scores) { - if ((score.getB() * getScoreFactor(score.getA())) > highestScore) { - highestScore = score.getB() * getScoreFactor(score.getA()); + final double scoreScaledByFactor = getScoreScaledByFactor(score.getA(), score.getB()); + if (scoreScaledByFactor > highestScore) { + highestScore = scoreScaledByFactor; } } return highestScore; diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractLeaderboardWithCache.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractLeaderboardWithCache.java index c0696fec8c6..73102c15bee 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractLeaderboardWithCache.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractLeaderboardWithCache.java @@ -419,7 +419,7 @@ public abstract class AbstractLeaderboardWithCache implements Leaderboard { RaceColumnDTO raceColumnDTO = result.createEmptyRaceColumn(raceColumn.getName(), raceColumn.isMedalRace(), raceColumn instanceof RaceColumnInSeries ? ((RaceColumnInSeries) raceColumn).getRegatta().getName() : null, raceColumn instanceof RaceColumnInSeries ? ((RaceColumnInSeries) raceColumn).getSeries().getName() : null, - isMetaLeaderboardColumn); + isMetaLeaderboardColumn, raceColumn.isOneAlwaysStaysOne()); if (isMetaLeaderboardColumn && raceColumnDTO instanceof MetaLeaderboardRaceColumnDTO) { calculateRacesMetadata((MetaLeaderboardColumn) raceColumn, (MetaLeaderboardRaceColumnDTO) raceColumnDTO, baseDomainFactory); } @@ -438,7 +438,7 @@ public abstract class AbstractLeaderboardWithCache implements Leaderboard { result.addRace(raceColumn.getName(), raceColumn.getExplicitFactor(), getScoringScheme().getScoreFactor(raceColumn), raceColumn instanceof RaceColumnInSeries ? ((RaceColumnInSeries) raceColumn).getRegatta().getName() : null, raceColumn instanceof RaceColumnInSeries ? ((RaceColumnInSeries) raceColumn).getSeries().getName() : null, - fleetDTO, raceColumn.isMedalRace(), raceIdentifier, race, isMetaLeaderboardColumn); + fleetDTO, raceColumn.isMedalRace(), raceIdentifier, race, isMetaLeaderboardColumn, raceColumn.isOneAlwaysStaysOne()); } Future> task = executor.submit( () -> baseDomainFactory.getCompetitorDTOList(AbstractLeaderboardWithCache.this.getCompetitorsFromBestToWorst( @@ -845,7 +845,7 @@ public abstract class AbstractLeaderboardWithCache implements Leaderboard { row.totalDurationFoiledInSeconds = totalDurationFoiled==null?null:totalDurationFoiled.asSeconds(); } - private LeaderboardDTOCache getLeaderboardDTOCache() { + protected LeaderboardDTOCache getLeaderboardDTOCache() { LeaderboardDTOCache result = this.leaderboardDTOCache; if (result == null) { synchronized (this) { @@ -1183,7 +1183,7 @@ public abstract class AbstractLeaderboardWithCache implements Leaderboard { } @Override - public int getTotalRankOfCompetitor(Competitor competitor, TimePoint timePoint) throws NoWindException { + public int getTotalRankOfCompetitor(Competitor competitor, TimePoint timePoint) { return getCompetitorsFromBestToWorst(timePoint).indexOf(competitor) + 1; } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractScoringSchemeImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractScoringSchemeImpl.java index 800dd6bdf08..7309efb5c8e 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractScoringSchemeImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractScoringSchemeImpl.java @@ -15,6 +15,7 @@ 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.leaderboard.Leaderboard; +import com.sap.sailing.domain.leaderboard.RegattaLeaderboardWithOtherTieBreakingLeaderboard; import com.sap.sailing.domain.leaderboard.ScoringScheme; import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sse.common.TimePoint; @@ -151,7 +152,7 @@ public abstract class AbstractScoringSchemeImpl implements ScoringScheme { * Assuming both competitors scored in the same number of races, compares the sorted scores based on World Sailing's * Racing Rules of Sailing (RRS) addendum A8.1:

* - * "A8.1 If there is a series-score tie between two or more boats, each boat’s race scores shall be listed in + * "A8.1 If there is a series-score tie between two or more boats, each boat's race scores shall be listed in * order of best to worst, and at the first point(s) where there is a difference the tie shall be broken in favour * of the boat(s) with the best score(s). No excluded scores shall be used." */ @@ -166,14 +167,14 @@ public abstract class AbstractScoringSchemeImpl implements ScoringScheme { final Set o1Discards = discardedRaceColumnsPerCompetitor.get(o1); for (com.sap.sse.common.Util.Pair o1ColumnAndScore : o1Scores) { if (includeDiscardedResults || !o1Discards.contains(o1ColumnAndScore.getA())) { - o1NetScores.add(new Pair<>(o1ColumnAndScore.getA(), o1ColumnAndScore.getB() / getScoreFactor(o1ColumnAndScore.getA()))); + o1NetScores.add(new Pair<>(o1ColumnAndScore.getA(), getOriginalScoreFromScoreScaledByFactor(o1ColumnAndScore.getA(), o1ColumnAndScore.getB()))); } } List> o2NetScores = new ArrayList<>(); final Set o2Discards = discardedRaceColumnsPerCompetitor.get(o2); for (com.sap.sse.common.Util.Pair o2ColumnAndScore : o2Scores) { if (includeDiscardedResults || !o2Discards.contains(o2ColumnAndScore.getA())) { - o2NetScores.add(new Pair<>(o2ColumnAndScore.getA(), o2ColumnAndScore.getB() / getScoreFactor(o2ColumnAndScore.getA()))); + o2NetScores.add(new Pair<>(o2ColumnAndScore.getA(), getOriginalScoreFromScoreScaledByFactor(o2ColumnAndScore.getA(), o2ColumnAndScore.getB()))); } } Collections.sort(o1NetScores, ruleA8_1ScoreComparator); @@ -228,7 +229,7 @@ public abstract class AbstractScoringSchemeImpl implements ScoringScheme { *

* * "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 + * 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." * * @param o1ScoresIncludingDiscarded @@ -270,4 +271,9 @@ public abstract class AbstractScoringSchemeImpl implements ScoringScheme { public int compareByLatestRegattaInMetaLeaderboard(Leaderboard leaderboard, Competitor o1, Competitor o2, TimePoint timePoint) { return 0; } + + @Override + public int compareByOtherTieBreakingLeaderboard(RegattaLeaderboardWithOtherTieBreakingLeaderboard leaderboard, Competitor o1, Competitor o2, TimePoint timePoint) { + return 0; + } } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractSimpleLeaderboardImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractSimpleLeaderboardImpl.java index 2549398e1b5..967ae91e312 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractSimpleLeaderboardImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractSimpleLeaderboardImpl.java @@ -271,6 +271,11 @@ public abstract class AbstractSimpleLeaderboardImpl extends AbstractLeaderboardW hasSplitFleetContiguousScoring); } + @Override + public void oneAlwaysStaysOneChanged(RaceColumn raceColumn, boolean oneAlwaysStaysOne) { + getRaceColumnListeners().notifyListenersAboutOneAlwaysStaysOneChanged(raceColumn, oneAlwaysStaysOne); + } + @Override public void hasCrossFleetMergedRankingChanged(RaceColumn raceColumn, boolean hasCrossFleetMergedRanking) { getRaceColumnListeners().notifyListenersAboutHasCrossFleetMergedRankingChanged(raceColumn, hasCrossFleetMergedRanking); @@ -495,7 +500,7 @@ public abstract class AbstractSimpleLeaderboardImpl extends AbstractLeaderboardW if (totalPoints == null) { result = null; } else { - result = getScoringScheme().getScoreFactor(raceColumn) * totalPoints; + result = getScoringScheme().getScoreScaledByFactor(raceColumn, totalPoints); } } return result; @@ -681,11 +686,10 @@ public abstract class AbstractSimpleLeaderboardImpl extends AbstractLeaderboardW boolean discarded = isDiscarded(competitor, race, timePoint, discardedRaceColumns); final Double correctedScore = correctedResults.getCorrectedScore(); final Double correctedScoreScaledByColumnFactor = correctedScore == null ? null - : Double.valueOf((correctedScore * getScoringScheme().getScoreFactor(race))); + : Double.valueOf(getScoringScheme().getScoreScaledByFactor(race, correctedScore)); return new EntryImpl(trackedRankProvider, correctedScoreScaledByColumnFactor, () -> correctedResults.getUncorrectedScore(), correctedResults.isCorrected(), - discarded ? DOUBLE_0 - : correctedScore == null ? null : Double.valueOf(correctedScore * getScoringScheme().getScoreFactor(race)), + discarded ? DOUBLE_0 : correctedScoreScaledByColumnFactor, correctedResults.getMaxPointsReason(), discarded, race.getFleetOfCompetitor(competitor)); } @@ -757,7 +761,7 @@ public abstract class AbstractSimpleLeaderboardImpl extends AbstractLeaderboardW boolean discarded = discardedRacesForCompetitor.contains(raceColumn); final Double correctedScore = correctedResults.getCorrectedScore(); final Double correctedScoreScaledByColumnFactor = correctedScore == null ? null - : Double.valueOf((correctedScore * getScoringScheme().getScoreFactor(raceColumn))); + : Double.valueOf(getScoringScheme().getScoreScaledByFactor(raceColumn, correctedScore)); Entry entry = new EntryImpl(trackedRankProvider, correctedScoreScaledByColumnFactor, () -> correctedResults.getUncorrectedScore(), correctedResults.isCorrected(), discarded ? DOUBLE_0 : correctedScoreScaledByColumnFactor, diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/DelegateLeaderboard.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/DelegateLeaderboard.java new file mode 100644 index 00000000000..d2f8b2e22bd --- /dev/null +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/DelegateLeaderboard.java @@ -0,0 +1,66 @@ +package com.sap.sailing.domain.leaderboard.impl; + +import java.io.Serializable; +import java.util.Iterator; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Consumer; +import java.util.function.Supplier; + +import com.sap.sailing.domain.leaderboard.RegattaLeaderboard; + +public class DelegateLeaderboard implements Serializable { + private static final long serialVersionUID = -1764036951368356044L; + + private RegattaLeaderboard delegateLeaderboard; + + private transient final Supplier delegateLeaderboardSupplier; + + /** + * The particular use case for which this field is introduced is registering score correction + * listeners at a point in time when the full leaderboard hasn't been resolved yet. Instead of + * letting this listener registration attempt the resolution without success the request can + * be queued here, and each time the {@link #getDelegateLeaderboard()} successfully resolves a + * leaderboard, all consumers in this set will be triggered. + */ + private final ConcurrentHashMap, Boolean> triggerWhenDelegateLeaderboardIsResolved; + + public DelegateLeaderboard(Supplier delegateLeaderboardSupplier) { + this.delegateLeaderboardSupplier = delegateLeaderboardSupplier; + this.triggerWhenDelegateLeaderboardIsResolved = new ConcurrentHashMap<>(); + } + + public RegattaLeaderboard getDelegateLeaderboard() { + // TODO make thread-safe; if multiple threads call concurrently, triggers from triggerWhenFullLeaderboardIsResolved may fire several times + if (delegateLeaderboard == null) { + if (delegateLeaderboardSupplier == null) { + throw new NullPointerException("Internal error: Regatta leaderboard supplier is null; this can only happen upon premature serialization"); + } + delegateLeaderboard = delegateLeaderboardSupplier.get(); + if (delegateLeaderboard != null) { + for (Iterator> i=triggerWhenDelegateLeaderboardIsResolved.keySet().iterator(); i.hasNext(); ) { + final Consumer toTrigger = i.next(); + toTrigger.accept(delegateLeaderboard); + i.remove(); + } + } + } + return delegateLeaderboard; + } + + public void scheduleLeaderboardConsumer(final Consumer consumer) { + triggerWhenDelegateLeaderboardIsResolved.put(consumer, true); + } + + /** + * If the {@link #getDelegateLeaderboard() delegate leaderboard} is already available, the {@code action} is + * run with it right away; otherwise it is scheduled and run the next time that {@link #getDelegateLeaderboard()} + * is invoked such that it returns a valid leaderboard. + */ + public void runOrSchedule(final Consumer action) { + if (getDelegateLeaderboard() != null) { + action.accept(getDelegateLeaderboard()); + } else { + scheduleLeaderboardConsumer(action); + } + } +} diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/DelegatingRegattaLeaderboardWithCompetitorElimination.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/DelegatingRegattaLeaderboardWithCompetitorElimination.java index aac0b0e0f4d..9aa2f70ee89 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/DelegatingRegattaLeaderboardWithCompetitorElimination.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/DelegatingRegattaLeaderboardWithCompetitorElimination.java @@ -6,12 +6,10 @@ import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; -import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -import java.util.function.Consumer; import java.util.function.Supplier; import com.sap.sailing.domain.abstractlog.race.RaceLog; @@ -66,18 +64,7 @@ import com.sap.sse.common.Util.Pair; public class DelegatingRegattaLeaderboardWithCompetitorElimination extends AbstractLeaderboardWithCache implements RegattaLeaderboardWithEliminations { private static final long serialVersionUID = 8331154893189722924L; private final String name; - private RegattaLeaderboard fullLeaderboard; - - private transient final Supplier fullLeaderboardSupplier; - - /** - * The particular use case for which this field is introduced is registering score correction - * listeners at a point in time when the full leaderboard hasn't been resolved yet. Instead of - * letting this listener registration attempt the resolution without success the request can - * be queued here, and each time the {@link #getFullLeaderboard()} successfully resolves a - * leaderboard, all consumers in this set will be triggered. - */ - private final ConcurrentHashMap, Boolean> triggerWhenFullLeaderboardIsResolved; + private final DelegateLeaderboard fullLeaderboard; /** * Competitors eliminated from this leaderboard for regatta ranking; those competitors are not part of @@ -93,12 +80,10 @@ public class DelegatingRegattaLeaderboardWithCompetitorElimination extends Abstr /** * The leaderboard wrapper starts out with an empty set of eliminated competitors */ - public DelegatingRegattaLeaderboardWithCompetitorElimination(Supplier fullLeaderboardSupplier, - String name) { + public DelegatingRegattaLeaderboardWithCompetitorElimination(Supplier fullLeaderboardSupplier, String name) { this.name = name; - this.fullLeaderboardSupplier = fullLeaderboardSupplier; this.eliminatedCompetitors = new ConcurrentHashMap<>(); - this.triggerWhenFullLeaderboardIsResolved = new ConcurrentHashMap<>(); + fullLeaderboard = new DelegateLeaderboard(fullLeaderboardSupplier); } @Override @@ -106,9 +91,13 @@ public class DelegatingRegattaLeaderboardWithCompetitorElimination extends Abstr return name; } + private RegattaLeaderboard getDelegateLeaderboard() { + return fullLeaderboard.getDelegateLeaderboard(); + } + @Override public Iterable getCompetitors() { - return new ObscuringIterable<>(getFullLeaderboard().getCompetitors(), eliminatedCompetitors.keySet()); + return new ObscuringIterable<>(getDelegateLeaderboard().getCompetitors(), eliminatedCompetitors.keySet()); } @Override @@ -118,6 +107,7 @@ public class DelegatingRegattaLeaderboardWithCompetitorElimination extends Abstr } else { eliminatedCompetitors.remove(competitor); } + getLeaderboardDTOCache().invalidate(this); } @Override @@ -133,7 +123,7 @@ public class DelegatingRegattaLeaderboardWithCompetitorElimination extends Abstr @Override public Map> getRankedCompetitorsFromBestToWorstAfterEachRaceColumn(TimePoint timePoint, WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache) throws NoWindException { - Map> preResult = getFullLeaderboard().getRankedCompetitorsFromBestToWorstAfterEachRaceColumn(timePoint, cache); + Map> preResult = getDelegateLeaderboard().getRankedCompetitorsFromBestToWorstAfterEachRaceColumn(timePoint, cache); for (final List e : preResult.values()) { e.removeAll(eliminatedCompetitors.keySet()); } @@ -143,7 +133,7 @@ public class DelegatingRegattaLeaderboardWithCompetitorElimination extends Abstr @Override public Map getCompetitorsForWhichThereAreCarriedPoints() { final Map result = new HashMap<>(); - for (final java.util.Map.Entry e : getFullLeaderboard().getCompetitorsForWhichThereAreCarriedPoints().entrySet()) { + for (final java.util.Map.Entry e : getDelegateLeaderboard().getCompetitorsForWhichThereAreCarriedPoints().entrySet()) { if (!isEliminated(e.getKey())) { result.put(e.getKey(), e.getValue()); } @@ -154,13 +144,13 @@ public class DelegatingRegattaLeaderboardWithCompetitorElimination extends Abstr @Override public Iterable getCompetitorsFromBestToWorst(RaceColumn raceColumn, TimePoint timePoint, WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache) throws NoWindException { - return new ObscuringIterable<>(getFullLeaderboard().getCompetitorsFromBestToWorst(raceColumn, timePoint, cache), eliminatedCompetitors.keySet()); + return new ObscuringIterable<>(getDelegateLeaderboard().getCompetitorsFromBestToWorst(raceColumn, timePoint, cache), eliminatedCompetitors.keySet()); } @Override public List getCompetitorsFromBestToWorst(TimePoint timePoint, WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache) { final List result = new ArrayList<>(); - for (final Competitor c : getFullLeaderboard().getCompetitorsFromBestToWorst(timePoint, cache)) { + for (final Competitor c : getDelegateLeaderboard().getCompetitorsFromBestToWorst(timePoint, cache)) { if (!isEliminated(c)) { result.add(c); } @@ -171,7 +161,7 @@ public class DelegatingRegattaLeaderboardWithCompetitorElimination extends Abstr @Override public Map, Entry> getContent(TimePoint timePoint) throws NoWindException { final Map, Entry> result = new HashMap<>(); - for (final java.util.Map.Entry, Entry> e : getFullLeaderboard().getContent(timePoint).entrySet()) { + for (final java.util.Map.Entry, Entry> e : getDelegateLeaderboard().getContent(timePoint).entrySet()) { if (!isEliminated(e.getKey().getA())) { result.put(e.getKey(), e.getValue()); } @@ -187,384 +177,359 @@ public class DelegatingRegattaLeaderboardWithCompetitorElimination extends Abstr // --------------------- Delegate Pattern Implementation ---------------------- @Override public CompetitorProviderFromRaceColumnsAndRegattaLike getOrCreateCompetitorsProvider() { - return getFullLeaderboard().getOrCreateCompetitorsProvider(); + return getDelegateLeaderboard().getOrCreateCompetitorsProvider(); } @Override public Regatta getRegatta() { - return getFullLeaderboard().getRegatta(); + return getDelegateLeaderboard().getRegatta(); } @Override public Iterable getCompetitorsRegisteredInRegattaLog() { - return getFullLeaderboard().getCompetitorsRegisteredInRegattaLog(); + return getDelegateLeaderboard().getCompetitorsRegisteredInRegattaLog(); } @Override public IsRegattaLike getRegattaLike() { - return getFullLeaderboard().getRegattaLike(); + return getDelegateLeaderboard().getRegattaLike(); } @Override public RaceLog getRacelog(String raceColumnName, String fleetName) { - return getFullLeaderboard().getRacelog(raceColumnName, fleetName); + return getDelegateLeaderboard().getRacelog(raceColumnName, fleetName); } @Override public void registerCompetitor(Competitor competitor) { - getFullLeaderboard().registerCompetitor(competitor); + getDelegateLeaderboard().registerCompetitor(competitor); } @Override public void registerCompetitors(Iterable competitors) { - getFullLeaderboard().registerCompetitors(competitors); + getDelegateLeaderboard().registerCompetitors(competitors); } @Override public void deregisterCompetitor(Competitor competitor) { - getFullLeaderboard().deregisterCompetitor(competitor); + getDelegateLeaderboard().deregisterCompetitor(competitor); } @Override public void deregisterCompetitors(Iterable competitor) { - getFullLeaderboard().deregisterCompetitors(competitor); + getDelegateLeaderboard().deregisterCompetitors(competitor); } @Override public Iterable getAllCompetitors() { - return getFullLeaderboard().getAllCompetitors(); + return getDelegateLeaderboard().getAllCompetitors(); } @Override public Pair, Iterable> getAllCompetitorsWithRaceDefinitionsConsidered() { - return getFullLeaderboard().getAllCompetitorsWithRaceDefinitionsConsidered(); + return getDelegateLeaderboard().getAllCompetitorsWithRaceDefinitionsConsidered(); } @Override public Iterable getAllCompetitors(RaceColumn raceColumn, Fleet fleet) { - return getFullLeaderboard().getAllCompetitors(raceColumn, fleet); + return getDelegateLeaderboard().getAllCompetitors(raceColumn, fleet); } @Override public Iterable getCompetitors(RaceColumn raceColumn, Fleet fleet) { - return getFullLeaderboard().getCompetitors(raceColumn, fleet); + return getDelegateLeaderboard().getCompetitors(raceColumn, fleet); } @Override public Iterable getSuppressedCompetitors() { - return getFullLeaderboard().getSuppressedCompetitors(); + return getDelegateLeaderboard().getSuppressedCompetitors(); } @Override public boolean isSuppressed(Competitor competitor) { - return getFullLeaderboard().isSuppressed(competitor); + return getDelegateLeaderboard().isSuppressed(competitor); } @Override public void setSuppressed(Competitor competitor, boolean suppressed) { - getFullLeaderboard().setSuppressed(competitor, suppressed); + getDelegateLeaderboard().setSuppressed(competitor, suppressed); } @Override public Fleet getFleet(String fleetName) { - return getFullLeaderboard().getFleet(fleetName); + return getDelegateLeaderboard().getFleet(fleetName); } @Override public Entry getEntry(Competitor competitor, RaceColumn race, TimePoint timePoint) throws NoWindException { - return getFullLeaderboard().getEntry(competitor, race, timePoint); + return getDelegateLeaderboard().getEntry(competitor, race, timePoint); } @Override public Entry getEntry(Competitor competitor, RaceColumn race, TimePoint timePoint, Set discardedRaceColumns) throws NoWindException { - return getFullLeaderboard().getEntry(competitor, race, timePoint, discardedRaceColumns); + return getDelegateLeaderboard().getEntry(competitor, race, timePoint, discardedRaceColumns); } @Override public Map> getNetPointsSumAfterRaceColumn(TimePoint timePoint) throws NoWindException { - return getFullLeaderboard().getNetPointsSumAfterRaceColumn(timePoint); + return getDelegateLeaderboard().getNetPointsSumAfterRaceColumn(timePoint); } @Override public double getCarriedPoints(Competitor competitor) { - return getFullLeaderboard().getCarriedPoints(competitor); + return getDelegateLeaderboard().getCarriedPoints(competitor); } @Override public int getTrackedRank(Competitor competitor, RaceColumn race, TimePoint timePoint, WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache) { - return getFullLeaderboard().getTrackedRank(competitor, race, timePoint, cache); + return getDelegateLeaderboard().getTrackedRank(competitor, race, timePoint, cache); } @Override public Double getTotalPoints(Competitor competitor, RaceColumn raceColumn, TimePoint timePoint, WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache) { - return getFullLeaderboard().getTotalPoints(competitor, raceColumn, timePoint, cache); + return getDelegateLeaderboard().getTotalPoints(competitor, raceColumn, timePoint, cache); } @Override public MaxPointsReason getMaxPointsReason(Competitor competitor, RaceColumn race, TimePoint timePoint) { - return getFullLeaderboard().getMaxPointsReason(competitor, race, timePoint); + return getDelegateLeaderboard().getMaxPointsReason(competitor, race, timePoint); } @Override public Double getNetPoints(Competitor competitor, RaceColumn race, TimePoint timePoint) { - return getFullLeaderboard().getNetPoints(competitor, race, timePoint); + return getDelegateLeaderboard().getNetPoints(competitor, race, timePoint); } @Override public boolean isDiscarded(Competitor competitor, RaceColumn raceColumn, TimePoint timePoint) { - return getFullLeaderboard().isDiscarded(competitor, raceColumn, timePoint); + return getDelegateLeaderboard().isDiscarded(competitor, raceColumn, timePoint); } @Override public Double getNetPoints(Competitor competitor, TimePoint timePoint) { - return getFullLeaderboard().getNetPoints(competitor, timePoint); + return getDelegateLeaderboard().getNetPoints(competitor, timePoint); } @Override public Double getNetPoints(Competitor competitor, Iterable raceColumnsToConsider, TimePoint timePoint) throws NoWindException { - return getFullLeaderboard().getNetPoints(competitor, raceColumnsToConsider, timePoint); + return getDelegateLeaderboard().getNetPoints(competitor, raceColumnsToConsider, timePoint); } @Override public Iterable getRaceColumns() { - final RegattaLeaderboard theFullLeaderboard = getFullLeaderboard(); + final RegattaLeaderboard theFullLeaderboard = getDelegateLeaderboard(); return theFullLeaderboard == null ? Collections.emptySet() : theFullLeaderboard.getRaceColumns(); } @Override public RaceColumn getRaceColumnByName(String name) { - return getFullLeaderboard().getRaceColumnByName(name); + return getDelegateLeaderboard().getRaceColumnByName(name); } @Override public void setCarriedPoints(Competitor competitor, double carriedPoints) { - getFullLeaderboard().setCarriedPoints(competitor, carriedPoints); + getDelegateLeaderboard().setCarriedPoints(competitor, carriedPoints); } @Override public void unsetCarriedPoints(Competitor competitor) { - getFullLeaderboard().unsetCarriedPoints(competitor); + getDelegateLeaderboard().unsetCarriedPoints(competitor); } @Override public boolean hasCarriedPoints() { - return getFullLeaderboard().hasCarriedPoints(); + return getDelegateLeaderboard().hasCarriedPoints(); } @Override public boolean hasCarriedPoints(Competitor competitor) { - return getFullLeaderboard().hasCarriedPoints(competitor); + return getDelegateLeaderboard().hasCarriedPoints(competitor); } @Override public SettableScoreCorrection getScoreCorrection() { - return getFullLeaderboard().getScoreCorrection(); + return getDelegateLeaderboard().getScoreCorrection(); } @Override public void addScoreCorrectionListener(ScoreCorrectionListener listener) { - if (getFullLeaderboard() != null) { - getFullLeaderboard().addScoreCorrectionListener(listener); - } else { - triggerWhenFullLeaderboardIsResolved.put(leaderboard->leaderboard.addScoreCorrectionListener(listener), true); - } + fullLeaderboard.runOrSchedule(leaderboard->leaderboard.addScoreCorrectionListener(listener)); } @Override public void removeScoreCorrectionListener(ScoreCorrectionListener listener) { - if (getFullLeaderboard() != null) { - getFullLeaderboard().removeScoreCorrectionListener(listener); - } else { - triggerWhenFullLeaderboardIsResolved.put(leaderboard->leaderboard.removeScoreCorrectionListener(listener), true); - } + fullLeaderboard.runOrSchedule(leaderboard->leaderboard.removeScoreCorrectionListener(listener)); } @Override public Competitor getCompetitorByName(String competitorName) { - return getFullLeaderboard().getCompetitorByName(competitorName); + return getDelegateLeaderboard().getCompetitorByName(competitorName); } public void setDisplayName(Competitor competitor, String displayName) { - getFullLeaderboard().setDisplayName(competitor, displayName); + getDelegateLeaderboard().setDisplayName(competitor, displayName); } @Override public String getDisplayName(Competitor competitor) { - return getFullLeaderboard().getDisplayName(competitor); + return getDelegateLeaderboard().getDisplayName(competitor); } @Override public boolean countRaceForComparisonWithDiscardingThresholds(Competitor competitor, RaceColumn raceColumn, TimePoint timePoint) { - return getFullLeaderboard().countRaceForComparisonWithDiscardingThresholds(competitor, raceColumn, timePoint); + return getDelegateLeaderboard().countRaceForComparisonWithDiscardingThresholds(competitor, raceColumn, timePoint); } @Override public ResultDiscardingRule getResultDiscardingRule() { - return getFullLeaderboard().getResultDiscardingRule(); + return getDelegateLeaderboard().getResultDiscardingRule(); } @Override public void setCrossLeaderboardResultDiscardingRule(ThresholdBasedResultDiscardingRule discardingRule) { - getFullLeaderboard().setCrossLeaderboardResultDiscardingRule(discardingRule); + getDelegateLeaderboard().setCrossLeaderboardResultDiscardingRule(discardingRule); } @Override public Competitor getCompetitorByIdAsString(String idAsString) { - return getFullLeaderboard().getCompetitorByIdAsString(idAsString); + return getDelegateLeaderboard().getCompetitorByIdAsString(idAsString); } @Override public void addRaceColumnListener(RaceColumnListener listener) { - getFullLeaderboard().addRaceColumnListener(listener); + getDelegateLeaderboard().addRaceColumnListener(listener); } @Override public void removeRaceColumnListener(RaceColumnListener listener) { - getFullLeaderboard().removeRaceColumnListener(listener); + getDelegateLeaderboard().removeRaceColumnListener(listener); } @Override public Long getDelayToLiveInMillis() { - return getFullLeaderboard().getDelayToLiveInMillis(); + return getDelegateLeaderboard().getDelayToLiveInMillis(); } @Override public Iterable getTrackedRaces() { - return getFullLeaderboard().getTrackedRaces(); + return getDelegateLeaderboard().getTrackedRaces(); } @Override public ScoringScheme getScoringScheme() { - return getFullLeaderboard().getScoringScheme(); + return getDelegateLeaderboard().getScoringScheme(); } @Override public TimePoint getTimePointOfLatestModification() { - return getFullLeaderboard().getTimePointOfLatestModification(); + return getDelegateLeaderboard().getTimePointOfLatestModification(); } @Override public Pair getMaximumSpeedOverGround(Competitor competitor, TimePoint timePoint) { - return getFullLeaderboard().getMaximumSpeedOverGround(competitor, timePoint); + return getDelegateLeaderboard().getMaximumSpeedOverGround(competitor, timePoint); } @Override public Speed getAverageSpeedOverGround(Competitor competitor, TimePoint timePoint) { - return getFullLeaderboard().getAverageSpeedOverGround(competitor, timePoint); + return getDelegateLeaderboard().getAverageSpeedOverGround(competitor, timePoint); } @Override public Double getNetPoints(Competitor competitor, RaceColumn raceColumn, Iterable raceColumnsToConsider, TimePoint timePoint) throws NoWindException { - return getFullLeaderboard().getNetPoints(competitor, raceColumn, raceColumnsToConsider, timePoint); + return getDelegateLeaderboard().getNetPoints(competitor, raceColumn, raceColumnsToConsider, timePoint); } @Override public Double getNetPoints(Competitor competitor, RaceColumn raceColumn, TimePoint timePoint, Set discardedRaceColumns) { - return getFullLeaderboard().getNetPoints(competitor, raceColumn, timePoint, discardedRaceColumns); + return getDelegateLeaderboard().getNetPoints(competitor, raceColumn, timePoint, discardedRaceColumns); } @Override public TimePoint getNowMinusDelay() { - return getFullLeaderboard().getNowMinusDelay(); + return getDelegateLeaderboard().getNowMinusDelay(); } @Override public Iterable getCourseAreas() { - return getFullLeaderboard().getCourseAreas(); + return getDelegateLeaderboard().getCourseAreas(); } @Override public NumberOfCompetitorsInLeaderboardFetcher getNumberOfCompetitorsInLeaderboardFetcher() { - return getFullLeaderboard().getNumberOfCompetitorsInLeaderboardFetcher(); + return getDelegateLeaderboard().getNumberOfCompetitorsInLeaderboardFetcher(); } @Override public Pair getRaceColumnAndFleet(TrackedRace trackedRace) { - return getFullLeaderboard().getRaceColumnAndFleet(trackedRace); + return getDelegateLeaderboard().getRaceColumnAndFleet(trackedRace); } @Override public BoatClass getBoatClass() { - return getFullLeaderboard().getBoatClass(); + return getDelegateLeaderboard().getBoatClass(); } @Override public Boat getBoatOfCompetitor(Competitor competitor, RaceColumn raceColumn, Fleet fleet) { - return getFullLeaderboard().getBoatOfCompetitor(competitor, raceColumn, fleet); - } - - private RegattaLeaderboard getFullLeaderboard() { - if (fullLeaderboard == null) { - if (fullLeaderboardSupplier == null) { - throw new NullPointerException("Internal error: Regatta leaderboard supplier is null; this can only happen upon premature serialization"); - } - fullLeaderboard = fullLeaderboardSupplier.get(); - if (fullLeaderboard != null) { - for (Iterator> i=triggerWhenFullLeaderboardIsResolved.keySet().iterator(); i.hasNext(); ) { - final Consumer toTrigger = i.next(); - toTrigger.accept(fullLeaderboard); - i.remove(); - } - } - } - return fullLeaderboard; + return getDelegateLeaderboard().getBoatOfCompetitor(competitor, raceColumn, fleet); } /** - * Before being serialized, ensure that the leaderboard supplier has been used to tru + * Before being serialized, ensure that the leaderboard supplier has been used * to resolve the leaderboard. * @throws IOException */ private void writeObject(ObjectOutputStream oos) throws IOException { - getFullLeaderboard(); + getDelegateLeaderboard(); oos.defaultWriteObject(); } @Override public Iterable getBoatsRegisteredInRegattaLog() { - return getFullLeaderboard().getBoatsRegisteredInRegattaLog(); + return getDelegateLeaderboard().getBoatsRegisteredInRegattaLog(); } @Override public Iterable getAllBoats() { - return getFullLeaderboard().getAllBoats(); + return getDelegateLeaderboard().getAllBoats(); } @Override public void registerBoat(Boat boat) { - getFullLeaderboard().registerBoat(boat); + getDelegateLeaderboard().registerBoat(boat); } @Override public void registerBoats(Iterable boats) { - getFullLeaderboard().registerBoats(boats); + getDelegateLeaderboard().registerBoats(boats); } @Override public void deregisterBoat(Boat boat) { - getFullLeaderboard().deregisterBoat(boat); + getDelegateLeaderboard().deregisterBoat(boat); } @Override public void deregisterBoats(Iterable boats) { - getFullLeaderboard().deregisterBoats(boats); + getDelegateLeaderboard().deregisterBoats(boats); } @Override public Double getNetPoints(Competitor competitor, RaceColumn raceColumn, TimePoint timePoint, Set discardedRaceColumns, Supplier totalPointsProvider) { - return getFullLeaderboard().getNetPoints(competitor, raceColumn, timePoint, discardedRaceColumns, + return getDelegateLeaderboard().getNetPoints(competitor, raceColumn, timePoint, discardedRaceColumns, totalPointsProvider); } @Override public boolean isResultsAreOfficial(RaceColumn raceColumn, Fleet fleet) { - return getFullLeaderboard().isResultsAreOfficial(raceColumn, fleet); + return getDelegateLeaderboard().isResultsAreOfficial(raceColumn, fleet); } } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/HighPointByWinsTiesLastlyBrokenByOtherLeaderboard.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/HighPointByWinsTiesLastlyBrokenByOtherLeaderboard.java new file mode 100644 index 00000000000..a1acf8191f7 --- /dev/null +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/HighPointByWinsTiesLastlyBrokenByOtherLeaderboard.java @@ -0,0 +1,35 @@ +package com.sap.sailing.domain.leaderboard.impl; + +import com.sap.sailing.domain.base.Competitor; +import com.sap.sailing.domain.common.ScoringSchemeType; +import com.sap.sailing.domain.leaderboard.RegattaLeaderboardWithOtherTieBreakingLeaderboard; +import com.sap.sse.common.TimePoint; + +public class HighPointByWinsTiesLastlyBrokenByOtherLeaderboard extends HighPointMatchRacing { + private static final long serialVersionUID = -2930982687072741643L; + + @Override + public ScoringSchemeType getType() { + return ScoringSchemeType.HIGH_POINT_BY_WINS_TIES_LASTLY_BROKEN_BY_OTHER_LEADERBOARD; + } + + @Override + public int compareByOtherTieBreakingLeaderboard(RegattaLeaderboardWithOtherTieBreakingLeaderboard leaderboard, + Competitor o1, Competitor o2, TimePoint timePoint) { + final int result; + final int o1RankInOtherTieBreakingLeaderboard = leaderboard.getOtherTieBreakingLeaderboard().getTotalRankOfCompetitor(o1, timePoint); + final int o2RankInOtherTieBreakingLeaderboard = leaderboard.getOtherTieBreakingLeaderboard().getTotalRankOfCompetitor(o2, timePoint); + if (o1RankInOtherTieBreakingLeaderboard == o2RankInOtherTieBreakingLeaderboard) { + result = 0; + } else { + if (o1RankInOtherTieBreakingLeaderboard == 0) { + result = 1; // o1 has no rank; this is worse ("greater") than any valid rank + } else if (o2RankInOtherTieBreakingLeaderboard == 0) { + result = -1; + } else { + result = Integer.compare(o1RankInOtherTieBreakingLeaderboard, o2RankInOtherTieBreakingLeaderboard); + } + } + return result; + } +} diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/HighPointMatchRacing.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/HighPointMatchRacing.java index ca85336eb8b..81bee87beb2 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/HighPointMatchRacing.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/HighPointMatchRacing.java @@ -32,5 +32,4 @@ public class HighPointMatchRacing extends HighPoint { Callable numberOfCompetitorsInRaceFetcher, NumberOfCompetitorsInLeaderboardFetcher numberOfCompetitorsInLeaderboardFetcher, TimePoint timePoint) { return rank == 0 ? null : rank == 1 ? 1.0 : 0.0; } - } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/LeaderboardTotalRankComparator.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/LeaderboardTotalRankComparator.java index b0ed671fc65..306a986cce2 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/LeaderboardTotalRankComparator.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/LeaderboardTotalRankComparator.java @@ -16,6 +16,7 @@ import com.sap.sailing.domain.base.Regatta; import com.sap.sailing.domain.base.Series; import com.sap.sailing.domain.common.NoWindException; import com.sap.sailing.domain.leaderboard.Leaderboard; +import com.sap.sailing.domain.leaderboard.RegattaLeaderboardWithOtherTieBreakingLeaderboard; import com.sap.sailing.domain.leaderboard.ScoringScheme; import com.sap.sailing.domain.tracking.WindLegTypeAndLegBearingAndORCPerformanceCurveCache; import com.sap.sse.common.TimePoint; @@ -255,7 +256,13 @@ public class LeaderboardTotalRankComparator implements Comparator { if (result == 0) { result = scoringScheme.compareByLatestRegattaInMetaLeaderboard(getLeaderboard(), o1, o2, timePoint); if (result == 0) { - result = compareByArbitraryButStableCriteria(o1, o2); + if (getLeaderboard() instanceof RegattaLeaderboardWithOtherTieBreakingLeaderboard) { + result = scoringScheme.compareByOtherTieBreakingLeaderboard( + (RegattaLeaderboardWithOtherTieBreakingLeaderboard) getLeaderboard(), o1, o2, timePoint); + } + if (result == 0) { + result = compareByArbitraryButStableCriteria(o1, o2); + } } } } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/RegattaLeaderboardWithOtherTieBreakingLeaderboardImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/RegattaLeaderboardWithOtherTieBreakingLeaderboardImpl.java new file mode 100644 index 00000000000..9a0a1cc990e --- /dev/null +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/RegattaLeaderboardWithOtherTieBreakingLeaderboardImpl.java @@ -0,0 +1,48 @@ +package com.sap.sailing.domain.leaderboard.impl; + +import java.util.function.Supplier; + +import com.sap.sailing.domain.base.Regatta; +import com.sap.sailing.domain.common.LeaderboardType; +import com.sap.sailing.domain.leaderboard.AbstractScoreCorrectionListenerWithDefaultAction; +import com.sap.sailing.domain.leaderboard.RegattaLeaderboard; +import com.sap.sailing.domain.leaderboard.RegattaLeaderboardWithOtherTieBreakingLeaderboard; +import com.sap.sailing.domain.leaderboard.ScoreCorrectionListener; +import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule; + +/** + * Upon construction accepts a supplier for another {@link RegattaLeaderboard} and schedules the registration + * of a {@link ScoreCorrectionListener} on that leaderboard. When score corrections change on that other + * leaderboard, this leaderboard's cache contents are invalidated. + * + * @author Axel Uhl (d043530) + * + */ +public class RegattaLeaderboardWithOtherTieBreakingLeaderboardImpl extends RegattaLeaderboardImpl + implements RegattaLeaderboardWithOtherTieBreakingLeaderboard { + private static final long serialVersionUID = -4859656155952136210L; + private final DelegateLeaderboard otherTieBreakingLeaderboardProvider; + + public RegattaLeaderboardWithOtherTieBreakingLeaderboardImpl(Regatta regatta, + ThresholdBasedResultDiscardingRule resultDiscardingRule, Supplier otherTieBreakingLeaderboardProvider) { + super(regatta, resultDiscardingRule); + this.otherTieBreakingLeaderboardProvider = new DelegateLeaderboard(otherTieBreakingLeaderboardProvider); + this.otherTieBreakingLeaderboardProvider.runOrSchedule(otherTieBreakingLeaderboard->otherTieBreakingLeaderboard.addScoreCorrectionListener( + new AbstractScoreCorrectionListenerWithDefaultAction() { + @Override + protected void defaultAction() { + getLeaderboardDTOCache().invalidate(RegattaLeaderboardWithOtherTieBreakingLeaderboardImpl.this); + } + })); + } + + @Override + public RegattaLeaderboard getOtherTieBreakingLeaderboard() { + return otherTieBreakingLeaderboardProvider.getDelegateLeaderboard(); + } + + @Override + public LeaderboardType getLeaderboardType() { + return LeaderboardType.RegattaLeaderboardWithOtherTieBreakingLeaderboard; + } +} diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/ThresholdBasedResultDiscardingRuleImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/ThresholdBasedResultDiscardingRuleImpl.java index 89dce680cc6..58a55566c1f 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/ThresholdBasedResultDiscardingRuleImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/ThresholdBasedResultDiscardingRuleImpl.java @@ -100,7 +100,7 @@ public class ThresholdBasedResultDiscardingRuleImpl implements ThresholdBasedRes sortedRaces.add(raceColumn); final Double totalPoints = totalPointsSupplier.apply(raceColumn); totalPointsForCompetitorPerColumn.put(raceColumn, - totalPoints == null ? null : (totalPoints * scoringScheme.getScoreFactor(raceColumn))); + totalPoints == null ? null : scoringScheme.getScoreScaledByFactor(raceColumn, totalPoints)); } } result = new HashSet(); diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/meta/MetaLeaderboardColumn.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/meta/MetaLeaderboardColumn.java index 7b5d77e6d2a..f1d3565a229 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/meta/MetaLeaderboardColumn.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/meta/MetaLeaderboardColumn.java @@ -109,6 +109,11 @@ public class MetaLeaderboardColumn extends SimpleAbstractRaceColumn implements R return false; } + @Override + public boolean isOneAlwaysStaysOne() { + return false; + } + @Override public void releaseTrackedRace(Fleet fleet) { } @@ -153,6 +158,11 @@ public class MetaLeaderboardColumn extends SimpleAbstractRaceColumn implements R getRaceColumnListeners().notifyListenersAboutHasCrossFleetMergedRankingChanged(raceColumn, hasCrossFleetMergedRanking); } + @Override + public void oneAlwaysStaysOneChanged(RaceColumn raceColumn, boolean oneAlwaysStaysOne) { + getRaceColumnListeners().notifyListenersAboutOneAlwaysStaysOneChanged(raceColumn, oneAlwaysStaysOne); + } + @Override public boolean canAddRaceColumnToContainer(RaceColumn raceColumn) { return getRaceColumnListeners().canAddRaceColumnToContainer(raceColumn); diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/util/impl/RaceColumnListeners.java b/java/com.sap.sailing.domain/src/com/sap/sailing/util/impl/RaceColumnListeners.java index 7371f62c466..9219f71c4d0 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/util/impl/RaceColumnListeners.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/util/impl/RaceColumnListeners.java @@ -187,4 +187,10 @@ public class RaceColumnListeners implements Serializable { listener.regattaLogEventAdded(event); } } + + public void notifyListenersAboutOneAlwaysStaysOneChanged(RaceColumn raceColumn, boolean oneAlwaysStaysOne) { + for (RaceColumnListener listener : getRaceColumnListeners()) { + listener.oneAlwaysStaysOneChanged(raceColumn, oneAlwaysStaysOne); + } + } } diff --git a/java/com.sap.sailing.geocoding/src/com/sap/sailing/geocoding/impl/ReverseGeocoderImpl.java b/java/com.sap.sailing.geocoding/src/com/sap/sailing/geocoding/impl/ReverseGeocoderImpl.java index 71c59f213ba..98fe1eb78d7 100644 --- a/java/com.sap.sailing.geocoding/src/com/sap/sailing/geocoding/impl/ReverseGeocoderImpl.java +++ b/java/com.sap.sailing.geocoding/src/com/sap/sailing/geocoding/impl/ReverseGeocoderImpl.java @@ -14,6 +14,8 @@ import java.util.Comparator; import java.util.Iterator; import java.util.List; import java.util.Random; +import java.util.logging.Level; +import java.util.logging.Logger; import org.json.simple.JSONArray; import org.json.simple.JSONObject; @@ -44,10 +46,12 @@ public class ReverseGeocoderImpl implements ReverseGeocoder { private final int XKM_RADIUS = 5; private final int ROWS_PER_XKM_RADIUS = 15; - private final int MAX_ROW_NUMBER = 1000; + private final int MAX_ROW_NUMBER = 500; private final int MAX_RADIUS = 300; private QuadTree>> cache = new QuadTree>>();; + + private static final Logger logger = Logger.getLogger(ReverseGeocoderImpl.class.getName()); @Override public Placemark getPlacemarkNearest(Position position) throws IOException, ParseException { @@ -71,14 +75,13 @@ public class ReverseGeocoderImpl implements ReverseGeocoder { @Override public List getPlacemarksNear(Position position, double radius) throws IOException, ParseException { - List placemarks = null; - Util.Triple> cachedPlacemarks = checkCache(position); + final List placemarks; + final Util.Triple> cachedPlacemarks = checkCache(position); // Calculating the search radius and the maximum number of returning Placemarks double limitedRadius = Math.min(radius, MAX_RADIUS); - int radiusInt = (int) limitedRadius; - int xKmRadius = radiusInt / XKM_RADIUS; - int maxRows = (int) (ROWS_PER_XKM_RADIUS * Math.pow(2, xKmRadius)); - maxRows = Math.min(maxRows, MAX_ROW_NUMBER); + final int radiusInt = (int) limitedRadius; + final int xKmRadius = radiusInt / XKM_RADIUS; + final int maxRows = (int) Math.min(ROWS_PER_XKM_RADIUS * Math.pow(2, xKmRadius), MAX_ROW_NUMBER); if (cachedPlacemarks != null && cachedPlacemarks.getB() >= limitedRadius) { if (cachedPlacemarks.getC().size() > maxRows) { placemarks = cachedPlacemarks.getC().subList(0, maxRows); @@ -95,23 +98,29 @@ public class ReverseGeocoderImpl implements ReverseGeocoder { } else { searchPosition = position; } - JSONArray geonames = callNearbyService(searchPosition, limitedRadius, maxRows); + final JSONArray geonames = callNearbyService(searchPosition, limitedRadius, maxRows); if (geonames != null) { Iterator iterator = geonames.iterator(); - placemarks = iterator.hasNext() ? new ArrayList() : null; - while (iterator.hasNext()) { - JSONObject object = (JSONObject) iterator.next(); - Placemark place = jsonToPlacemark(object); - if (place != null) { - placemarks.add(jsonToPlacemark(object)); + if (iterator.hasNext()) { + placemarks = new ArrayList(); + while (iterator.hasNext()) { + JSONObject object = (JSONObject) iterator.next(); + Placemark place = jsonToPlacemark(object); + if (place != null) { + placemarks.add(jsonToPlacemark(object)); + } } + // If there are no cached placemarks for the requested Position just cache them, otherwise update the cache + if (cachedPlacemarks == null) { + cachePlacemarks(searchPosition, limitedRadius, placemarks); + } else { + updateCachedPlacemarks(searchPosition, limitedRadius, placemarks); + } + } else { + placemarks = null; } - } - // If there are no cached placemarks for the requested Position just cache them, otherwise update the cache - if (cachedPlacemarks == null && placemarks != null) { - cachePlacemarks(searchPosition, limitedRadius, placemarks); - } else if (placemarks != null) { - updateCachedPlacemarks(searchPosition, limitedRadius, placemarks); + } else { + placemarks = null; } } return placemarks; @@ -135,11 +144,7 @@ public class ReverseGeocoderImpl implements ReverseGeocoder { public Placemark getPlacemark(String name, Comparator comp) throws IOException, ParseException { StringBuilder url = new StringBuilder(SEARCH_BY_NAME_SERVICE); url.append("name=" + URLEncoder.encode(name, "UTF-8")); - URLConnection connection = addUsernameParameterAndConnect(url); - BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream(), Charset.forName("UTF-8"))); - final JSONParser parser = new JSONParser(); - final JSONObject obj = (JSONObject) parser.parse(in); - final JSONArray geonames = (JSONArray) obj.get("geonames"); + final JSONArray geonames = submitGeonamesRequestForJSONArrayResult(url); return geonames.stream().map(o->jsonToPlacemark((JSONObject) o)).sorted(comp).findFirst().orElse(null); } @@ -242,14 +247,8 @@ public class ReverseGeocoderImpl implements ReverseGeocoder { } private JSONArray callNearestService(Position position) throws MalformedURLException, IOException, ParseException { - StringBuilder url = new StringBuilder(NEARBY_PLACE_SERVICE); - url.append("lat=" + Double.toString(position.getLatDeg())); - url.append("&lng=" + Double.toString(position.getLngDeg())); - URLConnection connection = addUsernameParameterAndConnect(url); - BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream(), Charset.forName("UTF-8"))); - JSONParser parser = new JSONParser(); - JSONObject obj = (JSONObject) parser.parse(in); - JSONArray geonames = (JSONArray) obj.get("geonames"); + StringBuilder url = generateRequestUrlNearbyPlaceService(position); + JSONArray geonames = submitGeonamesRequestForJSONArrayResult(url); return geonames; } @@ -261,16 +260,34 @@ public class ReverseGeocoderImpl implements ReverseGeocoder { private JSONArray callNearbyService(Position position, double radius, int maxRows) throws MalformedURLException, IOException, ParseException { + StringBuilder url = generateRequestUrlNearbyPlaceService(position, radius, maxRows); + JSONArray geonames = submitGeonamesRequestForJSONArrayResult(url); + return geonames; + } + + private StringBuilder generateRequestUrlNearbyPlaceService(Position position) { StringBuilder url = new StringBuilder(NEARBY_PLACE_SERVICE); url.append("lat=" + Double.toString(position.getLatDeg())); url.append("&lng=" + Double.toString(position.getLngDeg())); + return url; + } + + private StringBuilder generateRequestUrlNearbyPlaceService(Position position, double radius, int maxRows) { + StringBuilder url = generateRequestUrlNearbyPlaceService(position); url.append("&radius=" + Double.toString(radius)); url.append("&maxRows=" + Integer.toString(maxRows)); + return url; + } + + private JSONArray submitGeonamesRequestForJSONArrayResult(StringBuilder url) throws MalformedURLException, IOException, ParseException { URLConnection connection = addUsernameParameterAndConnect(url); BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream(), Charset.forName("UTF-8"))); JSONParser parser = new JSONParser(); JSONObject obj = (JSONObject) parser.parse(in); JSONArray geonames = (JSONArray) obj.get("geonames"); + if (geonames == null) { + logger.log(Level.WARNING, "Returning null value for geonames object: " + obj.toJSONString()); + } return geonames; } diff --git a/java/com.sap.sailing.gwt.ui/GWT Sailing SDM.launch b/java/com.sap.sailing.gwt.ui/GWT Sailing SDM.launch index 0bfb17c966e..146ce836dee 100755 --- a/java/com.sap.sailing.gwt.ui/GWT Sailing SDM.launch +++ b/java/com.sap.sailing.gwt.ui/GWT Sailing SDM.launch @@ -120,7 +120,7 @@ - + diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/client/BoatClassImageResolver.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/client/BoatClassImageResolver.java index 04f0384d795..9e707921c20 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/client/BoatClassImageResolver.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/client/BoatClassImageResolver.java @@ -40,10 +40,12 @@ public class BoatClassImageResolver { boatClassIconsMap.put(BoatClassMasterdata.ALBIN_EXPRESS.getDisplayName(), imageResources.AlbinExpressIcon()); boatClassIconsMap.put(BoatClassMasterdata.ALBIN_BALLAD.getDisplayName(), imageResources.AlbinBalladIcon()); boatClassIconsMap.put(BoatClassMasterdata.B_ONE.getDisplayName(), imageResources.BOneIcon()); - boatClassIconsMap.put(BoatClassMasterdata.BAVARIA_CRUISER_41S.getDisplayName(), imageResources.BavariaCruiser41S()); - boatClassIconsMap.put(BoatClassMasterdata.BAVARIA_CRUISER_45.getDisplayName(), imageResources.BavariaCruiser45()); - boatClassIconsMap.put(BoatClassMasterdata.BAVARIA_CRUISER_46.getDisplayName(), imageResources.BavariaCruiser46()); + boatClassIconsMap.put(BoatClassMasterdata.BAVARIA_CRUISER_41S.getDisplayName(), imageResources.BavariaCruiser41SIcon()); + boatClassIconsMap.put(BoatClassMasterdata.BAVARIA_CRUISER_45.getDisplayName(), imageResources.BavariaCruiser45Icon()); + boatClassIconsMap.put(BoatClassMasterdata.BAVARIA_CRUISER_46.getDisplayName(), imageResources.BavariaCruiser46Icon()); boatClassIconsMap.put(BoatClassMasterdata.BB10M.getDisplayName(), imageResources.BB10MIcon()); + boatClassIconsMap.put(BoatClassMasterdata.BENETEAU_FIRST_35.getDisplayName(), imageResources.BeneteauFirst35Icon()); + boatClassIconsMap.put(BoatClassMasterdata.BENETEAU_FIRST_45.getDisplayName(), imageResources.BeneteauFirst45Icon()); boatClassIconsMap.put(BoatClassMasterdata.BRASSFAHRT_I.getDisplayName(), imageResources.Brassfahrt1Icon()); boatClassIconsMap.put(BoatClassMasterdata.BRASSFAHRT_II.getDisplayName(), imageResources.Brassfahrt2Icon()); boatClassIconsMap.put(BoatClassMasterdata.BRASSFAHRT_III.getDisplayName(), imageResources.Brassfahrt3Icon()); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/client/BoatClassImageResources.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/client/BoatClassImageResources.java index ee7a4661894..446f0253145 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/client/BoatClassImageResources.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/client/BoatClassImageResources.java @@ -78,20 +78,28 @@ public interface BoatClassImageResources extends ClientBundle { @Source("com/sap/sailing/gwt/ui/client/images/boatclass/BAVARIA_CRUISER_41S.png") @ImageOptions(preventInlining = true) - ImageResource BavariaCruiser41S(); + ImageResource BavariaCruiser41SIcon(); @Source("com/sap/sailing/gwt/ui/client/images/boatclass/BAVARIA_CRUISER_45.png") @ImageOptions(preventInlining = true) - ImageResource BavariaCruiser45(); + ImageResource BavariaCruiser45Icon(); @Source("com/sap/sailing/gwt/ui/client/images/boatclass/BAVARIA_CRUISER_46.png") @ImageOptions(preventInlining = true) - ImageResource BavariaCruiser46(); + ImageResource BavariaCruiser46Icon(); @Source("com/sap/sailing/gwt/ui/client/images/boatclass/BB10M.png") @ImageOptions(preventInlining = true) ImageResource BB10MIcon(); + @Source("com/sap/sailing/gwt/ui/client/images/boatclass/BENETEAU_FIRST_35.png") + @ImageOptions(preventInlining = true) + ImageResource BeneteauFirst35Icon(); + + @Source("com/sap/sailing/gwt/ui/client/images/boatclass/BENETEAU_FIRST_45.png") + @ImageOptions(preventInlining = true) + ImageResource BeneteauFirst45Icon(); + @Source("com/sap/sailing/gwt/ui/client/images/boatclass/Brassfahrt1.png") @ImageOptions(preventInlining = true) ImageResource Brassfahrt1Icon(); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/communication/user/profile/sailorprofile/GetEventsForSailorProfileAction.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/communication/user/profile/sailorprofile/GetEventsForSailorProfileAction.java index 1f51068ebdc..683ac2817f4 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/communication/user/profile/sailorprofile/GetEventsForSailorProfileAction.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/communication/user/profile/sailorprofile/GetEventsForSailorProfileAction.java @@ -9,7 +9,6 @@ import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.CompetitorAndBoatStore; import com.sap.sailing.domain.base.Event; import com.sap.sailing.domain.base.Regatta; -import com.sap.sailing.domain.common.NoWindException; import com.sap.sailing.domain.leaderboard.Leaderboard; import com.sap.sailing.domain.leaderboard.LeaderboardGroup; import com.sap.sailing.gwt.home.communication.SailingAction; @@ -43,13 +42,10 @@ public class GetEventsForSailorProfileAction implements SailingAction participatedEvents = new ArrayList<>(); - + final CompetitorAndBoatStore store = ctx.getRacingEventService().getCompetitorAndBoatStore(); + final SailorProfilePreferences prefs = ctx.getPreferenceForCurrentUser(SailorProfilePreferences.PREF_NAME); + final SailorProfilePreference pref = findSailorProfile(store, prefs); + final Collection participatedEvents = new ArrayList<>(); // iterate over all existing events for (Event event : ctx.getRacingEventService().getAllEvents()) { Collection participatedRegattas = new ArrayList<>(); @@ -57,57 +53,41 @@ public class GetEventsForSailorProfileAction implements SailingAction containedCompetitors = new ArrayList<>(); + final Collection containedCompetitors = new ArrayList<>(); for (Competitor competitor : pref.getCompetitors()) { if (leaderboard.getCompetitors() != null && Util.contains(leaderboard.getCompetitors(), competitor)) { containedCompetitors.add(competitor); } } - // skip if none of the selected competitors is in this leaderboard if (containedCompetitors.size() == 0) { continue; } - // create and add ParticipatedRegattaDTO for each of the selected competitors who was in this // leaderboard for (Competitor competitor : containedCompetitors) { - int rank = 0; - try { - rank = leaderboard.getTotalRankOfCompetitor(competitor, MillisecondsTimePoint.now()); - } catch (NoWindException e1) { - // ignore - } + int rank = leaderboard.getTotalRankOfCompetitor(competitor, MillisecondsTimePoint.now()); // final String leaderboardName = leaderboard.getDisplayName(); final double points = leaderboard.getNetPoints(competitor, MillisecondsTimePoint.now()); - // regatta name is equal to the leaderboard name - String regattaName = leaderboard.getName(); - - Regatta regatta = ctx.getRacingEventService().getRegattaByName(regattaName); - + final String regattaName = leaderboard.getName(); + final Regatta regatta = ctx.getRacingEventService().getRegattaByName(regattaName); if (regatta == null) { continue; } - // skip, if the regatta is not part of this event (e.g. shared leaderboard group) if (!leaderboard.isPartOfEvent(event)) { - // skip, if overall leaderboard is not part of this event, don't skip if this is a regatta // during an event which is not part of an overall leaderboard if (leaderboardGroup.hasOverallLeaderboard() && !leaderboardGroup.getOverallLeaderboard().isPartOfEvent(event)) continue; } - participatedRegattas.add( new ParticipatedRegattaDTO(regattaName, rank, new SimpleCompetitorWithIdDTO(competitor), "" + regatta.getId(), "" + event.getId(), points)); @@ -119,7 +99,6 @@ public class GetEventsForSailorProfileAction implements SailingAction extends DataEntryDialog { @@ -17,7 +23,7 @@ public abstract class AbstractLeaderboardDialog validator, DialogCallback callback) { + Validator validator, DialogCallback callback) { super(title, null, stringMessages.ok(), stringMessages.cancel(), validator, callback); nameTextBox = createTextBox(leaderboardDescriptor.getName()); nameTextBox.setVisibleLength(50); @@ -63,4 +69,26 @@ public abstract class AbstractLeaderboardDialog existingLeaderboards, String preSelectedRegattaName) { + ListBox result = createListBox(false); + // sort the regatta names + List sortedRegattaLeaderboards = new ArrayList<>(); + for (StrippedLeaderboardDTO leaderboard : existingLeaderboards) { + sortedRegattaLeaderboards.add(leaderboard); + } + Collections.sort(sortedRegattaLeaderboards, (rl1, rl2) -> rl1.getName().compareTo(rl2.getName())); + result.addItem(stringMessages.pleaseSelectARegatta()); + int i=1; + for (StrippedLeaderboardDTO leaderboard : sortedRegattaLeaderboards) { + if (leaderboard.type.isRegattaLeaderboard()) { + result.addItem(leaderboard.getName(), leaderboard.getName()); + if (preSelectedRegattaName != null && leaderboard.getName().equals(preSelectedRegattaName)) { + result.setSelectedIndex(i); + } + i++; + } + } + return result; + } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompetitorRegistrationsPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompetitorRegistrationsPanel.java index 0ca8d955271..b855d31ca27 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompetitorRegistrationsPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompetitorRegistrationsPanel.java @@ -159,8 +159,7 @@ public class CompetitorRegistrationsPanel extends FlowPanel implements BusyDispl stringMessages, errorReporter, /* multiSelection */ true, /* enablePager */ true, /* filterCompetitorWithBoat */ false, /* filterCompetitorsWithoutBoat */ !canBoatsOfCompetitorsChangePerRace); registeredCompetitorsTable = new CompetitorTableWrapper<>(sailingServiceWrite, userService, - /* competitorsRefresher not needed; filled based on registrations */ null, - /* boatsRefresher not needed */ null, stringMessages, + competitorsRefresher, boatsRefresher, stringMessages, errorReporter, /* multiSelection */ true, /* enablePager */ false, /* filterCompetitorWithBoat */ false, /* filterCompetitorsWithoutBoat */ false); registeredCompetitorsTable.getSelectionModel().addSelectionChangeHandler(event -> validateAndUpdate()); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CreateRegattaCallback.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CreateRegattaCallback.java index ed2a583e9e5..847c4fbee9e 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CreateRegattaCallback.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CreateRegattaCallback.java @@ -41,7 +41,7 @@ public class CreateRegattaCallback implements DialogCallback{ this.stringMessages = stringMessages; this.existingEvents = existingEvents; } - + @Override public void ok(RegattaDTO newRegatta) { createNewRegatta(newRegatta, existingEvents); @@ -57,7 +57,7 @@ public class CreateRegattaCallback implements DialogCallback{ SeriesCreationParametersDTO seriesPair = new SeriesCreationParametersDTO(seriesDTO.getFleets(), seriesDTO.isMedal(), seriesDTO.isFleetsCanRunInParallel(), seriesDTO.isStartsWithZeroScore(), seriesDTO.isFirstColumnIsNonDiscardableCarryForward(), seriesDTO.getDiscardThresholds(), - seriesDTO.hasSplitFleetContiguousScoring(), seriesDTO.hasCrossFleetMergedRanking(), seriesDTO.getMaximumNumberOfDiscards()); + seriesDTO.hasSplitFleetContiguousScoring(), seriesDTO.hasCrossFleetMergedRanking(), seriesDTO.getMaximumNumberOfDiscards(), seriesDTO.isOneAlwaysStaysOne()); seriesStructure.put(seriesDTO.getName(), seriesPair); } sailingServiceWrite.createRegatta(newRegatta.getName(), @@ -114,25 +114,25 @@ public class CreateRegattaCallback implements DialogCallback{ } } } - + private void reloadLeaderboards() { if (presenter.getLeaderboardsRefresher() != null) { presenter.getLeaderboardsRefresher().reloadAndCallFillAll(); } } - + private void reloadRegattas() { if (presenter.getRegattasRefresher() != null){ presenter.getRegattasRefresher().reloadAndCallFillAll(); } } - + private void fillEvents() { if (presenter.getEventsRefresher() != null) { presenter.getEventsRefresher().reloadAndCallFillAll(); } } - + private void openCreateDefaultRegattaLeaderboardDialog(final RegattaDTO newRegatta, final Iterable existingEvents) { CreateDefaultRegattaLeaderboardDialog dialog = new CreateDefaultRegattaLeaderboardDialog(sailingServiceWrite, stringMessages, errorReporter, newRegatta, new DialogCallback() { @@ -169,12 +169,12 @@ public class CreateRegattaCallback implements DialogCallback{ dialog.ensureDebugId("CreateDefaultRegattaLeaderboardDialog"); dialog.show(); } - - + + /** * When a new regatta with a new regatta leaderboard has been created, the user will now be given the chance to link * the regatta leaderboard into a leaderboard group of the event out of which the regatta chose its default course area. - * + * * @param newRegattaLeaderboard the new regatta leaderboard that the user may link now to a leaderboard group of an event * @param eventToLinkRegattaTo an event that has at least one {@link EventDTO#getLeaderboardGroups() leaderboard group} */ diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardConfigPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardConfigPanel.java index defbe368296..1aa54feb180 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardConfigPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardConfigPanel.java @@ -163,6 +163,10 @@ public class LeaderboardConfigPanel extends AbstractLeaderboardConfigPanel stringMessages.createRegattaLeaderboardWithEliminations() + " ...", leaderboardCreateAndRegattaReadPermission, this::createRegattaLeaderboardWithEliminations); createRegattaLeaderboardWithEliminationsBtn.ensureDebugId("CreateRegattaLeaderboardWithEliminationsButton"); + final Button createRegattaLeaderboardWithOtherTieBreakingLeaderboardBtn = buttonPanel.addAction( + stringMessages.createRegattaLeaderboardWithOtherTieBreakingLeaderboard() + " ...", + leaderboardCreateAndRegattaReadPermission, this::createRegattaLeaderboardWithOtherTieBreakingLeaderboard); + createRegattaLeaderboardWithOtherTieBreakingLeaderboardBtn.ensureDebugId("CreateRegattaLeaderboardWithOtherTieBreakingLeaderboardButton"); leaderboardRemoveButton = buttonPanel.addRemoveAction(stringMessages.remove(), leaderboardSelectionModel, true, () -> removeLeaderboards(leaderboardSelectionModel.getSelectedSet())); @@ -402,7 +406,7 @@ public class LeaderboardConfigPanel extends AbstractLeaderboardConfigPanel AbstractLeaderboardDialog dialog; switch (leaderboardDTO.type) { case RegattaLeaderboard: - dialog = new RegattaLeaderboardEditDialog(Collections.unmodifiableCollection(otherExistingLeaderboard), + dialog = new RegattaLeaderboardEditDialog<>(Collections.unmodifiableCollection(otherExistingLeaderboard), Collections.unmodifiableCollection(allRegattas), createLeaderboardDescriptor(leaderboardDTO, /* scoring scheme is provided by regatta, not leaderboard */ null), @@ -418,6 +422,25 @@ public class LeaderboardConfigPanel extends AbstractLeaderboardConfigPanel }); dialog.show(); break; + case RegattaLeaderboardWithOtherTieBreakingLeaderboard: + dialog = new RegattaLeaderboardWithOtherTieBreakingLeaderboardEditDialog(Collections.unmodifiableCollection(otherExistingLeaderboard), + Collections.unmodifiableCollection(allRegattas), + new LeaderboardDescriptorWithOtherTieBreakingLeaderboard( + leaderboardDTO.getName(), leaderboardDTO.displayName, /* scoringScheme is provided by regatta, not leaderboard */ null, + leaderboardDTO.discardThresholds, leaderboardDTO.regattaName, Util.mapToArrayList(leaderboardDTO.courseAreas, CourseAreaDTO::getId), + leaderboardDTO.getOtherTieBreakingLeaderboardName()), + stringMessages, errorReporter, new DialogCallback() { + @Override + public void cancel() { + } + + @Override + public void ok(LeaderboardDescriptorWithOtherTieBreakingLeaderboard result) { + updateLeaderboard(oldLeaderboardName, result); + } + }); + dialog.show(); + break; case RegattaLeaderboardWithEliminations: dialog = new RegattaLeaderboardWithEliminationsEditDialog(sailingServiceWrite, userService, Collections.unmodifiableCollection(otherExistingLeaderboard), @@ -930,10 +953,10 @@ public class LeaderboardConfigPanel extends AbstractLeaderboardConfigPanel } private void createRegattaLeaderboard() { - RegattaLeaderboardCreateDialog dialog = new RegattaLeaderboardCreateDialog( + RegattaLeaderboardCreateDialog dialog = new RegattaLeaderboardCreateDialog( Collections.unmodifiableCollection(availableLeaderboardList), - Collections.unmodifiableCollection(allRegattas), stringMessages, errorReporter, - new DialogCallback() { + Collections.unmodifiableCollection(allRegattas), new LeaderboardDescriptor(), stringMessages, + errorReporter, new DialogCallback() { @Override public void cancel() { } @@ -1008,6 +1031,39 @@ public class LeaderboardConfigPanel extends AbstractLeaderboardConfigPanel dialog.show(); } + private void createRegattaLeaderboardWithOtherTieBreakingLeaderboard() { + RegattaLeaderboardWithOtherTieBreakingLeaderboardCreateDialog dialog = new RegattaLeaderboardWithOtherTieBreakingLeaderboardCreateDialog( + Collections.unmodifiableCollection(availableLeaderboardList), + Collections.unmodifiableCollection(allRegattas), stringMessages, errorReporter, + new DialogCallback() { + @Override + public void cancel() { + } + + @Override + public void ok(final LeaderboardDescriptorWithOtherTieBreakingLeaderboard newLeaderboard) { + final RegattaName regattaIdentifier = new RegattaName(newLeaderboard.getRegattaName()); + sailingServiceWrite.createRegattaLeaderboardWithOtherTieBreakingLeaderboard( + regattaIdentifier, newLeaderboard.getDisplayName(), + newLeaderboard.getDiscardThresholds(), newLeaderboard.getOtherTieBreakingLeaderboardName(), + new AsyncCallback() { + @Override + public void onFailure(Throwable t) { + errorReporter.reportError("Error trying to create the new regatta leaderboard " + + newLeaderboard.getName() + ": " + t.getMessage()); + } + + @Override + public void onSuccess(StrippedLeaderboardDTO result) { + addLeaderboard(result); + } + }); + } + }); + dialog.ensureDebugId("RegattaLeaderboardWithOtherTieBreakingLeaderboardCreateDialog"); + dialog.show(); + } + private void addLeaderboard(StrippedLeaderboardDTO result) { filteredLeaderboardList.getList().add(result); availableLeaderboardList.add(result); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardDescriptorWithOtherTieBreakingLeaderboard.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardDescriptorWithOtherTieBreakingLeaderboard.java new file mode 100644 index 00000000000..18fceb0e044 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardDescriptorWithOtherTieBreakingLeaderboard.java @@ -0,0 +1,23 @@ +package com.sap.sailing.gwt.ui.adminconsole; + +import java.util.UUID; + +import com.sap.sailing.domain.common.ScoringSchemeType; + +public class LeaderboardDescriptorWithOtherTieBreakingLeaderboard extends LeaderboardDescriptor { + private String otherTieBreakingLeaderboardName; + + public LeaderboardDescriptorWithOtherTieBreakingLeaderboard() { + super(); + } + + public LeaderboardDescriptorWithOtherTieBreakingLeaderboard(String name, String displayName, ScoringSchemeType scoringScheme, + int[] discardThresholds, String regattaName, Iterable courseAreaIds, String otherTieBreakingLeaderboardName) { + super(name, displayName, scoringScheme, discardThresholds, regattaName, courseAreaIds); + this.otherTieBreakingLeaderboardName = otherTieBreakingLeaderboardName; + } + + public String getOtherTieBreakingLeaderboardName() { + return otherTieBreakingLeaderboardName; + } +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceColumnsInLeaderboardDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceColumnsInLeaderboardDialog.java index 43d703d8864..0619cb68b9b 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceColumnsInLeaderboardDialog.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceColumnsInLeaderboardDialog.java @@ -110,7 +110,7 @@ public class RaceColumnsInLeaderboardDialog extends DataEntryDialog isOneAlwaysStaysOneColumn = new TextColumn() { + @Override + public String getValue(SeriesDTO series) { + return series.isOneAlwaysStaysOne() ? stringMessages.yes() : stringMessages.no(); + } + }; TextColumn maximumNumberOfDiscardsColumn = new TextColumn() { @Override public String getValue(SeriesDTO series) { @@ -318,6 +324,7 @@ public class RegattaDetailsComposite extends Composite { table.addColumn(hasSplitFleetContiguousScoringColumn, stringMessages.hasSplitFleetContiguousScoring()); table.addColumn(hasCrossFleetMergedRankingColumn, stringMessages.hasCrossFleetMergedRanking()); table.addColumn(isFleetsCanRunInParallelColumn, stringMessages.canFleetsRunInParallel()); + table.addColumn(isOneAlwaysStaysOneColumn, stringMessages.oneAlwaysStaysOne()); table.addColumn(maximumNumberOfDiscardsColumn, stringMessages.maximumNumberOfDiscards()); table.addColumn(actionsColumn, stringMessages.actions()); return table; @@ -360,6 +367,7 @@ public class RegattaDetailsComposite extends Composite { final boolean seriesResultDiscardingThresholdsChanged = !Arrays.equals(series.getDiscardThresholds(), seriesDescriptor.getResultDiscardingThresholds()); final boolean maximumNumberOfDiscardsChanged = series.getMaximumNumberOfDiscards() != seriesDescriptor.getMaximumNumberOfDiscards(); + final boolean oneAlwaysStaysOneChanged = series.isOneAlwaysStaysOne() != seriesDescriptor.isOneAlwaysStaysOne(); final boolean seriesNameChanged = !series.getName().equals(seriesDescriptor.getSeriesName()); final RegattaIdentifier regattaIdentifier = new RegattaName(regatta.getName()); List existingRaceColumns = series.getRaceColumns(); @@ -413,13 +421,13 @@ public class RegattaDetailsComposite extends Composite { public void onSuccess(List raceColumns) { if (isMedalChanged || isFleetsCanRunInParallelChanged || seriesResultDiscardingThresholdsChanged || isStartsWithZeroScoreChanged || isFirstColumnIsNonDiscardableCarryForwardChanged || hasSplitFleetContiguousScoringChanged - || hasCrossFleetMergedRankingChanged || seriesNameChanged || maximumNumberOfDiscardsChanged) { + || hasCrossFleetMergedRankingChanged || seriesNameChanged || maximumNumberOfDiscardsChanged || oneAlwaysStaysOneChanged) { sailingServiceWrite.updateSeries(regattaIdentifier, series.getName(), seriesDescriptor.getSeriesName(), seriesDescriptor.isMedal(), seriesDescriptor.isFleetsCanRunInParallel(), seriesDescriptor.getResultDiscardingThresholds(), seriesDescriptor.isStartsWithZeroScore(), seriesDescriptor.isFirstColumnIsNonDiscardableCarryForward(), seriesDescriptor.hasSplitFleetContiguousScoring(), seriesDescriptor.hasCrossFleetMergedRanking(), seriesDescriptor.getMaximumNumberOfDiscards(), - series.getFleets(), new AsyncCallback() { + seriesDescriptor.isOneAlwaysStaysOne(), series.getFleets(), new AsyncCallback() { @Override public void onFailure(Throwable caught) { errorReporter.reportError("Error trying to update series " + series.getName() + ": " diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaLeaderboardCreateDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaLeaderboardCreateDialog.java index 966d3c10c28..cd53a9b66b2 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaLeaderboardCreateDialog.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaLeaderboardCreateDialog.java @@ -9,13 +9,13 @@ import com.sap.sailing.gwt.ui.shared.RegattaDTO; import com.sap.sailing.gwt.ui.shared.StrippedLeaderboardDTO; import com.sap.sse.gwt.client.ErrorReporter; -public class RegattaLeaderboardCreateDialog extends RegattaLeaderboardDialog { +public class RegattaLeaderboardCreateDialog extends RegattaLeaderboardDialog { public RegattaLeaderboardCreateDialog(Collection existingLeaderboards, - Collection existingRegattas, StringMessages stringMessages, ErrorReporter errorReporter, - DialogCallback callback) { - super(stringMessages.createRegattaLeaderboard(), new LeaderboardDescriptor(), existingRegattas, stringMessages, - errorReporter, new RegattaLeaderboardDialog.LeaderboardParameterValidator(stringMessages, existingLeaderboards), + Collection existingRegattas, LD initialLeaderboardDescriptor, StringMessages stringMessages, + ErrorReporter errorReporter, DialogCallback callback) { + super(stringMessages.createRegattaLeaderboard(), initialLeaderboardDescriptor, existingRegattas, stringMessages, + errorReporter, new RegattaLeaderboardDialog.LeaderboardParameterValidator(stringMessages, existingLeaderboards), callback); displayNameTextBox = createTextBox(null); displayNameTextBox.ensureDebugId("DisplayNameTextBox"); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaLeaderboardDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaLeaderboardDialog.java index 8a162d77862..78f70036a96 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaLeaderboardDialog.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaLeaderboardDialog.java @@ -16,12 +16,12 @@ import com.sap.sailing.gwt.ui.shared.StrippedLeaderboardDTO; import com.sap.sse.gwt.client.ErrorReporter; -public abstract class RegattaLeaderboardDialog extends AbstractLeaderboardDialog { +public abstract class RegattaLeaderboardDialog extends AbstractLeaderboardDialog { protected ListBox regattaListBox; protected Collection existingRegattas; - private Label regattaDefinesDiscardsLabel; + protected Label regattaDefinesDiscardsLabel; - protected static class LeaderboardParameterValidator implements Validator { + protected static class LeaderboardParameterValidator implements Validator { protected final StringMessages stringMessages; protected final Collection existingLeaderboards; @@ -32,7 +32,7 @@ public abstract class RegattaLeaderboardDialog extends AbstractLeaderboardDialog } @Override - public String getErrorMessage(LeaderboardDescriptor leaderboardToValidate) { + public String getErrorMessage(LD leaderboardToValidate) { String errorMessage; boolean unique = true; for (StrippedLeaderboardDTO dao : existingLeaderboards) { @@ -58,9 +58,9 @@ public abstract class RegattaLeaderboardDialog extends AbstractLeaderboardDialog } } - public RegattaLeaderboardDialog(String title, LeaderboardDescriptor leaderboardDTO, Collection existingRegattas, StringMessages stringMessages, - ErrorReporter errorReporter, LeaderboardParameterValidator validator, DialogCallback callback) { - super(title, leaderboardDTO, stringMessages, validator, callback); + public RegattaLeaderboardDialog(String title, LD leaderboardDescriptor, Collection existingRegattas, StringMessages stringMessages, + ErrorReporter errorReporter, LeaderboardParameterValidator validator, DialogCallback callback) { + super(title, leaderboardDescriptor, stringMessages, validator, callback); this.existingRegattas = existingRegattas; regattaDefinesDiscardsLabel = new Label(stringMessages.regattaDefinesResultDiscardingRules()); } @@ -100,8 +100,8 @@ public abstract class RegattaLeaderboardDialog extends AbstractLeaderboardDialog } @Override - protected LeaderboardDescriptor getResult() { - LeaderboardDescriptor leaderboard = super.getResult(); + protected LD getResult() { + LD leaderboard = super.getResult(); leaderboard.setRegattaName(getSelectedRegatta() != null ? getSelectedRegatta().getName() : null); return leaderboard; } @@ -109,13 +109,13 @@ public abstract class RegattaLeaderboardDialog extends AbstractLeaderboardDialog @Override protected Widget getAdditionalWidget() { VerticalPanel mainPanel = new VerticalPanel(); - Grid formGrid = new Grid(3,3); + Grid formGrid = new Grid(3, 3); formGrid.setCellSpacing(3); formGrid.setWidget(0, 0, createLabel(stringMessages.regatta())); formGrid.setWidget(0, 1, regattaListBox); - formGrid.setWidget(1, 0, createLabel(stringMessages.name())); + formGrid.setWidget(1, 0, createLabel(stringMessages.name())); formGrid.setWidget(1, 1, nameTextBox); - formGrid.setWidget(2, 0, createLabel(stringMessages.displayName())); + formGrid.setWidget(2, 0, createLabel(stringMessages.displayName())); formGrid.setWidget(2, 1, displayNameTextBox); mainPanel.add(formGrid); mainPanel.add(regattaDefinesDiscardsLabel); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaLeaderboardEditDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaLeaderboardEditDialog.java index 18a9ed41f1f..50d8fa92d27 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaLeaderboardEditDialog.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaLeaderboardEditDialog.java @@ -7,12 +7,12 @@ import com.sap.sailing.gwt.ui.shared.RegattaDTO; import com.sap.sailing.gwt.ui.shared.StrippedLeaderboardDTO; import com.sap.sse.gwt.client.ErrorReporter; -public class RegattaLeaderboardEditDialog extends RegattaLeaderboardDialog { +public class RegattaLeaderboardEditDialog extends RegattaLeaderboardDialog { public RegattaLeaderboardEditDialog(Collection otherExistingLeaderboards, Collection existingRegattas, - LeaderboardDescriptor leaderboardDescriptor, StringMessages stringConstants, ErrorReporter errorReporter, - DialogCallback callback) { + LD leaderboardDescriptor, StringMessages stringConstants, ErrorReporter errorReporter, + DialogCallback callback) { super(stringConstants.editRegattaLeaderboard(), leaderboardDescriptor, existingRegattas, stringConstants, errorReporter, - new RegattaLeaderboardDialog.LeaderboardParameterValidator(stringConstants, otherExistingLeaderboards), callback); + new RegattaLeaderboardDialog.LeaderboardParameterValidator<>(stringConstants, otherExistingLeaderboards), callback); displayNameTextBox = createTextBox(leaderboardDescriptor.getDisplayName()); displayNameTextBox.setVisibleLength(50); regattaListBox = createSortedRegattaListBox(existingRegattas, leaderboardDescriptor.getRegattaName()); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaLeaderboardWithEliminationsDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaLeaderboardWithEliminationsDialog.java index 9408d1e5928..9274736950d 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaLeaderboardWithEliminationsDialog.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaLeaderboardWithEliminationsDialog.java @@ -1,10 +1,7 @@ package com.sap.sailing.gwt.ui.adminconsole; -import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.HashSet; -import java.util.List; import java.util.function.Consumer; import com.google.gwt.event.dom.client.ChangeEvent; @@ -159,28 +156,6 @@ public abstract class RegattaLeaderboardWithEliminationsDialog extends AbstractL return null; } - protected ListBox createSortedRegattaLeaderboardsListBox(Collection existingLeaderboards, String preSelectedRegattaName) { - ListBox result = createListBox(false); - // sort the regatta names - List sortedRegattaLeaderboards = new ArrayList<>(); - for (StrippedLeaderboardDTO leaderboard : existingLeaderboards) { - sortedRegattaLeaderboards.add(leaderboard); - } - Collections.sort(sortedRegattaLeaderboards, (rl1, rl2) -> rl1.getName().compareTo(rl2.getName())); - result.addItem(stringMessages.pleaseSelectARegatta()); - int i=1; - for (StrippedLeaderboardDTO leaderboard : sortedRegattaLeaderboards) { - if (leaderboard.type.isRegattaLeaderboard()) { - result.addItem(leaderboard.getName(), leaderboard.getName()); - if (preSelectedRegattaName != null && leaderboard.getName().equals(preSelectedRegattaName)) { - result.setSelectedIndex(i); - } - i++; - } - } - return result; - } - @Override protected LeaderboardDescriptorWithEliminations getResult() { LeaderboardDescriptor leaderboard = super.getResult(); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaLeaderboardWithEliminationsEditDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaLeaderboardWithEliminationsEditDialog.java index 2bacb3860ab..4494b8c61c6 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaLeaderboardWithEliminationsEditDialog.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaLeaderboardWithEliminationsEditDialog.java @@ -30,7 +30,7 @@ public class RegattaLeaderboardWithEliminationsEditDialog extends RegattaLeaderb Collection otherExistingLeaderboards, Collection existingRegattas, LeaderboardDescriptorWithEliminations leaderboardDescriptor, StringMessages stringMessages, ErrorReporter errorReporter, DialogCallback callback) { - super(sailingServiceWrite, userService, stringMessages.editRegattaLeaderboard(), leaderboardDescriptor, + super(sailingServiceWrite, userService, stringMessages.editRegattaLeaderboardWithEliminations(), leaderboardDescriptor, existingRegattas, otherExistingLeaderboards, stringMessages, errorReporter, new RegattaLeaderboardWithEliminationsDialog.LeaderboardParameterValidator(stringMessages, diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaLeaderboardWithOtherTieBreakingLeaderboardCreateDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaLeaderboardWithOtherTieBreakingLeaderboardCreateDialog.java new file mode 100644 index 00000000000..d9193997954 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaLeaderboardWithOtherTieBreakingLeaderboardCreateDialog.java @@ -0,0 +1,38 @@ +package com.sap.sailing.gwt.ui.adminconsole; + +import java.util.Collection; + +import com.google.gwt.event.dom.client.ChangeEvent; +import com.google.gwt.event.dom.client.ChangeHandler; +import com.sap.sailing.gwt.ui.client.StringMessages; +import com.sap.sailing.gwt.ui.shared.RegattaDTO; +import com.sap.sailing.gwt.ui.shared.StrippedLeaderboardDTO; +import com.sap.sse.gwt.client.ErrorReporter; + +public class RegattaLeaderboardWithOtherTieBreakingLeaderboardCreateDialog extends RegattaLeaderboardWithOtherTieBreakingLeaderboardDialog { + public RegattaLeaderboardWithOtherTieBreakingLeaderboardCreateDialog( + Collection existingLeaderboards, Collection existingRegattas, + StringMessages stringMessages, ErrorReporter errorReporter, + DialogCallback callback) { + super(stringMessages.createRegattaLeaderboardWithOtherTieBreakingLeaderboard(), new LeaderboardDescriptorWithOtherTieBreakingLeaderboard(), + existingLeaderboards, existingRegattas, stringMessages, errorReporter, callback); + displayNameTextBox = createTextBox(null); + displayNameTextBox.ensureDebugId("DisplayNameTextBox"); + displayNameTextBox.setVisibleLength(50); + regattaListBox = createSortedRegattaListBox(existingRegattas, null); + regattaListBox.ensureDebugId("RegattaListBox"); + regattaListBox.addChangeHandler(new ChangeHandler() { + public void onChange(ChangeEvent event) { + int selectedIndex = regattaListBox.getSelectedIndex(); + if (selectedIndex > 0) { + nameTextBox.setText(regattaListBox.getValue(selectedIndex)); + validateAndUpdate(); + } + adjustVisibilityOfResultDiscardingRuleComponent(); + } + }); + otherTieBreakingLeaderboardsListBox = createSortedRegattaLeaderboardsListBox(existingLeaderboards, null); + otherTieBreakingLeaderboardsListBox.ensureDebugId("OtherTieBreakingLeaderboardsListBox"); + discardThresholdBoxes = new DiscardThresholdBoxes(this, stringMessages); + } +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaLeaderboardWithOtherTieBreakingLeaderboardDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaLeaderboardWithOtherTieBreakingLeaderboardDialog.java new file mode 100644 index 00000000000..093bacfa6c3 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaLeaderboardWithOtherTieBreakingLeaderboardDialog.java @@ -0,0 +1,58 @@ +package com.sap.sailing.gwt.ui.adminconsole; + +import java.util.Collection; + +import com.google.gwt.user.client.ui.Grid; +import com.google.gwt.user.client.ui.ListBox; +import com.google.gwt.user.client.ui.VerticalPanel; +import com.google.gwt.user.client.ui.Widget; +import com.sap.sailing.gwt.ui.client.StringMessages; +import com.sap.sailing.gwt.ui.shared.RegattaDTO; +import com.sap.sailing.gwt.ui.shared.StrippedLeaderboardDTO; +import com.sap.sse.gwt.client.ErrorReporter; + +public class RegattaLeaderboardWithOtherTieBreakingLeaderboardDialog extends RegattaLeaderboardDialog { + protected ListBox otherTieBreakingLeaderboardsListBox; + + public RegattaLeaderboardWithOtherTieBreakingLeaderboardDialog( + String title, LeaderboardDescriptorWithOtherTieBreakingLeaderboard leaderboardDescriptor, + Collection existingLeaderboards, Collection existingRegattas, + StringMessages stringMessages, ErrorReporter errorReporter, DialogCallback callback) { + super(title, leaderboardDescriptor, + existingRegattas, stringMessages, errorReporter, + new RegattaLeaderboardDialog.LeaderboardParameterValidator( + stringMessages, existingLeaderboards), + callback); + } + + @Override + protected Widget getAdditionalWidget() { + VerticalPanel mainPanel = new VerticalPanel(); + Grid formGrid = new Grid(4, 3); + formGrid.setCellSpacing(3); + formGrid.setWidget(0, 0, createLabel(stringMessages.regatta())); + formGrid.setWidget(0, 1, regattaListBox); + formGrid.setWidget(1, 0, createLabel(stringMessages.otherTieBreakingLeaderboard())); + formGrid.setWidget(1, 1, otherTieBreakingLeaderboardsListBox); + formGrid.setWidget(2, 0, createLabel(stringMessages.name())); + formGrid.setWidget(2, 1, nameTextBox); + formGrid.setWidget(3, 0, createLabel(stringMessages.displayName())); + formGrid.setWidget(3, 1, displayNameTextBox); + mainPanel.add(formGrid); + mainPanel.add(regattaDefinesDiscardsLabel); + if (discardThresholdBoxes != null) { + mainPanel.add(discardThresholdBoxes.getWidget()); + regattaDefinesDiscardsLabel.setVisible(false); + } + return mainPanel; + } + + @Override + protected LeaderboardDescriptorWithOtherTieBreakingLeaderboard getResult() { + final LeaderboardDescriptor interimsResult = super.getResult(); + return new LeaderboardDescriptorWithOtherTieBreakingLeaderboard(interimsResult.getName(), + interimsResult.getDisplayName(), interimsResult.getScoringScheme(), + interimsResult.getDiscardThresholds(), interimsResult.getRegattaName(), + interimsResult.getCourseAreaIds(), otherTieBreakingLeaderboardsListBox.getSelectedValue()); + } +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaLeaderboardWithOtherTieBreakingLeaderboardEditDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaLeaderboardWithOtherTieBreakingLeaderboardEditDialog.java new file mode 100644 index 00000000000..6bba2fcb497 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaLeaderboardWithOtherTieBreakingLeaderboardEditDialog.java @@ -0,0 +1,27 @@ +package com.sap.sailing.gwt.ui.adminconsole; + +import java.util.Collection; + +import com.sap.sailing.gwt.ui.client.StringMessages; +import com.sap.sailing.gwt.ui.shared.RegattaDTO; +import com.sap.sailing.gwt.ui.shared.StrippedLeaderboardDTO; +import com.sap.sse.gwt.client.ErrorReporter; + +public class RegattaLeaderboardWithOtherTieBreakingLeaderboardEditDialog extends RegattaLeaderboardWithOtherTieBreakingLeaderboardDialog { + public RegattaLeaderboardWithOtherTieBreakingLeaderboardEditDialog(Collection otherExistingLeaderboards, Collection existingRegattas, + LeaderboardDescriptorWithOtherTieBreakingLeaderboard leaderboardDescriptor, StringMessages stringMessages, ErrorReporter errorReporter, + DialogCallback callback) { + super(stringMessages.editRegattaLeaderboardWithOtherTieBreakingLeaderboard(), leaderboardDescriptor, otherExistingLeaderboards, existingRegattas, stringMessages, errorReporter, callback); + displayNameTextBox = createTextBox(leaderboardDescriptor.getDisplayName()); + displayNameTextBox.setVisibleLength(50); + regattaListBox = createSortedRegattaListBox(existingRegattas, leaderboardDescriptor.getRegattaName()); + regattaListBox.setEnabled(false); + otherTieBreakingLeaderboardsListBox = createSortedRegattaLeaderboardsListBox(otherExistingLeaderboards, leaderboardDescriptor.getOtherTieBreakingLeaderboardName()); + otherTieBreakingLeaderboardsListBox.ensureDebugId("OtherTieBreakingLeaderboardsListBox"); + otherTieBreakingLeaderboardsListBox.setEnabled(false); + if (!getSelectedRegatta().definesSeriesDiscardThresholds()) { + discardThresholdBoxes = new DiscardThresholdBoxes(this, leaderboardDescriptor.getDiscardThresholds(), stringMessages); + } // else, the regatta leaderboard obtains its result discarding rule implicitly from the underlying regatta + adjustVisibilityOfResultDiscardingRuleComponent(); + } +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaListComposite.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaListComposite.java index 2fae324e1a6..4a97b6d9049 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaListComposite.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaListComposite.java @@ -381,7 +381,8 @@ public class RegattaListComposite extends Composite { sailingServiceWrite.updateSeries(regattaName, series.getName(), series.getName(), series.isMedal(), series.isFleetsCanRunInParallel(), series.getDiscardThresholds(), series.isStartsWithZeroScore(), series.isFirstColumnIsNonDiscardableCarryForward(), series.hasSplitFleetContiguousScoring(), series.hasCrossFleetMergedRanking(), - series.getMaximumNumberOfDiscards(), series.getFleets(), new MarkedAsyncCallback(new AsyncCallback() { + series.getMaximumNumberOfDiscards(), series.isOneAlwaysStaysOne(), series.getFleets(), + new MarkedAsyncCallback(new AsyncCallback() { @Override public void onFailure(Throwable caught) { errorReporter.reportError("Error trying to update regatta " + editedRegatta.getName() diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaWithSeriesAndFleetsCreateDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaWithSeriesAndFleetsCreateDialog.java index ac616cbf10f..b933f7bf9b0 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaWithSeriesAndFleetsCreateDialog.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaWithSeriesAndFleetsCreateDialog.java @@ -154,7 +154,7 @@ public class RegattaWithSeriesAndFleetsCreateDialog extends RegattaWithSeriesAnd if (series.getName().equals(LeaderboardNameConstants.DEFAULT_SERIES_NAME)) { List races = new ArrayList(); for (int i = 1; i <= 3; i++) { - RaceColumnDTO raceColumnDTO = new RaceColumnInSeriesDTO("R"+i, series.getName(), dto.getName()); + RaceColumnDTO raceColumnDTO = new RaceColumnInSeriesDTO("R"+i, series.getName(), dto.getName(), series.isOneAlwaysStaysOne()); races.add(raceColumnDTO); } series.setRaceColumns(races); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SeriesDescriptor.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SeriesDescriptor.java index 4bdc71044bd..ecbcd9389d7 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SeriesDescriptor.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SeriesDescriptor.java @@ -24,10 +24,10 @@ public class SeriesDescriptor { private final boolean firstColumnIsNonDiscardableCarryForward; private final Integer maximumNumberOfDiscards; private final int[] resultDiscardingThresholds; - + private final boolean oneAlwaysStaysOne; public SeriesDescriptor(SeriesDTO series, String seriesName, List races, boolean isMedal, boolean isFleetsCanRunInParallel, int[] resultDiscardingThresholds, boolean startsWithZeroScore, - boolean firstColumnIsNonDiscardableCarryForward, boolean hasSplitFleetContiguousScoring, boolean hasCrossFleetMergedRanking, Integer maximumNumberOfDiscards) { + boolean firstColumnIsNonDiscardableCarryForward, boolean hasSplitFleetContiguousScoring, boolean hasCrossFleetMergedRanking, Integer maximumNumberOfDiscards, boolean oneAlwaysStaysOne) { this.seriesName = seriesName; this.series = series; this.races = races; @@ -39,6 +39,7 @@ public class SeriesDescriptor { this.resultDiscardingThresholds = resultDiscardingThresholds; this.firstColumnIsNonDiscardableCarryForward = firstColumnIsNonDiscardableCarryForward; this.maximumNumberOfDiscards = maximumNumberOfDiscards; + this.oneAlwaysStaysOne = oneAlwaysStaysOne; } public SeriesDTO getSeries() { @@ -84,4 +85,8 @@ public class SeriesDescriptor { public Integer getMaximumNumberOfDiscards() { return maximumNumberOfDiscards; } + + public boolean isOneAlwaysStaysOne() { + return oneAlwaysStaysOne; + } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SeriesEditDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SeriesEditDialog.java index 9ceecf10540..37951d9561a 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SeriesEditDialog.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SeriesEditDialog.java @@ -46,6 +46,7 @@ public class SeriesEditDialog extends DataEntryDialog { private CheckBox hasSplitFleetContiguousScoringCheckbox; private CheckBox hasCrossFleetMergedRankingCheckbox; private CheckBox firstColumnIsNonDiscardableCarryForwardCheckbox; + private CheckBox oneAlwaysStaysOneCheckbox; private IntegerBox maximumNumberOfDiscardsBox; private CheckBox useSeriesResultDiscardingThresholdsCheckbox; private final StringMessages stringMessages; @@ -132,7 +133,7 @@ public class SeriesEditDialog extends DataEntryDialog { for (String name : raceNamesEditor.getValue()) { RaceColumnDTO raceColumnDTO = findRaceColumnInSeriesByName(selectedSeries, name); if (raceColumnDTO == null) { - raceColumnDTO = new RaceColumnInSeriesDTO(name, selectedSeries.getName(), regatta.getName()); + raceColumnDTO = new RaceColumnInSeriesDTO(name, selectedSeries.getName(), regatta.getName(), selectedSeries.isOneAlwaysStaysOne()); } races.add(raceColumnDTO); } @@ -141,7 +142,7 @@ public class SeriesEditDialog extends DataEntryDialog { useSeriesResultDiscardingThresholdsCheckbox.getValue() ? discardThresholdBoxes.getDiscardThresholds() : null, startWithZeroScoreCheckbox.getValue(), firstColumnIsNonDiscardableCarryForwardCheckbox.getValue(), hasSplitFleetContiguousScoringCheckbox.getValue(), - hasCrossFleetMergedRankingCheckbox.getValue(), maximumNumberOfDiscardsBox.getValue()); + hasCrossFleetMergedRankingCheckbox.getValue(), maximumNumberOfDiscardsBox.getValue(), oneAlwaysStaysOneCheckbox.getValue()); } private RaceColumnDTO findRaceColumnInSeriesByName(SeriesDTO series, String raceColumnName) { @@ -203,6 +204,11 @@ public class SeriesEditDialog extends DataEntryDialog { firstColumnIsNonDiscardableCarryForwardCheckbox.setValue(selectedSeries.isFirstColumnIsNonDiscardableCarryForward()); additionalWidgetPanel.add(firstColumnIsNonDiscardableCarryForwardCheckbox); + oneAlwaysStaysOneCheckbox = createCheckbox(stringMessages.oneAlwaysStaysOne()); + oneAlwaysStaysOneCheckbox.ensureDebugId("OneAlwaysStaysOneCheckbox"); + oneAlwaysStaysOneCheckbox.setValue(selectedSeries.isOneAlwaysStaysOne()); + additionalWidgetPanel.add(oneAlwaysStaysOneCheckbox); + final HorizontalPanel maximumNumberOfDiscardsPanel = new HorizontalPanel(); maximumNumberOfDiscardsPanel.add(new Label(stringMessages.maximumNumberOfDiscards())); maximumNumberOfDiscardsBox = createIntegerBox(selectedSeries.getMaximumNumberOfDiscards(), /* visibleLength */ 3); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SeriesWithFleetsCreateDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SeriesWithFleetsCreateDialog.java index 4a1c8191cfb..f5910d86f61 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SeriesWithFleetsCreateDialog.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SeriesWithFleetsCreateDialog.java @@ -41,6 +41,7 @@ public class SeriesWithFleetsCreateDialog extends DataEntryDialog { protected CheckBox hasCrossFleetMergedRankingCheckbox; protected CheckBox firstColumnIsNonDiscardableCarryForwardCheckbox; protected CheckBox useSeriesResultDiscardingThresholdsCheckbox; + protected CheckBox oneAlwaysStaysOneCheckbox; protected IntegerBox maximumNumberOfDiscardsBox; protected final DiscardThresholdBoxes discardThresholdBoxes; protected ListEditorComposite fleetListComposite; @@ -153,10 +154,11 @@ public class SeriesWithFleetsCreateDialog extends DataEntryDialog { SeriesWithFleetsCreateDialog.this.discardThresholdBoxes.getWidget().setVisible(event.getValue()); } }); + oneAlwaysStaysOneCheckbox = createCheckbox(stringMessages.oneAlwaysStaysOne()); + oneAlwaysStaysOneCheckbox.ensureDebugId("OneAlwaysStaysOneCheckbox"); Widget widget = discardThresholdBoxes.getWidget(); widget.ensureDebugId("DiscardThresholdBoxes"); widget.setVisible(false); - initializeFleetListComposite(stringMessages); } @@ -182,6 +184,7 @@ public class SeriesWithFleetsCreateDialog extends DataEntryDialog { series.setCrossFleetMergedRanking(hasCrossFleetMergedRankingCheckbox.getValue()); series.setFirstColumnIsNonDiscardableCarryForward(firstColumnIsNonDiscardableCarryForwardCheckbox.getValue()); series.setMaximumNumberOfDiscards(maximumNumberOfDiscardsBox.getValue()); + series.setOneAlwaysStaysOne(oneAlwaysStaysOneCheckbox.getValue()); series.setFleets(fleetListComposite.getValue()); series.setDiscardThresholds(useSeriesResultDiscardingThresholdsCheckbox.getValue() ? discardThresholdBoxes.getDiscardThresholds() : null); return series; @@ -205,11 +208,11 @@ public class SeriesWithFleetsCreateDialog extends DataEntryDialog { formGrid.setWidget(row++, 1, hasSplitFleetContiguousScoringCheckbox); formGrid.setWidget(row++, 1, hasCrossFleetMergedRankingCheckbox); formGrid.setWidget(row++, 1, firstColumnIsNonDiscardableCarryForwardCheckbox); + formGrid.setWidget(row++, 1, oneAlwaysStaysOneCheckbox); formGrid.setWidget(row, 0, new Label(stringMessages.maximumNumberOfDiscards())); formGrid.setWidget(row++, 1, maximumNumberOfDiscardsBox); formGrid.setWidget(row++, 1, useSeriesResultDiscardingThresholdsCheckbox); formGrid.setWidget(row++, 1, discardThresholdBoxes.getWidget()); - TabPanel tabPanel = new TabPanel(); tabPanel.setWidth("100%"); tabPanel.add(fleetListComposite, stringMessages.fleets()); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingServiceWrite.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingServiceWrite.java index 637f5ae3bf0..be7deb48be9 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingServiceWrite.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingServiceWrite.java @@ -270,8 +270,8 @@ public interface SailingServiceWrite extends FileStorageManagementGwtService, Sa void updateSeries(RegattaIdentifier regattaIdentifier, String seriesName, String newSeriesName, boolean isMedal, boolean isFleetsCanRunInParallel, int[] resultDiscardingThresholds, boolean startsWithZeroScore, - boolean firstRaceIsNonDiscardableCarryForward, boolean hasSplitFleetScore, boolean hasCrossFleetMergedRanking, - Integer maximumNumberOfDiscards, List fleets) throws UnauthorizedException; + boolean firstRaceIsNonDiscardableCarryForward, boolean hasSplitFleetScore, boolean hasCrossFleetMergedRanking, Integer maximumNumberOfDiscards, + boolean oneAlwaysStaysOne, List fleets) throws UnauthorizedException; void updateRegatta(RegattaIdentifier regattaIdentifier, Date startDate, Date endDate, List courseAreaUuids, RegattaConfigurationDTO regattaConfiguration, Double buoyZoneRadiusInHullLengths, @@ -378,6 +378,9 @@ public interface SailingServiceWrite extends FileStorageManagementGwtService, Sa StrippedLeaderboardDTO createRegattaLeaderboardWithEliminations(String name, String displayName, String regattaName) throws UnauthorizedException; + StrippedLeaderboardDTO createRegattaLeaderboardWithOtherTieBreakingLeaderboard(RegattaName regattaIdentifier, + String leaderboardDisplayName, int[] discardThresholds, String otherTieBreakingLeaderboardName); + StrippedLeaderboardDTO createRegattaLeaderboard(RegattaName regattaIdentifier, String leaderboardDisplayName, int[] discardThresholds) throws UnauthorizedException; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingServiceWriteAsync.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingServiceWriteAsync.java index a844878ff7f..e1b44eae514 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingServiceWriteAsync.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingServiceWriteAsync.java @@ -356,8 +356,11 @@ public interface SailingServiceWriteAsync extends FileStorageManagementGwtServic int[] discardThresholds, AsyncCallback asyncCallback); void createRegattaLeaderboardWithEliminations(String name, String displayName, - String regattaName, - AsyncCallback asyncCallback); + String regattaName, AsyncCallback asyncCallback); + + void createRegattaLeaderboardWithOtherTieBreakingLeaderboard(RegattaName regattaIdentifier, + String leaderboardDisplayName, int[] discardThresholds, String otherTieBreakingLeaderboardName, + AsyncCallback callback); void removeLeaderboard(String leaderboardName, AsyncCallback asyncCallback); @@ -479,8 +482,8 @@ public interface SailingServiceWriteAsync extends FileStorageManagementGwtServic void updateSeries(RegattaIdentifier regattaIdentifier, String seriesName, String newSeriesName, boolean isMedal, boolean isFleetsCanRunInParallel, int[] resultDiscardingThresholds, boolean startsWithZeroScore, - boolean firstRaceIsNonDiscardableCarryForward, boolean hasSplitFleetScore, boolean hasCrossFleetMergedRanking, - Integer maximumNumberOfDiscards, List fleets, AsyncCallback callback); + boolean firstRaceIsNonDiscardableCarryForward, boolean hasSplitFleetScore, boolean hasCrossFleetMergedRanking, Integer maximumNumberOfDiscards, + boolean oneAlwaysStaysOne, List fleets, AsyncCallback callback); void removeRaceColumnsFromSeries(RegattaIdentifier regattaIdentifier, String seriesName, List columnNames, AsyncCallback callback); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java index 954cb384b16..7a6aa15c290 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java @@ -463,8 +463,11 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages, String createFlexibleLeaderboard(); String createRegattaLeaderboard(); String createRegattaLeaderboardWithEliminations(); + String createRegattaLeaderboardWithOtherTieBreakingLeaderboard(); String editFlexibleLeaderboard(); String editRegattaLeaderboard(); + String editRegattaLeaderboardWithEliminations(); + String editRegattaLeaderboardWithOtherTieBreakingLeaderboard(); String defaultRegatta(); String useOverallLeaderboard(); String overallStandings(); @@ -2411,4 +2414,8 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages, String goToSelfServicePortalDialogTitle(); String goToSelfServicePortalDialogText(); String failedFetchingSelfServicePortalSession(); + String oneAlwaysStaysOne(); + String otherTieBreakingLeaderboard(); + String scoringSchemeHighPointsByWindTiesLastlyBrokenByOtherLeaderboard(); + String scoringSchemeHighPointsByWindTiesLastlyBrokenByOtherLeaderboardDescription(); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties index 93f7a3c078c..29f0befa314 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties @@ -502,9 +502,12 @@ scoringSystem=Scoring system createFlexibleLeaderboard=Create flexible leaderboard createRegattaLeaderboard=Create regatta leaderboard createRegattaLeaderboardWithEliminations=Create regatta leaderboard with eliminations +createRegattaLeaderboardWithOtherTieBreakingLeaderboard=Create regatta leaderboard with other tie-breaking leaderboard createDefaultRegattaLeaderboard=Create default regatta leaderboard editFlexibleLeaderboard=Edit flexible leaderboard editRegattaLeaderboard=Edit regatta leaderboard +editRegattaLeaderboardWithEliminations=Edit regatta leaderboard with eliminations +editRegattaLeaderboardWithOtherTieBreakingLeaderboard=Edit regatta leaderboard with other tie-breaking leaderboard defaultRegatta=Default regatta useOverallLeaderboard=Use Overall leaderboard overallStandings=Overall Standings @@ -2447,4 +2450,8 @@ twdInDegrees=TWD (from) in degrees legDirectionInDegrees=Leg direction in degrees goToSelfServicePortalDialogTitle=Go to Self Service Portal goToSelfServicePortalDialogText=This will take you to the subscription providers Self-Service portal. -failedFetchingSelfServicePortalSession=Could not open the self service portal. \ No newline at end of file +failedFetchingSelfServicePortalSession=Could not open the self service portal. +oneAlwaysStaysOne=Score 1 stays 1, regardless the column factor +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 \ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties index fb427061931..cdb0b8c9a0f 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties @@ -489,9 +489,12 @@ scoringSystem=Bepunktungssystem createFlexibleLeaderboard=Flexible Rangliste anlegen createRegattaLeaderboard=Rangliste für Regatta anlegen createRegattaLeaderboardWithEliminations=Rangliste für Regatten mit eliminierten Teilnehmern anlegen +createRegattaLeaderboardWithOtherTieBreakingLeaderboard=Regatta-Rangliste anlegen mit anderer Rangliste für Tie-Breaking createDefaultRegattaLeaderboard=Standard-Rangliste für Regatta anlegen editFlexibleLeaderboard=Flexible Rangliste ändern editRegattaLeaderboard=Rangliste für Regatta ändern +editRegattaLeaderboardWithEliminations=Rangliste für Regatta mit eliminierten Teilnehmern ändern +editRegattaLeaderboardWithOtherTieBreakingLeaderboard=Rangliste für Regatta mit anderer Rangliste für Tie-Breaking bearbeiten defaultRegatta=Regatta automatisch wählen/anlegen useOverallLeaderboard=Gesamtrangliste anzeigen overallStandings=Gesamtstand @@ -2457,4 +2460,6 @@ twdInDegrees=TWD (aus) in Grad legDirectionInDegrees=Bahnschenkel-Peilung in Grad goToSelfServicePortalDialogTitle=Zum Self Service Portal goToSelfServicePortalDialogText=Self-Service Portal des Abonementanbieters in einem neuen Tab öffnen? -failedFetchingSelfServicePortalSession=Self-Service Portal konnte nicht geöffnet werden. \ No newline at end of file +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 \ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/BoatClassVectorGraphicsResolver.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/BoatClassVectorGraphicsResolver.java index b57a40ff7a8..b32449dc675 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/BoatClassVectorGraphicsResolver.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/BoatClassVectorGraphicsResolver.java @@ -49,7 +49,8 @@ public class BoatClassVectorGraphicsResolver { BoatClassMasterdata.B_ONE, BoatClassMasterdata.IRC, BoatClassMasterdata.LASER_SB3, BoatClassMasterdata.LONGTZE, BoatClassMasterdata.RS_FEVA, BoatClassMasterdata.RS_TERA, BoatClassMasterdata.RS100, BoatClassMasterdata.RS21, BoatClassMasterdata.TP52, BoatClassMasterdata.CLUB_SWAN_50, BoatClassMasterdata.BAVARIA_CRUISER_41S, BoatClassMasterdata.BAVARIA_CRUISER_45, - BoatClassMasterdata.BAVARIA_CRUISER_46, BoatClassMasterdata.SPAEKHUGGER, BoatClassMasterdata.SCAN_KAP_99, + BoatClassMasterdata.BAVARIA_CRUISER_46, BoatClassMasterdata.BENETEAU_FIRST_35, BoatClassMasterdata.BENETEAU_FIRST_45, + 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); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/ScoringSchemeTypeFormatter.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/ScoringSchemeTypeFormatter.java index be43a5fea39..42a095c4c73 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/ScoringSchemeTypeFormatter.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/ScoringSchemeTypeFormatter.java @@ -50,6 +50,8 @@ public class ScoringSchemeTypeFormatter { return stringMessages.scoringSchemeLowPointSystemFirstTwoWins(); case LOW_POINT_FIRST_TO_WIN_THREE_RACES: return stringMessages.scoringSchemeLowPointSystemFirstThreeWins(); + case HIGH_POINT_BY_WINS_TIES_LASTLY_BROKEN_BY_OTHER_LEADERBOARD: + return stringMessages.scoringSchemeHighPointsByWindTiesLastlyBrokenByOtherLeaderboard(); } return null; } @@ -80,6 +82,8 @@ public class ScoringSchemeTypeFormatter { return stringMessages.scoringSchemeLowPointSystemFirstTwoWinsDescription(); case LOW_POINT_FIRST_TO_WIN_THREE_RACES: return stringMessages.scoringSchemeLowPointSystemFirstThreeWinsDescription(); + case HIGH_POINT_BY_WINS_TIES_LASTLY_BROKEN_BY_OTHER_LEADERBOARD: + return stringMessages.scoringSchemeHighPointsByWindTiesLastlyBrokenByOtherLeaderboardDescription(); default: return format(scoringSchemeType, stringMessages); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboardedit/EditableLeaderboardPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboardedit/EditableLeaderboardPanel.java index ada55d2d465..7da580e706a 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboardedit/EditableLeaderboardPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboardedit/EditableLeaderboardPanel.java @@ -819,11 +819,11 @@ public class EditableLeaderboardPanel extends LeaderboardPanel() { + @Override + public StrippedLeaderboardDTO call() throws Exception { + return createStrippedLeaderboardDTO( + getService().apply(new CreateRegattaLeaderboardWithOtherTieBreakingLeaderboard(regattaIdentifier, leaderboardDisplayName, discardThresholds, + otherTieBreakingLeaderboardName)), + false, false); + } + }); + } + @Override public StrippedLeaderboardDTO updateLeaderboard(String leaderboardName, String newLeaderboardDisplayName, int[] newDiscardingThresholds, List newCourseAreaIds) { @@ -1620,7 +1637,7 @@ public class SailingServiceWriteImpl extends SailingServiceImpl implements Saili public void updateSeries(RegattaIdentifier regattaIdentifier, String seriesName, String newSeriesName, boolean isMedal, boolean isFleetsCanRunInParallel, int[] resultDiscardingThresholds, boolean startsWithZeroScore, boolean firstColumnIsNonDiscardableCarryForward, boolean hasSplitFleetContiguousScoring, - boolean hasCrossFleetMergedRanking, Integer maximumNumberOfDiscards, List fleets) { + boolean hasCrossFleetMergedRanking,Integer maximumNumberOfDiscards, boolean oneAlwaysStaysOne, List fleets) { Regatta regatta = getService().getRegatta(regattaIdentifier); if (regatta != null) { SecurityUtils.getSubject().checkPermission(SecuredDomainType.REGATTA.getStringPermissionForObject(DefaultActions.UPDATE, regatta)); @@ -1628,7 +1645,7 @@ public class SailingServiceWriteImpl extends SailingServiceImpl implements Saili getService().apply( new UpdateSeries(regattaIdentifier, seriesName, newSeriesName, isMedal, isFleetsCanRunInParallel, resultDiscardingThresholds, startsWithZeroScore, firstColumnIsNonDiscardableCarryForward, hasSplitFleetContiguousScoring, hasCrossFleetMergedRanking, - maximumNumberOfDiscards, fleets)); + maximumNumberOfDiscards, oneAlwaysStaysOne, fleets)); } @Override @@ -1700,7 +1717,8 @@ public class SailingServiceWriteImpl extends SailingServiceImpl implements Saili protected RaceColumnInSeriesDTO convertToRaceColumnInSeriesDTO(RaceColumnInSeries raceColumnInSeries) { RaceColumnInSeriesDTO raceColumnInSeriesDTO = new RaceColumnInSeriesDTO(raceColumnInSeries.getName(), - raceColumnInSeries.getSeries().getName(), raceColumnInSeries.getRegatta().getName()); + raceColumnInSeries.getSeries().getName(), raceColumnInSeries.getRegatta().getName(), + raceColumnInSeries.isOneAlwaysStaysOne()); fillRaceColumnDTO(raceColumnInSeries, raceColumnInSeriesDTO); return raceColumnInSeriesDTO; } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/SeriesDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/SeriesDTO.java index 9d08a9154aa..f31f313aa5b 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/SeriesDTO.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/SeriesDTO.java @@ -19,6 +19,7 @@ public class SeriesDTO extends NamedDTO { private boolean hasSplitFleetContiguousScoring; private boolean hasCrossFleetMergedRanking; private Integer maximumNumberOfDiscards; + private boolean oneAlwaysStaysOne; public SeriesDTO() { super(""); @@ -26,7 +27,7 @@ public class SeriesDTO extends NamedDTO { public SeriesDTO(String name, List fleets, List raceColumns, boolean isMedal, boolean isFleetsCanRunInParallel, int[] discardThresholds, boolean startsWithZeroScore, boolean firstColumnIsNonDiscardableCarryForward, - boolean hasSplitFleetContiguousScoring, boolean hasCrossFleetMergedRanking, Integer maximumNumberOfDiscards) { + boolean hasSplitFleetContiguousScoring, boolean hasCrossFleetMergedRanking, Integer maximumNumberOfDiscards, boolean oneAlwaysStaysOne) { super(name); this.fleets = fleets; this.raceColumns = raceColumns; @@ -38,6 +39,7 @@ public class SeriesDTO extends NamedDTO { this.discardThresholds = discardThresholds; this.firstColumnIsNonDiscardableCarryForward = firstColumnIsNonDiscardableCarryForward; this.maximumNumberOfDiscards = maximumNumberOfDiscards; + this.oneAlwaysStaysOne = oneAlwaysStaysOne; } /** @@ -50,7 +52,7 @@ public class SeriesDTO extends NamedDTO { otherSeries.getRaceColumns() == null ? null : new ArrayList(otherSeries.getRaceColumns()), otherSeries.isMedal(), otherSeries.isFleetsCanRunInParallel(), otherSeries.getDiscardThresholds(), otherSeries.isStartsWithZeroScore(), otherSeries.isFirstColumnIsNonDiscardableCarryForward(), otherSeries.hasSplitFleetContiguousScoring(), otherSeries.hasCrossFleetMergedRanking(), - otherSeries.getMaximumNumberOfDiscards()); + otherSeries.getMaximumNumberOfDiscards(), otherSeries.isOneAlwaysStaysOne()); } public boolean hasSplitFleetContiguousScoring() { @@ -141,4 +143,12 @@ public class SeriesDTO extends NamedDTO { public void setMaximumNumberOfDiscards(Integer maximumNumberOfDiscards) { this.maximumNumberOfDiscards = maximumNumberOfDiscards; } + + public boolean isOneAlwaysStaysOne() { + return oneAlwaysStaysOne; + } + + public void setOneAlwaysStaysOne(boolean oneAlwaysStaysOne) { + this.oneAlwaysStaysOne = oneAlwaysStaysOne; + } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/client/images/boatclass/BENETEAU_FIRST_35.png b/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/client/images/boatclass/BENETEAU_FIRST_35.png new file mode 100644 index 0000000000000000000000000000000000000000..94dbfcb04508a47e6c0a21bfde1b4dbf22855206 GIT binary patch literal 5115 zcmbtY^;;8;^WT_+lHv$KdLyKUbPXgoNrn0|0K-A~o(m^t1RkO(<6l*XmZNUx`jy}r$4G} zXf`TSk552*rsm0RS{N;;MD7QmCc^yR0!2z#$fG-s!Dij%J{};wulT!kQutEq$IT7E zd@D2)B^%nbs>B?tgIDsv_3G~$BQsS9)KE+@Bqn1~G#-nzxm@y?0|#T~AzW#T-SJ8< zaoTs#eJcEzFi4OGMx=T3Cf9Y=A^$vxW%A-MOXdEHcx=b(jY+6OS4a+EkIK$Kb$f!~ zVz}2HkM+f==>1QN;^BLIwYEKg4p% zbp1@ek_DFVgxVlZ^YT${WcwwYaEpH}cZflj6Sp`Kf96b&UKy$t=A7rm8@j5f5Ijjo z+Qnz?xL!TAT^ga5K%aQngM`J;k6S2Mr1#5$t;8<1Z~`Uvr&h=riRp?i=l0VQzUc0F=UpwAQWQOlt}q&66H3^@~7oyB2oaLp`h z5;6lrI=kh_S7L;{sArIe+Ysc%QWRz^tPanIl6;@7JFi)nmWsJhcl0zztIB+ALCp6w|Z9$c9j#?28Iv4 zkQb|GW_8cs!gEi-!arIRbMVY(L`}8JQ98o+z@0>-h0j+}HPmb`cvxvMB+e?qr1!$lH1N`o9Cc7>O2dnE@zkB>QSsT80@7X$b);h8L07p&iQ z6*S-eqow)~?wWX(=YalLtpItyHq*v8#dg^Hf6MjuQ5+44E=S6cpprq-(e^@kbP zFzE|W=CX@8NLKQq=VAYe-saK+Mh)|gmcL>2QfFqlyNtygg}R$X74kx8PCmk3qDa%Y z-mX9LRU<@afR@=2Q5aJy26oH-`b>cU>O8YhhIo^h-q%CqqeBQZ+wBR8$Czdq? z^|3=Sb7c1J9@v)?2Tgsqd8{!4IkgH=AuUgF6%RVA=eug4W2QsDf;IDm@G12qVTFlB z`d8b1U}#|#aCh^BY%;0LzWV8_9Zc>1$l;=Bic~t0sFdh0#&*;|I=>ohL}oXQ@Zina&?inmL9V zO5DoI5^H~hkQzH%tLhY9t^U#b%5}AF^3r?@ z7H7m|YCLmoXCLU&wEKpl5X%8s&pDR~8Lp-6;N|z3tDWW4)?knr7qUYXrBihwSH#hKWv(S$Xhz!6kMXJm zqiJt`?nj-ob%hQet$t3fWNqyGxl!{ao<_slZgXx6sedx{dxVQ1H%edWymqF>6}xyE zSzH;Rf&%_&s~3-R5Km}A>&~YvBpB=Cb^N}E<{WQW7#Isc zne@rzz1|&Q*(5k}svaJRwQ>f4AZmbp7Jy(I-A$*Y5eBm}=x>^e!cIA;$d*!gIrYi; zO!wWtZYulRw}GWvsNIKY>Sf#h(e40;EShgcp#p1ihc4k9=f=2k(al)1q-bE+!ZoTd z&KkFwE{}7~uPvd#17ZO>xnZEMxptSa}T+@{MCny8Ho|2fyW^ICGH;YW|3{ z<<$m{W5CMUIMGQTA(**xwmTVx?oY-VG9D}9n!na41za+qQ>^cJ^ebo65{_8Fx272B zRGDXWyB;7Y{KMxK7>);O5Jf`w&~2>w2hu;DWG5;)I?<`0=&e9(g?4;*0wzY5y|Umc zp`M89=p+iItwr%F6~+(D2H$hLijHaFjwz~fZ;J87o{ zP$|d}&v%p1jKY({c*B;qaYP*-MyoD6I%%=nWhv2+ti*){y;w+64lCtlT%s*8dav5I z|LJ(VE8H5zyZe(tIEq)cq^JV3N^ZXK$6AAgX<99rmFrape2v=dm43Uz1&b}Xd&>0= zac$W*>u;l*>4EKyzl_#)NH@R_UC90R0`cH_XbIgT`N}X?Mwt3B{-YObS`ETM`-^w2 z^vLCN9Ms;KO(%1A>%h8*I-ya*Gc6Moo9>ldTtC_ZnIJwnAw!n0m;o z@u3M9=)NfLty<+HYZu8t@^Lr(qJKXoA6SlIg}g(~`}9IL;FZx0$LFYwPy`dhc1E*n zRMyW}6T=@>e9fU5Xc_vVI-JtqAz{gs1f#p38Ons?|It&hLbly0cDSnxd$NfncBDzE zc(jsL=%kzHdwd*F+hJ`lygoe(q_W~z2yhhX>DCSpRe$bkWpEsDz7#P2 z%pB+JH=5@~vQbnq?BC$w!}op*>ioV&71YQ#C2MDQPb6hC@M~D^PRnO-qT-IT$kD3q zyzZKt{(;?b^MOiGd^U@4}FZ;ruqRfK=j)p8amzLh|YCDkNee$R}b8Wmu^ z#R8%RtB44iYgGQ!?opqiR~?}Z+K!T^+a2<~7(WW@xtEgA9Ze_3Tk|gX^XFmnR8X#QnBc zR!samY}IA&i0^sDou=&rgSC<@GQiS@7W$%gw@Y0ss-;47{-}Gy0398p)0C_lVxeo} zI&_pC!af>|Yt-sw_p4_sLb0lDCL?s~*km5g*cQ^eSM(WqEjWejXo8GP;-@7H+^6b( zV~bDZ*cUL2Q>2@b?{8}`VERA{ls{!06$m|eK8McS0~4L?2WrRo+N0P>|A-tBOW)|n zkYqv-5lt(W52;-n2X8e>`L2sEz3mv@@)>K5yl<_2)TdmE0;MQ#IUUY0UY!tAxX_j& zrz->(xDf)_(5?ivnNg`Lj3Cx8D$~O1ZF$_Dv(dt2yVp>=nG`9g>R4H6+Te*lu%;r9 zy2JGASC_}2EsvNTD=jR!+SuXVAVrF#**o3aS|9jD1YKG){Q4}e> z9YFo5B3*ejIlyIO({;U`34rHGs9{a6$B=NcF~@|v`DA{bZLtW66z{N6S=Bn>E08hF zfX-Kdb4sMJi1rs{<@p;@hDRq!ZIo?pQ8K3EPwf>$Mpf)ySl2-?`V@&dgHXE%qBC!FnL^az zJ%zw!F&xZ>T5Vy~ue|J>5eKPIc-R}s=UDD@aV)fJ`OX;WCwCP| zbH8a);6Z^qlSzV4wu07*2#T08gSUcmtvB!f@*+I5=5 zL{V|^yqL*`D)yd^;i=BXXllfv;Q5I(UJH=FHSp7Q`RUAL&k5~a@6(@gUc%D&bSfCL zZQ*R+Xi?duW1+}jI^BOw`EGZ$Mf1Kz?7j%)5b}Ux>gs%)gXMY9h}5Q-GPajG%ZBxP zPcNT(PoBN|R!BcQ8K7qg>c0-Oq1~v<$>03HlrXxgG%~%w)gxTKY5_nK z)ZZ@;SN|N+sw19H)ci8{1M@jYh{GPSGJOBL8F1z053P8Z01ifC8SsIZojfZT1hX}3 zN9nLjqS+O7*YhFzS9Jh6lyq6~GhYMa{gDL(9+L*p8&Mq-gt2al)dRGrFrE!fM;2vb zu}^KE=sqw`>FRX@rafsCxPssWtT*{OF*N4tPncFVY-s^&96oKDTibIovHeo~d2&(9 zi8=?=qL`jP(SfA2b<>@>my?8^j>zDn5Gi?)UltXbD5vepK(2os+n`&2Unlw>!fmw; z49?3bQf`s;JQjP8H2D6EkqD+DH8fieN1iBqCj-J~_TD4!`1`!=atfAbfH7;M1uH{q53_nk(Z z>6}KBiE!485O3K=-ZM+Uh|H^Ex{?C7E-Goi{mL zfARN@bH*5ll*Bd8z@QADYU!Z{pgkqJsiI|2ac9eu@82hx<~K@c=yL#Na!+5!vzANa6(kMJBGS3uI*x1Nb;j~XG0qRSgAj~ySSf6RIBG!qrCH+ zcU)cSVHbbDYI17v2Ts&Bk7rQK#lqEs6d>z=axgWDtVpmQi>UR6Y-utOLrQd~Pau&x zy|2iZao5V_ornNWUOZaKtwg*DUhomAn1p~ftaADr>NBUS{ezL3+9s2FR$dMnpYiuR z@(rwqM*4-3ZKpZOF0{{i)&txx|BK0(IBCBuF-?i@z#opykptOlv}#2pKk9B7zT66-IUL#EPAv+VvmH! zAi3jSdQXc;N>6GJ69As0fZv`MF(47nBI?}F*x6=*M&B5D^DM=Jo$6A9L}I`<|1bIP z!YgYkr3Je*rEt$XE=iv!Tl!%IH&_K>a)}Et%m`_-8{;x$Y4u tQq+I>DbqUzH)httZS!ei|Np|5)Td%=-Ky#s#ML(sfYdb5C|5(j{vV`8&FBCC literal 0 HcmV?d00001 diff --git a/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/client/images/boatclass/BENETEAU_FIRST_45.png b/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/client/images/boatclass/BENETEAU_FIRST_45.png new file mode 100644 index 0000000000000000000000000000000000000000..50d6dc4b8ee4076e7039cce5dc9ac059afca2732 GIT binary patch literal 4920 zcmbt&cQ_l4^M8gdR;axaG2dvY+GnI|h52See#@f=+RZ&Z>gnxK zPRiBq&7OgkDf~=t)p7V<*K8*ryb|P200ofQsn|RPaO3Y;!ULs}okD7XUC8uKAQ;2j z{L>Yj;v$|5W+2IjOn2siNMmN2rQ|A96PetP(ySt9nl*hS-<-0x=!hx>|9DFfRai?R zq+VWYVDmYuBL|utcdE!c95_5@;qMAoK>HDcmSQEF>d1VOjvm=5-zZREps<>>-nL_< zn%?nk^slVFD`>yxLElgXQ=1~fSV5fPR((;Mdg6{KTAH}0tCmo`b8a?zQ&PudwBL7P z!ykvn>nGlAx4Kh&p}t0KYs(I@M>`*@1T1@^`3QRl09^1j2#!{Gkc(qR9yCXqMK%riVIn7V>d3A{!hCV!%1-L#0%j zy3YxU(rX(cEL)+Hb5^5_n(}2gnpebqQ&<9V78FNAXe95d7-1gnr6#R2Q}(ce{K)J$ znyh252)C`NBDY=ok)@;9o@}3qmT8h{;dJ<%h4^SUnxpA_d1CKAOV&ke zrgc7Fm!Hdr#;cF8&An?~uFnwD8oK}0XF+Z)yj|?F)Y{{WwP!DX_2eigAlHEv{0 zctrCeZ1F+_!FctVB~J?qC1t}jJQE==lpp4UuALa`QYjggp@@aH=F8^vW36WLT1Fc2 z8t=(BwHT!W56R>_vtt^O+w}3|EM6a;jxc`9qf~TLL(FvjaO9>X@s^&oc$v7opdRhg zpTif8_{2-j6vMFCG|vDPH5`t~f!mJ54&-1X{NBPvuvWraWoPs7P8IRw!5dgaz!UT# zUYrbIJ$<^U--w6ZK9XoZOCtuMzCZjnctYC92Jkb)O#WfBaCyAGIQ?_>V7e?)ZArlC zLy1F+@>CKNAyUeI+mRyfq|~p&1%K#Jx|0_9(;CoWVZYpW2d8fCiKRu5XHSBx;XY2a#HwG1%r{jnW#^37b55XAfOcOELDr-^5 zVZ=>RM}8{lK4R_npy9J69Xez!H5vYFe?I{lQ@xWS35>7Ro~K9b92eYxR$ma6;EBUv zte)c@Uog% zlI{lReWBc;KEmyvyGjHx)m?*neplrK!5v`|>J{C^TVtnTUn>dG#*TXsY>OJ-TIi5? zXcz?N8ka3+;cCfY&cq!th;HF%@(mvl&iv&B!tr-FfzxCwS$$-mejjTfV55m1==_N{ zBCAZ77MJzy*(6&U@l#q8tv_4CAEfI%0}e!4B>owqj2j~)Q4e3D~*oJ zVOH^=cyH#{io}(4lA4>zk;zh6Z@WoMY5Z5f21MjCg7AZ8Zmw%fFt`P-#m92$$zKPu zD8b>Qt1RZ9mdbsu@T}PIcmIhR%rK-=sU4kC7LQQC;%Ii?3e|Z27OwxdKb^HwRPX%z z?XbLnt+=Qg_5FG3H;o1z>#AQEP}7%zoWt8NqSfo9!X|BJT^vd8+GV{Gmbd?jKU=>Z zl;2vR3Qc}<@F6EkyPk!YZ4R)M+$~4V^(raGPk(I$w|t+d|NdUV^C#buX3MbAa1}ad z2H!6Hi7O$2XNM(Y=OU61Cm{@|J;d;^GWX)=_5EuT4~2#dOoO>F9egpBFAQtd_~Hn! z72C3ar+Q+ykbE8K3@IOMf7{$GtM<`E_Q=!}3q`z4;BEFvmnw zwVWf`$G=-?ez-VU?8Y|qUM0%}9YhorKKhWl>%zsA1A|3bx(380eB$lmOD*WI%Nk9T zYY}6Pn^cP%PTmIA9&(2BL4U0zVy|`^;j`~e2z6n5gm+@_2_b0KoP7; z#XfHpU?G7_|3L04PuSxyfHK~R)AbJwNYaRl>e+3{kMb1@JIRKBbXnt#D5vbq($l_< zWPWpKR{9CyGp!QALBe$gkkfAXO%P&<*H1#>uNA^=bpD|!O}sPQ$T)(>#)1U z%M}1BKd3T1S*&{;@3p(jjy_E-tQ)bj_T;KX`f$k9`dlfF?q2|KPcA3JV6sxy>H#k^ ztr)|$e--6}pn$lm*Gna`GEYMl&Dlum0ZBVc@^{WPjD*>Z=lsruHWS)kOU1|&nTYS9 zM&vRXcKF*k00YLrzFL)wuso;e#ll$(2^@Ot*eGAr@l02e?cZ>>bSS&{N7z*x&hreo z>F@|QK~UBa&RCI=aP>cgRBRSkZaYdKX-(GXUSAU<1i?j1rzR&FPw>9D-Ujnx*zc6p)Z6r|@K`(9GIrD+DsC-0Uz#ZxW4 zBEto^h=Ib9*`^pJeNho+KW9GV;VT{XCfE2sXik4?cb%$<%gw z5SA_rbXKt7X&C{kVr?6ye(KqoEn!r&;&0-znqX&wETAJhmZMr|D3TX!*woe zpZAl$-xkj-ZLw_VssaFowuyl$MR5XaOt%9c%99FZKBM5rRPm_8+`|)VPkG#_Hq#C5 zKH2JP1rw?!*Ho4ske$UVrC(|QI=)qK>Jidq=s6FmQ-v;qeF{lREaLO7l&FWP<@_JZ za(mU&H%bA^x;XMbYoanISj*PMLR&6*gA^cn=Q3kz=(ukn(0#mU^T*NOlRPKwKolM)qhEEQhzAm3HdL&h)aQ)AdEAIo?aIMy~fuzt)dW zLn$^QHo2#r2M`hIwKQ_m=DBUH zA&GwKio4G^lqo8PDTsUC&)1rvqNrRD3i{Q{0wr*d-Ezr8Y)vyL0BIs5fCCL;WL;)> zW0i(W^X>Lzc|zH5G?s7Vw&qHJ2dID!|EBQ0_+*g}_x7I{-YD*5UeSjsR8*C0&>6Lh zDrk8MCZ-xHMMlWuZn|}Kg38<(C5NW0H8FXq<9{S=o}oW-_sM)trWUB47qotOU?H%K_8LygJDV`7N`!G1h73tMc_?jdY2F-t zCkVpry%Go~B7ME5yQpP^cm3tbtCq?XTM&db`q#HoD~`PCF=wwfed7|{#qm&0G5anS zPcNH(yVb-|?CUqPAwK7kV2m-P1{ZMQu-3`_TLAks!tMQQ7~L!^WQ2cQ+_!iXvs`6``_1bJ56rGl4$~n~$WSDb5|jqwNpJ zug>%c;_MNag{5z&B6Hw5$w^@MQPyntG)to_`!#GYSD#5*PjBLi7rv5-zx4ru)Pr6# zb6p25RukC$!>y)bHR#{1>fFfqq<>cukqRsm$61a`GnDpzFJ zFTZP;0X_;>FP2n$s!3p;RYq?b`;qM@3U^qqm6R!=X^T`6+M1A zIKSiiLF8=#yz=yFrYXr6nBpNyfyEJ0mteI>qtX$HGQ=_0FIrP^pq}v*?|fQJU7ZHV z)3cx!=uW;hM+~Jd`BN2`F*Y$>U~rrOG7w{lavL&Grlin8l1HA$IJmO%OBByg26{i_ zYg~+JW#)oR?M*EC1UHnhq9=YCem;UE&d-*ukkx;Q>Jg(uQ9D2vr~J zc>-f@PF}Kc3LXCixizvXD*sQa7Nn3K8@7=C=ZJw6jdx*PT-9n_Ojox{YnfH-o{QQd z@;(tul3fu9P^Ds%szBD-KYV|wPMZ{6gLA}+vLiaFmmmP8ZeMp@a?*W-r%p6s02-$ z53*rN9P~P1n<*Rb9fwJ3H#E&io*k&;&164IbJ{5;PhaA#d_VE^N5jUFhVhY3I00F|5*w>`djH)vUOY*el9bYT7-i>LZ+Eh(aV{^QzU zqj^?+3_IGKO&or!A_(#i(gxaaOqT{MgW_=7^LDMPhd#cTnR+9+=;YZLQ66sAND?{p z>iEsNQd;2j*rhhEcc52X+Y)^`^p8r3W7mNknYn>ZyYtR3_!T@Mv3!`cS HT=f3|m-_>B literal 0 HcmV?d00001 diff --git a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndLoadingEventsAndRegattas.java b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndLoadingEventsAndRegattas.java index e8dae67ec90..5cb93372950 100755 --- a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndLoadingEventsAndRegattas.java +++ b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndLoadingEventsAndRegattas.java @@ -685,6 +685,7 @@ public class TestStoringAndLoadingEventsAndRegattas extends AbstractMongoDBTest Regatta regatta = createRegattaAndAddRaceColumns(numberOfQualifyingRaces, numberOfFinalRaces, RegattaImpl.getDefaultName(regattaBaseName, boatClass.getName()), boatClass, regattaStartDate, regattaEndDate, /* persistent */false, DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), OneDesignRankingMetric::new); regatta.getSeriesByName("Medal").setStartsWithZeroScore(true); + regatta.getSeriesByName("Medal").setOneAlwaysStaysOne(true); MongoObjectFactory mof = PersistenceFactory.INSTANCE.getMongoObjectFactory(getMongoService()); mof.storeRegatta(regatta); @@ -692,6 +693,7 @@ public class TestStoringAndLoadingEventsAndRegattas extends AbstractMongoDBTest Regatta loadedRegatta = dof.loadRegatta(regatta.getName(), /* trackedRegattaRegistry */ null); assertFalse(loadedRegatta.getSeriesByName("Qualifying").isStartsWithZeroScore()); assertTrue(loadedRegatta.getSeriesByName("Medal").isStartsWithZeroScore()); + assertTrue(loadedRegatta.getSeriesByName("Medal").isOneAlwaysStaysOne()); } @Test diff --git a/java/com.sap.sailing.server.gateway.serialization.test/src/com/sap/sailing/server/gateway/deserialization/test/GPSFixMovingJsonDeserializerTest.java b/java/com.sap.sailing.server.gateway.serialization.test/src/com/sap/sailing/server/gateway/deserialization/test/GPSFixMovingJsonDeserializerTest.java new file mode 100644 index 00000000000..6f156a4ac6c --- /dev/null +++ b/java/com.sap.sailing.server.gateway.serialization.test/src/com/sap/sailing/server/gateway/deserialization/test/GPSFixMovingJsonDeserializerTest.java @@ -0,0 +1,35 @@ +package com.sap.sailing.server.gateway.deserialization.test; + +import static org.junit.Assert.assertEquals; + +import org.json.simple.JSONObject; +import org.json.simple.parser.ParseException; +import org.junit.Before; +import org.junit.Test; + +import com.sap.sailing.domain.common.tracking.GPSFixMoving; +import com.sap.sailing.domain.common.tracking.impl.GPSFixMovingImpl; +import com.sap.sailing.server.gateway.deserialization.impl.GPSFixMovingJsonDeserializer; +import com.sap.sailing.server.gateway.serialization.impl.GPSFixMovingJsonSerializer; +import com.sap.sse.shared.json.JsonDeserializationException; + +public class GPSFixMovingJsonDeserializerTest { + private GPSFixMovingJsonSerializer serializer; + private GPSFixMovingJsonDeserializer deserializer; + + @Before + public void setup() { + serializer = new GPSFixMovingJsonSerializer(); + deserializer = new GPSFixMovingJsonDeserializer(); + } + + @Test + public void testGPSFixMovingDeserializer() throws ParseException, JsonDeserializationException { + GPSFixMoving gpsFix = GPSFixMovingImpl.create(8.0, 55.0, System.currentTimeMillis(), 5.0, 14.0); + + JSONObject gpsFixSerialized = serializer.serialize(gpsFix); + GPSFixMoving gpsFixDeserialized = deserializer.deserialize(gpsFixSerialized); + + assertEquals(gpsFix, gpsFixDeserialized); + } +} diff --git a/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/deserialization/impl/GPSFixMovingJsonDeserializer.java b/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/deserialization/impl/GPSFixMovingJsonDeserializer.java index 4b894f21512..d775bd90249 100644 --- a/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/deserialization/impl/GPSFixMovingJsonDeserializer.java +++ b/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/deserialization/impl/GPSFixMovingJsonDeserializer.java @@ -24,7 +24,7 @@ public class GPSFixMovingJsonDeserializer extends TypeBasedJsonDeserializer() { - @Override public RegattaLeaderboard call() throws Exception { return getService() - .apply(new CreateRegattaLeaderboard(new RegattaName(regattaName), regattaName, discardThresholds)); + .apply(new CreateRegattaLeaderboard(new RegattaName(regattaName), regattaName, discardThresholds)); } }); } @@ -871,7 +870,7 @@ public class EventsResource extends AbstractSailingServerResource { series.put(LeaderboardNameConstants.DEFAULT_SERIES_NAME, new SeriesCreationParametersDTO( Arrays.asList(new FleetDTO(LeaderboardNameConstants.DEFAULT_FLEET_NAME, 0, null)), /* isMedal */ false, /* isFleetsCanRunInParallel */ false, /* isStartsWithZeroScore */ false, /* firstColumnIsNonDiscardableCarryForward */ false, - /* discardingThresholds */ null, /* hasSplitFleetContiguousScoring */ false, /* hasCrossFleetMergedRanking */ false, /* maximumNumberOfDiscards */ null)); + /* discardingThresholds */ null, /* hasSplitFleetContiguousScoring */ false, /* hasCrossFleetMergedRanking */ false, /* maximumNumberOfDiscards */ null, /* oneAlwaysStaysOne */ false)); return series; } diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RegattasResource.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RegattasResource.java index ec6618e1d08..52c8a4db103 100755 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RegattasResource.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RegattasResource.java @@ -2645,15 +2645,16 @@ public class RegattasResource extends AbstractSailingServerResource { Regatta regatta = getService().getRegattaByName(regattaName); if (regatta != null) { SecurityUtils.getSubject().checkPermission(regatta.getIdentifier().getStringPermission(DefaultActions.UPDATE)); - String seriesName = (String) requestObject.get("seriesName"); - String seriesNameNew = (String) requestObject.get("seriesNameNew"); - boolean isMedal = (boolean) requestObject.get("isMedal"); - boolean isFleetsCanRunInParallel = (boolean) requestObject.get("isFleetsCanRunInParallel"); - boolean startsWithZeroScore = (boolean) requestObject.get("startsWithZeroScore"); - boolean firstColumnIsNonDiscardableCarryForward = (boolean) requestObject + final String seriesName = (String) requestObject.get("seriesName"); + final String seriesNameNew = (String) requestObject.get("seriesNameNew"); + final boolean isMedal = (boolean) requestObject.get("isMedal"); + final boolean isFleetsCanRunInParallel = (boolean) requestObject.get("isFleetsCanRunInParallel"); + final boolean startsWithZeroScore = (boolean) requestObject.get("startsWithZeroScore"); + final boolean firstColumnIsNonDiscardableCarryForward = (boolean) requestObject .get("firstColumnIsNonDiscardableCarryForward"); - boolean hasSplitFleetContiguousScoring = (boolean) requestObject.get("hasSplitFleetContiguousScoring"); - boolean hasCrossFleetMergedRanking = (boolean) requestObject.get("hasCrossFleetMergedRanking"); + final boolean hasSplitFleetContiguousScoring = (boolean) requestObject.get("hasSplitFleetContiguousScoring"); + final boolean hasCrossFleetMergedRanking = (boolean) requestObject.get("hasCrossFleetMergedRanking"); + final boolean oneAlwaysStaysOne = (boolean) requestObject.get("oneAlwaysStaysOne"); Integer maximumNumberOfDiscards = null; if (requestObject.containsKey("maximumNumberOfDiscards")) { maximumNumberOfDiscards = ((Long) requestObject.get("maximumNumberOfDiscards")).intValue(); @@ -2678,7 +2679,7 @@ public class RegattasResource extends AbstractSailingServerResource { getService().apply(new UpdateSeries(regatta.getRegattaIdentifier(), seriesName, seriesNameNew, isMedal, isFleetsCanRunInParallel, resultDiscardingThresholds, startsWithZeroScore, firstColumnIsNonDiscardableCarryForward, hasSplitFleetContiguousScoring, hasCrossFleetMergedRanking ,maximumNumberOfDiscards, - fleets)); + oneAlwaysStaysOne, fleets)); } else { throw new IllegalStateException("RegattaName could not be resolved to regatta " + regattaName); } diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/trackfiles/impl/ExpeditionAllInOneImporter.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/trackfiles/impl/ExpeditionAllInOneImporter.java index 73c1cc1ddeb..711e35c56db 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/trackfiles/impl/ExpeditionAllInOneImporter.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/trackfiles/impl/ExpeditionAllInOneImporter.java @@ -134,7 +134,7 @@ import com.sap.sse.security.shared.impl.SecuredSecurityTypes.ServerActions; * The imported {@link GPSFixMoving} and {@link BravoExtendedFix} tracks aren't mapped to a {@link Competitor} by the * importer. Instead the IDs of the imported tracks are contained in the result and are expected to be mapped by the * user afterwards. - * + * * This importer is intended to be used by {@link ExpeditionAllInOneImportServlet}. */ public class ExpeditionAllInOneImporter { @@ -142,14 +142,14 @@ public class ExpeditionAllInOneImporter { private static final Logger logger = Logger.getLogger(ExpeditionAllInOneImporter.class.getName()); private static final double VENUE_RANGE_CHECK = 10; - + /** * For sessions created automatically from start times found in the log, tries to set the tracking start * time this much before the race start, unless it would be before the first fix received which then would * provide the start of tracking time instead. */ private static final Duration TRACKING_DURATION_BEFORE_START = Duration.ONE_MINUTE.times(5); - + /** * This prefix is used to create race columns based on start times automatically. */ @@ -219,7 +219,7 @@ public class ExpeditionAllInOneImporter { this.serviceFinderFactory = serviceFinderFactory; this.context = context; } - + private static class TimePointsOfFirstAndLastFix { private final TimePoint firstFixAt; private final TimePoint lastFixAt; @@ -235,7 +235,7 @@ public class ExpeditionAllInOneImporter { return lastFixAt; } } - + private TimePointsOfFirstAndLastFix importFixes(final String filenameWithSuffix, final FileItem fileItem, final ImportResult jsonHolderForGpsFixImport, final ImportResult jsonHolderForSensorFixImport, final List errors) throws AllInOneImportException { @@ -446,7 +446,7 @@ public class ExpeditionAllInOneImporter { throw new AllInOneImportException(e, errors); } } - + /** * Checks whether the current {@link Subject} is permitted to created the {@link SecuredDomainType#TRACKED_RACE * tracked races} named as specified by {@code trackedRaceName} and the additional strings in @@ -465,7 +465,7 @@ public class ExpeditionAllInOneImporter { return checkTrackedRaceCreationPermissionRecursively(regattaName, additionalTrackedRaceNamesIterator, action); }); } - + private T checkTrackedRaceCreationPermissionRecursively(final String regattaName, final Iterator additionalTrackedRaceNamesIterator, final Callable terminalAction) throws Exception { if (additionalTrackedRaceNamesIterator.hasNext()) { @@ -488,7 +488,7 @@ public class ExpeditionAllInOneImporter { * result of {@link #getStartTimesAndStartAndEndOfTrackingTimes(Iterable, TimePoint, TimePoint)} and passes those on * to the {@code consumer} passed. *

- * + * * Idea: use this to first determine all race names to check permissions ("dry run"). If no permission problems * exist, use a second call to actually perform the race creation. */ @@ -509,7 +509,7 @@ public class ExpeditionAllInOneImporter { /* fleet name */ session.getC())); } } - + private Iterable getNextRaceColumnNames(Regatta regatta, int howMany) { return getNextRaceColumnNames(getNextAvailableStartBasedSessionCount(regatta), howMany); } @@ -522,7 +522,7 @@ public class ExpeditionAllInOneImporter { } return result; } - + private Triple createSessionForStartTime(TimePoint startTime, TimePoint firstFixAt, TimePoint lastFixAt, List errors, Regatta regatta, RegattaLeaderboard regattaLeaderboard) throws AllInOneImportException { @@ -577,7 +577,7 @@ public class ExpeditionAllInOneImporter { } return new Triple<>(trackedRace, raceColumnName, fleetName); } - + private void ensureEventLongEnough(TimePoint firstFixAt, TimePoint lastFixAt, UUID eventId) { Event event = service.getEvent(eventId); TimePoint startDate = event.getStartDate(); @@ -588,7 +588,7 @@ public class ExpeditionAllInOneImporter { if (lastFixAt.after(endDate)) { endDate = lastFixAt; } - Iterable leaderboardGroups = StreamSupport.stream(event.getLeaderboardGroups().spliterator(), false).map(t -> t.getId()).collect(Collectors.toList()); + Iterable leaderboardGroups = StreamSupport.stream(event.getLeaderboardGroups().spliterator(), false).map(t -> t.getId()).collect(Collectors.toList()); service.apply(new UpdateEvent(event.getId(), event.getName(), event.getDescription(), startDate, endDate, event.getVenue().getName(), event.isPublic(), leaderboardGroups, event.getOfficialWebsiteURL(), event.getBaseURL(), @@ -757,7 +757,7 @@ public class ExpeditionAllInOneImporter { seriesCreationParameters.put(seriesName, new SeriesCreationParametersDTO(fleets, /*isMedal*/ false, /* isFleetsCanRunInParallel */ false, /*isStartsWithZeroScore*/ false, /*firstColumnIsNonDiscardableCarryForward*/false, /*discardingThresholds*/ null, - /*hasSplitFleetContiguousScoring*/ false, /* hasCrossFleetMergedRanking */ false, /*maximumNumberOfDiscards*/ null)); + /*hasSplitFleetContiguousScoring*/ false, /* hasCrossFleetMergedRanking */ false, /*maximumNumberOfDiscards*/ null, /* oneAlwaysStaysOne */ false)); final RegattaCreationParametersDTO regattaCreationParameters = new RegattaCreationParametersDTO(seriesCreationParameters); regatta = service.apply(new AddSpecificRegatta(regattaNameAndleaderboardName, boatClassName, /* can boats of competitors change */ false, CompetitorRegistrationType.CLOSED, diff --git a/java/com.sap.sailing.server.gateway/webservices/api/v1/gpsFixesPostDoc.html b/java/com.sap.sailing.server.gateway/webservices/api/v1/gpsFixesPostDoc.html index c5d4243c868..795441b7425 100644 --- a/java/com.sap.sailing.server.gateway/webservices/api/v1/gpsFixesPostDoc.html +++ b/java/com.sap.sailing.server.gateway/webservices/api/v1/gpsFixesPostDoc.html @@ -12,7 +12,7 @@

URL: /api/v1/gps_fixes

Description: -

Adds an arbitrary number GPS-fixes that originated from a device with a unique identifier to the GPSFixStore. +

Adds an arbitrary number GPS-fixes that originated from a device with a universally unique identifier (UUID, all types accepted) to the GPSFixStore. These can be mapped to a competitor or a mark via the DeviceMappings in the RaceLog-Tracking connector.


diff --git a/java/com.sap.sailing.server.gateway/webservices/api/v1/regattaUpdateOrCreateSeries.html b/java/com.sap.sailing.server.gateway/webservices/api/v1/regattaUpdateOrCreateSeries.html index ffa1d0aff58..7b08e86b107 100644 --- a/java/com.sap.sailing.server.gateway/webservices/api/v1/regattaUpdateOrCreateSeries.html +++ b/java/com.sap.sailing.server.gateway/webservices/api/v1/regattaUpdateOrCreateSeries.html @@ -43,6 +43,7 @@ "hasSplitFleetContiguousScoring":false, "hasCrossFleetMergedRanking":false, "maximumNumberOfDiscards":1, + "oneAlwaysStaysOne":false, "resultDiscardingThresholds":[1,2], "fleets":[ { diff --git a/java/com.sap.sailing.server.interface/src/com/sap/sailing/server/interfaces/RacingEventService.java b/java/com.sap.sailing.server.interface/src/com/sap/sailing/server/interfaces/RacingEventService.java index e50ae13ca99..8c399173136 100644 --- a/java/com.sap.sailing.server.interface/src/com/sap/sailing/server/interfaces/RacingEventService.java +++ b/java/com.sap.sailing.server.interface/src/com/sap/sailing/server/interfaces/RacingEventService.java @@ -77,6 +77,7 @@ import com.sap.sailing.domain.leaderboard.LeaderboardGroupResolver; import com.sap.sailing.domain.leaderboard.LeaderboardRegistry; import com.sap.sailing.domain.leaderboard.RegattaLeaderboard; import com.sap.sailing.domain.leaderboard.RegattaLeaderboardWithEliminations; +import com.sap.sailing.domain.leaderboard.RegattaLeaderboardWithOtherTieBreakingLeaderboard; import com.sap.sailing.domain.leaderboard.ScoringScheme; import com.sap.sailing.domain.leaderboard.impl.DelegatingRegattaLeaderboardWithCompetitorElimination; import com.sap.sailing.domain.persistence.DomainObjectFactory; @@ -240,9 +241,12 @@ public interface RacingEventService extends TrackedRegattaRegistry, RegattaFetch RegattaLeaderboardWithEliminations addRegattaLeaderboardWithEliminations(String leaderboardName, String leaderboardDisplayName, RegattaLeaderboard fullRegattaLeaderboard); + RegattaLeaderboardWithOtherTieBreakingLeaderboard addRegattaLeaderboardWithOtherTieBreakingLeaderboard(RegattaIdentifier regattaIdentifier, + String leaderboardDisplayName, int[] discardThresholds, RegattaLeaderboard otherTieBreakingLeaderboard); + /** * Removes the leaderboard specified by {@code leaderboardName} as well as all delegating leaderboards that reference it, - * in particular those {@link DelegatingRegattaLeaderboardWithCompetitorElimination} which which the leaderboard specified + * in particular those {@link DelegatingRegattaLeaderboardWithCompetitorElimination} for which the leaderboard specified * by {@code leaderboardName} was their underlying regatta leaderboard. If no leaderboard named as specified by the * {@code leaderboardName} parameter exists, this method has no effect. */ diff --git a/java/com.sap.sailing.server.interface/src/com/sap/sailing/server/interfaces/RacingEventServiceOperation.java b/java/com.sap.sailing.server.interface/src/com/sap/sailing/server/interfaces/RacingEventServiceOperation.java index e58e4b32854..9f840c0ca46 100755 --- a/java/com.sap.sailing.server.interface/src/com/sap/sailing/server/interfaces/RacingEventServiceOperation.java +++ b/java/com.sap.sailing.server.interface/src/com/sap/sailing/server/interfaces/RacingEventServiceOperation.java @@ -4,6 +4,7 @@ import com.sap.sailing.server.operationaltransformation.AddColumnToLeaderboard; import com.sap.sailing.server.operationaltransformation.CreateFlexibleLeaderboard; import com.sap.sailing.server.operationaltransformation.CreateRegattaLeaderboard; import com.sap.sailing.server.operationaltransformation.CreateRegattaLeaderboardWithEliminations; +import com.sap.sailing.server.operationaltransformation.CreateRegattaLeaderboardWithOtherTieBreakingLeaderboard; import com.sap.sailing.server.operationaltransformation.MoveLeaderboardColumnDown; import com.sap.sailing.server.operationaltransformation.MoveLeaderboardColumnUp; import com.sap.sailing.server.operationaltransformation.RemoveLeaderboard; @@ -40,6 +41,10 @@ OperationWithTransformationSupport transformAddRegattaLeaderboardServerOp(CreateRegattaLeaderboardWithEliminations addLeaderboard); + RacingEventServiceOperation transformAddRegattaLeaderboardClientOp(CreateRegattaLeaderboardWithOtherTieBreakingLeaderboard addLeaderboard); + + RacingEventServiceOperation transformAddRegattaLeaderboardServerOp(CreateRegattaLeaderboardWithOtherTieBreakingLeaderboard addLeaderboard); + RacingEventServiceOperation transformRemoveLeaderboardClientOp(RemoveLeaderboard removeLeaderboard); RacingEventServiceOperation transformRemoveLeaderboardServerOp(RemoveLeaderboard removeLeaderboard); diff --git a/java/com.sap.sailing.server.interface/src/com/sap/sailing/server/operationaltransformation/AbstractRacingEventServiceOperation.java b/java/com.sap.sailing.server.interface/src/com/sap/sailing/server/operationaltransformation/AbstractRacingEventServiceOperation.java index fcb87dd99d9..03a99475da2 100755 --- a/java/com.sap.sailing.server.interface/src/com/sap/sailing/server/operationaltransformation/AbstractRacingEventServiceOperation.java +++ b/java/com.sap.sailing.server.interface/src/com/sap/sailing/server/operationaltransformation/AbstractRacingEventServiceOperation.java @@ -54,6 +54,16 @@ public abstract class AbstractRacingEventServiceOperation implements return addLeaderboard; } + @Override + public RacingEventServiceOperation transformAddRegattaLeaderboardClientOp(CreateRegattaLeaderboardWithOtherTieBreakingLeaderboard addLeaderboard) { + return addLeaderboard; + } + + @Override + public RacingEventServiceOperation transformAddRegattaLeaderboardServerOp(CreateRegattaLeaderboardWithOtherTieBreakingLeaderboard addLeaderboard) { + return addLeaderboard; + } + @Override public RacingEventServiceOperation transformRenameLeaderboardColumnClientOp( RenameLeaderboardColumn renameLeaderboardColumnClientOp) { diff --git a/java/com.sap.sailing.server.interface/src/com/sap/sailing/server/operationaltransformation/AddSpecificRegatta.java b/java/com.sap.sailing.server.interface/src/com/sap/sailing/server/operationaltransformation/AddSpecificRegatta.java index 42535f436ea..49dbf4162ac 100755 --- a/java/com.sap.sailing.server.interface/src/com/sap/sailing/server/operationaltransformation/AddSpecificRegatta.java +++ b/java/com.sap.sailing.server.interface/src/com/sap/sailing/server/operationaltransformation/AddSpecificRegatta.java @@ -92,6 +92,7 @@ public class AddSpecificRegatta extends AbstractAddRegattaOperation { s.setSplitFleetContiguousScoring(e.getValue().hasSplitFleetContiguousScoring()); s.setFirstColumnIsNonDiscardableCarryForward(e.getValue().isFirstColumnIsNonDiscardableCarryForward()); s.setMaximumNumberOfDiscards(e.getValue().getMaximumNumberOfDiscards()); + s.setOneAlwaysStaysOne(e.getValue().isOneAlwaysStaysOne()); result.add(s); } return result; diff --git a/java/com.sap.sailing.server.interface/src/com/sap/sailing/server/operationaltransformation/CreateRegattaLeaderboardWithEliminations.java b/java/com.sap.sailing.server.interface/src/com/sap/sailing/server/operationaltransformation/CreateRegattaLeaderboardWithEliminations.java index ca07e8c67b8..66073b07672 100755 --- a/java/com.sap.sailing.server.interface/src/com/sap/sailing/server/operationaltransformation/CreateRegattaLeaderboardWithEliminations.java +++ b/java/com.sap.sailing.server.interface/src/com/sap/sailing/server/operationaltransformation/CreateRegattaLeaderboardWithEliminations.java @@ -25,7 +25,7 @@ public class CreateRegattaLeaderboardWithEliminations extends AbstractLeaderboar result = toState.addRegattaLeaderboardWithEliminations(getLeaderboardName(), leaderboardDisplayName, (RegattaLeaderboard) toState.getLeaderboardByName(fullRegattaLeaderboardName)); } else { - logger.warning("Cannot replicate creation of flexible leaderboard "+getLeaderboardName()+" because it already exists in the replica"); + logger.warning("Cannot replicate creation of regatta leaderboard "+getLeaderboardName()+" because it already exists in the replica"); } return result; } diff --git a/java/com.sap.sailing.server.interface/src/com/sap/sailing/server/operationaltransformation/CreateRegattaLeaderboardWithOtherTieBreakingLeaderboard.java b/java/com.sap.sailing.server.interface/src/com/sap/sailing/server/operationaltransformation/CreateRegattaLeaderboardWithOtherTieBreakingLeaderboard.java new file mode 100644 index 00000000000..1801e0ee2de --- /dev/null +++ b/java/com.sap.sailing.server.interface/src/com/sap/sailing/server/operationaltransformation/CreateRegattaLeaderboardWithOtherTieBreakingLeaderboard.java @@ -0,0 +1,50 @@ +package com.sap.sailing.server.operationaltransformation; + +import java.util.logging.Logger; + +import com.sap.sailing.domain.common.RegattaIdentifier; +import com.sap.sailing.domain.common.RegattaName; +import com.sap.sailing.domain.leaderboard.RegattaLeaderboard; +import com.sap.sailing.domain.leaderboard.RegattaLeaderboardWithOtherTieBreakingLeaderboard; +import com.sap.sailing.server.interfaces.RacingEventService; +import com.sap.sailing.server.interfaces.RacingEventServiceOperation; + +public class CreateRegattaLeaderboardWithOtherTieBreakingLeaderboard extends AbstractLeaderboardOperation { + private static final long serialVersionUID = -2851501773630513795L; + private static final Logger logger = Logger.getLogger(CreateRegattaLeaderboardWithOtherTieBreakingLeaderboard.class.getName()); + private final RegattaIdentifier regattaIdentifier; + private final String leaderboardDisplayName; + private final int[] discardThresholds; + private final String otherTieBreakingLeaderboardName; + + public CreateRegattaLeaderboardWithOtherTieBreakingLeaderboard(RegattaIdentifier regattaIdentifier, + String leaderboardDisplayName, int[] discardThresholds, String otherTieBreakingLeaderboardName) { + super(((RegattaName) regattaIdentifier).getRegattaName()); + this.regattaIdentifier = regattaIdentifier; + this.leaderboardDisplayName = leaderboardDisplayName; + this.discardThresholds = discardThresholds; + this.otherTieBreakingLeaderboardName = otherTieBreakingLeaderboardName; + } + + @Override + public RegattaLeaderboardWithOtherTieBreakingLeaderboard internalApplyTo(RacingEventService toState) { + RegattaLeaderboardWithOtherTieBreakingLeaderboard result = null; + if (toState.getLeaderboardByName(getLeaderboardName()) == null) { + result = toState.addRegattaLeaderboardWithOtherTieBreakingLeaderboard(regattaIdentifier, leaderboardDisplayName, + discardThresholds, (RegattaLeaderboard) toState.getLeaderboardByName(otherTieBreakingLeaderboardName)); + } else { + logger.warning("Cannot replicate creation of regatta leaderboard "+getLeaderboardName()+" because it already exists in the replica"); + } + return result; + } + + @Override + public RacingEventServiceOperation transformClientOp(RacingEventServiceOperation serverOp) { + return serverOp.transformAddRegattaLeaderboardClientOp(this); + } + + @Override + public RacingEventServiceOperation transformServerOp(RacingEventServiceOperation clientOp) { + return clientOp.transformAddRegattaLeaderboardServerOp(this); + } +} diff --git a/java/com.sap.sailing.server.interface/src/com/sap/sailing/server/operationaltransformation/UpdateSeries.java b/java/com.sap.sailing.server.interface/src/com/sap/sailing/server/operationaltransformation/UpdateSeries.java index bdd9765062c..d27b20e43af 100755 --- a/java/com.sap.sailing.server.interface/src/com/sap/sailing/server/operationaltransformation/UpdateSeries.java +++ b/java/com.sap.sailing.server.interface/src/com/sap/sailing/server/operationaltransformation/UpdateSeries.java @@ -30,11 +30,12 @@ public class UpdateSeries extends AbstractSeriesOperation { private final boolean seriesNameChanged; private final String newSeriesName; private final Integer maximumNumberOfDiscards; + private final boolean oneAlwaysStaysOne; public UpdateSeries(RegattaIdentifier regattaIdentifier, String seriesName, String newSeriesName, boolean isMedal, boolean isFleetsCanRunInParallel, int[] resultDiscardingThresholds, boolean startsWithZeroScore, boolean firstColumnIsNonDiscardableCarryForward, boolean hasSplitFleetContiguousScoring, boolean hasCrossFleetMergedRanking, - Integer maximumNumberOfDiscards, List fleets) { + Integer maximumNumberOfDiscards, boolean oneAlwaysStaysOne, List fleets) { super(regattaIdentifier, seriesName); this.seriesNameChanged = !seriesName.equals(newSeriesName); this.newSeriesName = newSeriesName; @@ -47,6 +48,7 @@ public class UpdateSeries extends AbstractSeriesOperation { this.hasCrossFleetMergedRanking = hasCrossFleetMergedRanking; this.maximumNumberOfDiscards = maximumNumberOfDiscards; this.fleets = fleets; + this.oneAlwaysStaysOne = oneAlwaysStaysOne; } @Override @@ -67,6 +69,7 @@ public class UpdateSeries extends AbstractSeriesOperation { series.setSplitFleetContiguousScoring(hasSplitFleetContiguousScoring); series.setCrossFleetMergedRanking(hasCrossFleetMergedRanking); series.setMaximumNumberOfDiscards(maximumNumberOfDiscards); + series.setOneAlwaysStaysOne(oneAlwaysStaysOne); if (series.getRegatta().isPersistent()) { toState.updateStoredRegatta(series.getRegatta()); } diff --git a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/AbstractLogReplicationTest.java b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/AbstractLogReplicationTest.java index 402dc08592b..24af7962aa8 100644 --- a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/AbstractLogReplicationTest.java +++ b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/AbstractLogReplicationTest.java @@ -34,11 +34,11 @@ import com.sap.sse.common.Util; public abstract class AbstractLogReplicationTest, EventT extends AbstractLogEvent, VisitorT> extends AbstractServerReplicationTest { protected static final String BOAT_CLASS_NAME_49er = "49er"; - + /** * Uses a new master that loads the existing regatta and race log to append an event to the race log. This will store it to the DB so that if the original * master re-loads the race log it should see the new race log event. - * @throws Exception + * @throws Exception */ protected void addEventToDB(RaceLogIdentifier raceLogIdentifier, RaceLogRaceStatusEvent createRaceStatusEvent, String regattaName, String raceColumnName, String fleetName) throws Exception { final RacingEventServiceImpl temporaryMaster = createNewMaster(); @@ -47,7 +47,7 @@ public abstract class AbstractLogReplicationTest seriesCreationParameters = new LinkedHashMap<>(); SeriesCreationParametersDTO creationParametersForDefaultSeries = new SeriesCreationParametersDTO( - Arrays.asList(new FleetDTO[] { new FleetDTO(fleetName, 0, Color.BLACK), }), /* medal */false, /* fleetsCanRunInParallel */ true, /* startsWithZero */ - false, /* firstColumnIsNonDiscardableCarryForward */ false, /* discardingThresholds */new int[0], /* hasSplitFleetContiguousScoring */ - false, /* hasCrossFleetMergedRanking */ false, /* maximumNumberOfDiscards */ null); + Arrays.asList(new FleetDTO[] { new FleetDTO(fleetName, 0, Color.BLACK), }), /* medal */ false, /* fleetsCanRunInParallel */ true, /* startsWithZero */ + false, /* firstColumnIsNonDiscardableCarryForward */ false, /* discardingThresholds */ new int[0], /* hasSplitFleetContiguousScoring */ + false, /* hasCrossFleetMergedRanking */ false, /* maximumNumberOfDiscards */ null, /* oneAlwaysStaysOne */ false); seriesCreationParameters.put(seriesName, creationParametersForDefaultSeries); // 1. Install some race column on master... RegattaCreationParametersDTO regattaCreationParams = new RegattaCreationParametersDTO(seriesCreationParameters); diff --git a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/RegattaReplicationTest.java b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/RegattaReplicationTest.java index 56d9fc8839f..7a5cc4aa550 100755 --- a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/RegattaReplicationTest.java +++ b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/RegattaReplicationTest.java @@ -278,7 +278,7 @@ public class RegattaReplicationTest extends AbstractServerReplicationTest { master.apply(new UpdateSeries(masterRegatta.getRegattaIdentifier(), finals.getName(), finals.getName(), finals.isMedal(), finals.isFleetsCanRunInParallel(), new int[] {}, finals.isStartsWithZeroScore(), finals.isFirstColumnIsNonDiscardableCarryForward(), - finals.hasSplitFleetContiguousScoring(), finals.hasCrossFleetMergedRanking(), finals.getMaximumNumberOfDiscards(), Arrays.asList(new FleetDTO[] { finalsGoldFleet }))); + finals.hasSplitFleetContiguousScoring(), finals.hasCrossFleetMergedRanking(), finals.getMaximumNumberOfDiscards(), finals.isOneAlwaysStaysOne(), Arrays.asList(new FleetDTO[] { finalsGoldFleet }))); Thread.sleep(1000); replicatedRegatta = replica.getRegatta(new RegattaName(masterRegatta.getName())); assertNotNull(replicatedRegatta); @@ -321,8 +321,8 @@ public class RegattaReplicationTest extends AbstractServerReplicationTest { qualification.isMedal(), qualification.isFleetsCanRunInParallel(), new int[] {}, qualification.isStartsWithZeroScore(), qualification.isFirstColumnIsNonDiscardableCarryForward(), - qualification.hasSplitFleetContiguousScoring(), qualification.hasCrossFleetMergedRanking(), - qualification.getMaximumNumberOfDiscards(), Arrays.asList(new FleetDTO[] { }))); + qualification.hasSplitFleetContiguousScoring(), qualification.hasCrossFleetMergedRanking(), qualification.getMaximumNumberOfDiscards(), + qualification.isOneAlwaysStaysOne(), Arrays.asList(new FleetDTO[] { }))); Thread.sleep(1000); replicatedRegatta = replica.getRegatta(new RegattaName(masterRegatta.getName())); assertNotNull(replicatedRegatta); diff --git a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/TrackRaceBoatCompetitorMetadataReplicationTest.java b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/TrackRaceBoatCompetitorMetadataReplicationTest.java index f463f34b8ea..a707124bf1c 100644 --- a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/TrackRaceBoatCompetitorMetadataReplicationTest.java +++ b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/TrackRaceBoatCompetitorMetadataReplicationTest.java @@ -116,7 +116,7 @@ public class TrackRaceBoatCompetitorMetadataReplicationTest extends AbstractServ master.apply(new UpdateSeries(regattaIdentifier, "Default", "Default", /* isMedal */ false, /* isFleetsCanRunInParallel */ false, /* resultDiscardingThresholds */ null, /* startsWithZeroScore */ false, /* firstColumnIsNonDiscardableCarryForward */ false, /* hasSplitFleetContiguousScoring */ false, /* hasCrossFleetMergedRanking */ false, /* maximumNumberOfDiscards */ null, - Arrays.asList(new FleetDTO("Red", 0, Color.RED), new FleetDTO("Green", 0, Color.GREEN), new FleetDTO("Blue", 0, Color.BLUE)))); + /* oneAlwaysStaysOne */ false, Arrays.asList(new FleetDTO("Red", 0, Color.RED), new FleetDTO("Green", 0, Color.GREEN), new FleetDTO("Blue", 0, Color.BLUE)))); racesHandle = master.addRace(/* regattaToAddTo */ regattaIdentifier, trackingParams, /* timeoutInMilliseconds */ 60000, new DefaultRaceTrackingHandler()); } diff --git a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/AutomaticRetrackUponCompetitorSetChangeTest.java b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/AutomaticRetrackUponCompetitorSetChangeTest.java index 54767bf4065..a3dc3a5dc7f 100755 --- a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/AutomaticRetrackUponCompetitorSetChangeTest.java +++ b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/AutomaticRetrackUponCompetitorSetChangeTest.java @@ -81,7 +81,7 @@ import com.sap.sse.security.SecurityService; /** * See also bug 5219 (https://bugzilla.sapsailing.com/bugzilla/show_bug.cgi?id=5219). - * + * * @author Axel Uhl (D043530) * */ @@ -139,7 +139,7 @@ public class AutomaticRetrackUponCompetitorSetChangeTest { final String seriesName = "Default"; service.apply(new UpdateSeries(regattaIdentifier, seriesName, seriesName, /* isMedal */ false, /* isFleetsCanRunInParallel */ false, /* resultDiscardingThresholds */ null, /* startsWithZeroScore */ false, /* firstColumnIsNonDiscardableCarryForward */ false, - /* hasSplitFleetContiguousScoring */ false, /* hasCrossFleetMergedRanking */ false, /* maximumNumberOfDiscards */ null, + /* hasSplitFleetContiguousScoring */ false, /* hasCrossFleetMergedRanking */ false, /* maximumNumberOfDiscards */ null, /* oneAlwaysStaysOne */ false, Arrays.asList(new FleetDTO(RED_FLEET_NAME, 0, Color.RED), new FleetDTO(GREEN_FLEET_NAME, 0, Color.GREEN), new FleetDTO(BLUE_FLEET_NAME, 0, Color.BLUE)))); service.apply(new CreateRegattaLeaderboard(regattaIdentifier, /* leaderboardDisplayName */ null, new int[0])); service.apply(new AddColumnToSeries(regattaIdentifier, seriesName, FIRST_RACE_COLUMN_NAME)); @@ -194,7 +194,7 @@ public class AutomaticRetrackUponCompetitorSetChangeTest { assertNotSame(race, newRace); racesHandle = newHandle; // ensure that tearDown tears down the correct tracker } - + @Test public void testStartRaceLogTrackingAndAddCompetitor() throws NotDenotedForRaceLogTrackingException, Exception { final RaceLog raceLog = service.getRaceLog(regattaIdentifier.getRegattaName(), FIRST_RACE_COLUMN_NAME, RED_FLEET_NAME); diff --git a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/SearchServiceTest.java b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/SearchServiceTest.java index cde971ed1fc..47f7bea9a13 100755 --- a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/SearchServiceTest.java +++ b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/SearchServiceTest.java @@ -167,7 +167,7 @@ public class SearchServiceTest { seriesCreationParams.put("Default", new SeriesCreationParametersDTO(Collections.singletonList(new FleetDTO("Default", /* order */-1, Color.RED)), /* medal */false, /* fleetsCanRunInParallel */ true, /* startsWithZero */false, /* firstColumnIsNonDiscardableCarryForward */false, - /* discardingThresholds */null, /* hasSplitFleetContiguousScoring */false, /* hasCrossFleetMergedRanking */ false, /* maximumNumberOfDiscards */ null)); + /* discardingThresholds */null, /* hasSplitFleetContiguousScoring */false, /* hasCrossFleetMergedRanking */ false, /* maximumNumberOfDiscards */ null, /* oneAlwaysStaysOne */ false)); pfingstbusch29er = server.apply(new AddSpecificRegatta(RegattaImpl.getDefaultName("Pfingstbusch", "29er"), "29er", /* canBoatsOfCompetitorsChangePerRace */ true, CompetitorRegistrationType.CLOSED, /* registrationLinkSecret */ UUID.randomUUID().toString(), /* startDate */ null, /* endDate */ null, diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java index da5eeab3d6a..5bff980528c 100644 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java @@ -175,13 +175,16 @@ import com.sap.sailing.domain.leaderboard.LeaderboardGroup; import com.sap.sailing.domain.leaderboard.LeaderboardRegistry; import com.sap.sailing.domain.leaderboard.RegattaLeaderboard; import com.sap.sailing.domain.leaderboard.RegattaLeaderboardWithEliminations; +import com.sap.sailing.domain.leaderboard.RegattaLeaderboardWithOtherTieBreakingLeaderboard; import com.sap.sailing.domain.leaderboard.ScoreCorrectionListener; import com.sap.sailing.domain.leaderboard.ScoringScheme; import com.sap.sailing.domain.leaderboard.SettableScoreCorrection; +import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule; import com.sap.sailing.domain.leaderboard.impl.DelegatingRegattaLeaderboardWithCompetitorElimination; import com.sap.sailing.domain.leaderboard.impl.FlexibleLeaderboardImpl; import com.sap.sailing.domain.leaderboard.impl.LeaderboardGroupImpl; import com.sap.sailing.domain.leaderboard.impl.RegattaLeaderboardImpl; +import com.sap.sailing.domain.leaderboard.impl.RegattaLeaderboardWithOtherTieBreakingLeaderboardImpl; import com.sap.sailing.domain.leaderboard.impl.ThresholdBasedResultDiscardingRuleImpl; import com.sap.sailing.domain.leaderboard.meta.LeaderboardGroupMetaLeaderboard; import com.sap.sailing.domain.persistence.DomainObjectFactory; @@ -1332,12 +1335,18 @@ implements RacingEventService, ClearStateTestSupport, RegattaListener, Leaderboa @Override public RegattaLeaderboard addRegattaLeaderboard(RegattaIdentifier regattaIdentifier, String leaderboardDisplayName, int[] discardThresholds) { + return addRegattaLeaderboard(regattaIdentifier, leaderboardDisplayName, discardThresholds, + (regatta, thresholdBasedResultDiscardingRule)->new RegattaLeaderboardImpl(regatta, new ThresholdBasedResultDiscardingRuleImpl(discardThresholds))); + } + + private R addRegattaLeaderboard(RegattaIdentifier regattaIdentifier, String leaderboardDisplayName, + int[] discardThresholds, BiFunction regattaConstructor) { Regatta regatta = getRegatta(regattaIdentifier); if (regatta == null) { throw new IllegalArgumentException("Cannot find regatta " + regattaIdentifier + ". Hence, cannot create regatta leaderboard for it."); } - final RegattaLeaderboard result = new RegattaLeaderboardImpl(regatta, new ThresholdBasedResultDiscardingRuleImpl(discardThresholds)); + final R result = regattaConstructor.apply(regatta, new ThresholdBasedResultDiscardingRuleImpl(discardThresholds)); result.setDisplayName(leaderboardDisplayName); if (getLeaderboardByName(result.getName()) != null) { throw new IllegalArgumentException("Leaderboard with name " + result.getName() + " already exists in " @@ -1369,6 +1378,14 @@ implements RacingEventService, ClearStateTestSupport, RegattaListener, Leaderboa return result; } + @Override + public RegattaLeaderboardWithOtherTieBreakingLeaderboard addRegattaLeaderboardWithOtherTieBreakingLeaderboard(RegattaIdentifier regattaIdentifier, + String leaderboardDisplayName, int[] discardThresholds, RegattaLeaderboard otherTieBreakingLeaderboard) { + return addRegattaLeaderboard(regattaIdentifier, leaderboardDisplayName, discardThresholds, + (regatta, thresholdBasedResultDiscardingRule)->new RegattaLeaderboardWithOtherTieBreakingLeaderboardImpl( + regatta, new ThresholdBasedResultDiscardingRuleImpl(discardThresholds), ()->otherTieBreakingLeaderboard)); + } + @Override public RaceColumn addColumnToLeaderboard(String columnName, String leaderboardName, boolean medalRace) { Leaderboard leaderboard = getLeaderboardByName(leaderboardName); @@ -1507,6 +1524,9 @@ implements RacingEventService, ClearStateTestSupport, RegattaListener, Leaderboa final Regatta candidatesRegatta = ((RegattaLeaderboard) candidateForRemoval).getRegatta(); if (candidatesRegatta == regatta) { leaderboardsToRemove.add(candidateForRemoval); + } else if (candidateForRemoval instanceof RegattaLeaderboardWithOtherTieBreakingLeaderboard && + ((RegattaLeaderboardWithOtherTieBreakingLeaderboard) candidateForRemoval).getOtherTieBreakingLeaderboard().getRegatta() == regatta) { + leaderboardsToRemove.add(candidateForRemoval); } } } @@ -2095,7 +2115,7 @@ implements RacingEventService, ClearStateTestSupport, RegattaListener, Leaderboa new SeriesCreationParametersDTO(fleetNamesAndOrdering, s.isMedal(), s.isFleetsCanRunInParallel(), s.isStartsWithZeroScore(), s .isFirstColumnIsNonDiscardableCarryForward(), s.getResultDiscardingRule() == null ? null : s.getResultDiscardingRule().getDiscardIndexResultsStartingWithHowManyRaces(), s - .hasSplitFleetContiguousScoring(), s.hasCrossFleetMergedRanking(), s.getMaximumNumberOfDiscards())); + .hasSplitFleetContiguousScoring(), s.hasCrossFleetMergedRanking(), s.getMaximumNumberOfDiscards(), s.isOneAlwaysStaysOne())); } return new RegattaCreationParametersDTO(result); } diff --git a/java/com.sap.sailing.www/release_notes_admin.html b/java/com.sap.sailing.www/release_notes_admin.html index a05ca0f5c57..2b3bce1c338 100755 --- a/java/com.sap.sailing.www/release_notes_admin.html +++ b/java/com.sap.sailing.www/release_notes_admin.html @@ -23,6 +23,23 @@

Release Notes - Administration Console

+

September 2022

+
    +
  • Series within a regatta now support a configuration option that, regardless a race column factor, + keeps the 1.0 score as 1.0, scaling the other scores accordingly. For example, the scores 1, 2, 3, ... + with a column factor of 2 would then be mapped to 1, 3, 5, ... Use this, e.g., to model iQFOil + slalom races with heat split where the general opening series does not use split fleets.
  • +
  • A new leaderboard type is now available which can refer to another leaderboard for tie-breaking. + An example could be the Formula Kite rules where the high-point medal stage leaderboard is required + to use the low-point opening series leaderboard for tie-breaking under certain circumstances. + New scoring schemes may follow that use this new construct.
  • +
  • A new scoring scheme "High Point System, Match Racing, Tie-Break Based on other Leaderboard" is now + available which uses the new type of leaderboard which can reference another leaderboard for tie-breaking. + Note that when choosing this scoring scheme for any leaderboard but one that references a tie-breaking + leaderboard, the tie-breaking leaderboard functionality will simply be ignored, making it equivalent + in behavior to the simpler "High Point System, Match Racing" scoring scheme.
  • +
  • Added boat classes "Beneteau First 35" and "Beneteau First 45" with their class logos.
  • +

July 2022

  • New load balancers that are created on demand now use the SSL policy "ELBSecurityPolicy-FS-1-2-Res-2019-08" diff --git a/java/com.sap.sailing.xrr.structureimport/src/com/sap/sailing/xrr/structureimport/SeriesParameters.java b/java/com.sap.sailing.xrr.structureimport/src/com/sap/sailing/xrr/structureimport/SeriesParameters.java index 7d2d29b222f..285725515e5 100755 --- a/java/com.sap.sailing.xrr.structureimport/src/com/sap/sailing/xrr/structureimport/SeriesParameters.java +++ b/java/com.sap.sailing.xrr.structureimport/src/com/sap/sailing/xrr/structureimport/SeriesParameters.java @@ -7,15 +7,17 @@ public class SeriesParameters { private boolean startswithZeroScore = false; private int[] discardingThresholds = null; private Integer maximumNumberOfDiscards = null; + private boolean oneAlwaysStaysOne; public SeriesParameters(boolean firstColumnIsNonDiscardableCarryForward, boolean hasSplitFleetContiguousScoring, - boolean hasCrossFleetMergedRanking, boolean startswithZeroScore, int[] discardingThresholds, Integer maximumNumberOfDiscards) { + boolean hasCrossFleetMergedRanking, boolean startswithZeroScore, int[] discardingThresholds, Integer maximumNumberOfDiscards, boolean oneAlwaysStaysOne) { this.firstColumnIsNonDiscardableCarryForward = firstColumnIsNonDiscardableCarryForward; this.hasSplitFleetContiguousScoring = hasSplitFleetContiguousScoring; this.hasCrossFleetMergedRanking = hasCrossFleetMergedRanking; this.startswithZeroScore = startswithZeroScore; this.discardingThresholds = discardingThresholds; this.maximumNumberOfDiscards = maximumNumberOfDiscards; + this.oneAlwaysStaysOne = oneAlwaysStaysOne; } public boolean isFirstColumnIsNonDiscardableCarryForward() { @@ -61,4 +63,8 @@ public class SeriesParameters { public Integer getMaximumNumberOfDiscards() { return maximumNumberOfDiscards; } + + public boolean isOneAlwaysStaysOne() { + return oneAlwaysStaysOne; + } }