diff --git a/java/com.sap.sailing.dashboards.gwt/src/main/java/com/sap/sailing/dashboards/gwt/client/widgets/startanalysis/card/StartAnalysisCard.java b/java/com.sap.sailing.dashboards.gwt/src/main/java/com/sap/sailing/dashboards/gwt/client/widgets/startanalysis/card/StartAnalysisCard.java index e8aa94cfe14..e77052ae87b 100644 --- a/java/com.sap.sailing.dashboards.gwt/src/main/java/com/sap/sailing/dashboards/gwt/client/widgets/startanalysis/card/StartAnalysisCard.java +++ b/java/com.sap.sailing.dashboards.gwt/src/main/java/com/sap/sailing/dashboards/gwt/client/widgets/startanalysis/card/StartAnalysisCard.java @@ -24,8 +24,6 @@ import com.sap.sailing.dashboards.gwt.client.widgets.startanalysis.rankingtable. import com.sap.sailing.dashboards.gwt.shared.StartlineAdvantageType; import com.sap.sailing.dashboards.gwt.shared.dto.StartAnalysisDTO; import com.sap.sailing.domain.common.racelog.RacingProcedureType; -import com.sap.sailing.gwt.ui.client.CompetitorColorProvider; -import com.sap.sailing.gwt.ui.client.CompetitorColorProviderImpl; import com.sap.sailing.gwt.ui.client.RaceCompetitorSelectionModel; import com.sap.sailing.gwt.ui.client.RaceTimesInfoProvider; import com.sap.sailing.gwt.ui.client.SailingServiceAsync; @@ -75,7 +73,6 @@ public class StartAnalysisCard extends Composite implements HasWidgets, StartAna private SailingServiceAsync sailingServiceAsync; private StringMessages stringMessages; private final RaceCompetitorSelectionModel competitorSelectionModel; - private final CompetitorColorProvider colorProvider; private final double WIND_LINE_ADVANTAGE_DIV_WIDTH_IN_PT = 185; private final double GEOMETRIC_LINE_ADVANTAGE_DIV_WIDTH_IN_PT = 210; @@ -87,8 +84,7 @@ public class StartAnalysisCard extends Composite implements HasWidgets, StartAna stringMessages = StringMessages.INSTANCE; this.sailingServiceAsync = sailingServiceAsync; this.raceMapResources = raceMapResources; - colorProvider = new CompetitorColorProviderImpl(); - competitorSelectionModel = new RaceCompetitorSelectionModel(/* hasMultiSelection */true, colorProvider); + competitorSelectionModel = new RaceCompetitorSelectionModel(/* hasMultiSelection */true); competitorSelectionModel.setCompetitors(startAnalysisDTO.getCompetitorDTOsFromStartAnaylsisCompetitorDTOs(), raceMap); initWidget(uiBinder.createAndBindUi(this)); diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/CompetitorDTOImpl.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/CompetitorDTOImpl.java index 54592fb6494..aa249733c4d 100755 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/CompetitorDTOImpl.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/CompetitorDTOImpl.java @@ -63,7 +63,7 @@ public class CompetitorDTOImpl extends CompetitorWithoutBoatDTOImpl implements C @Override public String getShortInfo() { String result = null; - if (getShortName() != null) { + if (getShortName() != null && !getShortName().trim().isEmpty()) { result = getShortName(); } else if (getBoat() != null) { result = getBoat().getSailId() != null ? getBoat().getSailId() : getBoat().getName(); diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/CreateAndTrackWithRaceLogTest.java b/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/CreateAndTrackWithRaceLogTest.java index f3b6948b679..b47ed62c6d1 100644 --- a/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/CreateAndTrackWithRaceLogTest.java +++ b/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/CreateAndTrackWithRaceLogTest.java @@ -11,8 +11,9 @@ import java.io.IOException; import java.net.MalformedURLException; import java.net.URISyntaxException; import java.util.Collections; -import java.util.Optional; import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; import org.junit.After; import org.junit.Before; @@ -62,11 +63,12 @@ import com.sap.sailing.domain.racelogtracking.test.RaceLogTrackingTestHelper; import com.sap.sailing.domain.ranking.OneDesignRankingMetric; import com.sap.sailing.domain.tracking.DynamicTrackedRace; import com.sap.sailing.domain.tracking.RaceHandle; -import com.sap.sailing.domain.tracking.RaceListener; import com.sap.sailing.domain.tracking.Track; import com.sap.sailing.domain.tracking.TrackedRace; +import com.sap.sailing.domain.tracking.impl.AbstractRaceChangeListener; import com.sap.sailing.server.RacingEventService; import com.sap.sailing.server.impl.RacingEventServiceImpl; +import com.sap.sailing.server.util.WaitForTrackedRaceUtil; import com.sap.sse.common.NoCorrespondingServiceRegisteredException; import com.sap.sse.common.TimePoint; import com.sap.sse.common.impl.MillisecondsTimePoint; @@ -130,7 +132,7 @@ public class CreateAndTrackWithRaceLogTest extends RaceLogTrackingTestHelper { Exception { RaceColumn column = leaderboard.getRaceColumnByName(columnName); exception.expect(NotDenotedForRaceLogTrackingException.class); - trackRace(column); + trackAndGetRace(column); } private void testSize(Track track, int expected) { @@ -202,15 +204,12 @@ public class CreateAndTrackWithRaceLogTest extends RaceLogTrackingTestHelper { raceLog.add(new RaceLogRegisterCompetitorEventImpl(t(), author, 0, comp1, boat1)); raceLog.add(new RaceLogStartOfTrackingEventImpl(t(0), author, /* passId */ 0)); // start tracking - trackRace(column); - - - // now there is a tracked race - TrackedRace race = column.getTrackedRace(fleet); + TrackedRace race = trackAndGetRace(column); assertNotNull(race); RaceLogFixTrackerManager raceLogFixTrackerManager = new RaceLogFixTrackerManager((DynamicTrackedRace) race, sensorFixStore, null); + raceLogFixTrackerManager.waitForTracker(); race.waitForLoadingToFinish(); addFixes1(race, comp1, dev1); regattaLog.add(new RegattaLogDeviceCompetitorMappingEventImpl(t(), t(), author, 0, comp1, dev1, t(11), t(20))); @@ -225,21 +224,10 @@ public class CreateAndTrackWithRaceLogTest extends RaceLogTrackingTestHelper { addFixes3(race, comp1, dev1); } - private void trackRace(RaceColumn column) throws NotDenotedForRaceLogTrackingException, Exception { + private TrackedRace trackAndGetRace(RaceColumn column) throws NotDenotedForRaceLogTrackingException, Exception { final RaceHandle raceHandle = adapter.startTracking(service, leaderboard, column, fleet, /* trackWind */ false, /* correctWindDirectionByMagneticDeclination */ false); - - // The following lines ensure that we received all necessary events from the TrackedRegatta - // When removing a listener we get a Future whose get method will block until all previously triggered events are processed. - final RaceListener raceListener = new RaceListener() { - @Override - public void raceRemoved(TrackedRace trackedRace) { - } - @Override - public void raceAdded(TrackedRace trackedRace) { - } - }; - raceHandle.getTrackedRegatta().addRaceListener(raceListener, Optional.empty()); - raceHandle.getTrackedRegatta().removeRaceListener(raceListener).get(); + raceHandle.getRace(); + return WaitForTrackedRaceUtil.waitForTrackedRace(column, fleet, 10); } /** @@ -255,10 +243,7 @@ public class CreateAndTrackWithRaceLogTest extends RaceLogTrackingTestHelper { raceLog = column.getRaceLog(fleet); adapter.denoteRaceForRaceLogTracking(service, leaderboard, column, fleet, "race"); // start tracking - adapter.startTracking(service, leaderboard, column, fleet, /* trackWind */ false, /* correctWindDirectionByMagneticDeclination */ false); - - // now there is a trackedrace - TrackedRace race = column.getTrackedRace(fleet); + TrackedRace race = trackAndGetRace(column); assertNotNull(race); final TimePoint startTimeInRaceLog = new MillisecondsTimePoint(123456); final TimePoint endTimeInRaceLog = new MillisecondsTimePoint(234567); @@ -276,9 +261,8 @@ public class CreateAndTrackWithRaceLogTest extends RaceLogTrackingTestHelper { // now remove the tracked race and track again; again, the explicit tracking times in the race log must prevail service.removeRace(regatta, race.getRace()); - trackRace(column); - TrackedRace race2 = column.getTrackedRace(fleet); - assertEquals(explicitStartOfTracking, race2.getStartOfTracking()); + TrackedRace race2 = trackAndGetRace(column); + assertEquals(explicitStartOfTracking, waitForStartOfTracking(race2)); assertEquals(explicitEndOfTracking, race2.getEndOfTracking()); } @@ -300,14 +284,12 @@ public class CreateAndTrackWithRaceLogTest extends RaceLogTrackingTestHelper { regattaLog.add(new RegattaLogRegisterCompetitorEventImpl(t(), t(), author, UUID.randomUUID(), comp1)); raceLog.add(new RaceLogStartOfTrackingEventImpl(t(0), author, /* passId */ 0)); - trackRace(column); - - // now there is a trackedrace - TrackedRace race = column.getTrackedRace(fleet); + TrackedRace race = trackAndGetRace(column); assertNotNull(race); RaceLogFixTrackerManager raceLogFixTrackerManager = new RaceLogFixTrackerManager((DynamicTrackedRace) race, sensorFixStore, null); + raceLogFixTrackerManager.waitForTracker(); race.waitForLoadingToFinish(); addFixes1(race, comp1, dev1); @@ -322,4 +304,35 @@ public class CreateAndTrackWithRaceLogTest extends RaceLogTrackingTestHelper { raceLogFixTrackerManager.stop(false, /* willBeRemoved */ false); addFixes3(race, comp1, dev1); } + + + /** + * Waits for startOfTracking being available for a given {@link TrackedRace}. + */ + public static TimePoint waitForStartOfTracking(TrackedRace trackedRace) { + final CompletableFuture future = new CompletableFuture<>(); + + final AbstractRaceChangeListener listener = new AbstractRaceChangeListener() { + @Override + public void startOfTrackingChanged(TimePoint oldStartOfTracking, TimePoint newStartOfTracking) { + if (newStartOfTracking != null) { + future.complete(newStartOfTracking); + trackedRace.removeListener(this); + } + } + }; + trackedRace.addListener(listener); + final TimePoint startOfTracking = trackedRace.getStartOfTracking(); + if (startOfTracking != null) { + trackedRace.removeListener(listener); + return startOfTracking; + } + + try { + return future.get(10, TimeUnit.SECONDS); + } catch (Exception e) { + trackedRace.removeListener(listener); + return null; + } + } } diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/fixtracker/RaceLogFixTrackerManager.java b/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/fixtracker/RaceLogFixTrackerManager.java index d8c29226b0e..ea9aa77f0ef 100644 --- a/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/fixtracker/RaceLogFixTrackerManager.java +++ b/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/fixtracker/RaceLogFixTrackerManager.java @@ -90,6 +90,7 @@ public class RaceLogFixTrackerManager implements TrackingDataLoader { } else { stopTrackerIfStillRunning(/* preemptive */ false, /* willBeRemoved */ false); } + this.notifyAll(); } private void addRaceLog(RaceLog raceLog) { @@ -153,4 +154,11 @@ public class RaceLogFixTrackerManager implements TrackingDataLoader { } return false; } + + public synchronized FixLoaderAndTracker waitForTracker() throws InterruptedException { + while (tracker == null) { + this.wait(); + } + return tracker; + } } diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/TransientCompetitorStoreImpl.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/TransientCompetitorStoreImpl.java index 12060ff855e..05147c64c8f 100755 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/TransientCompetitorStoreImpl.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/TransientCompetitorStoreImpl.java @@ -725,7 +725,6 @@ public class TransientCompetitorStoreImpl implements CompetitorStore, Serializab boatDTO = convertToBoatDTO(boat); } CompetitorDTO competitorDTO = new CompetitorDTOImpl(c, boatDTO); - return competitorDTO; } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/places/screens/afterliveraceloop/boats/RaceEndWithBoatsPresenterImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/places/screens/afterliveraceloop/boats/RaceEndWithBoatsPresenterImpl.java index 8336e6ff22c..cfbb6dc9386 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/places/screens/afterliveraceloop/boats/RaceEndWithBoatsPresenterImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/places/screens/afterliveraceloop/boats/RaceEndWithBoatsPresenterImpl.java @@ -61,7 +61,7 @@ public class RaceEndWithBoatsPresenterImpl extends AutoPlayPresenterConfigured racesToShow = null; - if (lifeRace != null) { + if (liveRace != null) { racesToShow = new ArrayList<>(); - racesToShow.add(lifeRace.getRaceName()); + racesToShow.add(liveRace.getRaceName()); } else { view.showErrorNoLive(this, panel, new IllegalStateException("No race is live")); return; @@ -152,21 +152,20 @@ public class LiveRaceWithRacemapAndLeaderBoardPresenterImpl final SingleRaceLeaderboardSettings leaderboardSettings = new SingleRaceLeaderboardSettings( /* maneuverDetailsToShow */ null, /* legDetailsToShow */ null, /* raceDetailsToShow */ null, /* overallDetailsToShow */ null, /* delayBetweenAutoAdvancesInMilliseconds */ null, - /* showAddedScores */ false, /* showCompetitorShortNameColumn */ false, + /* showAddedScores */ false, /* showCompetitorShortNameColumn */ true, /* showCompetitorFullNameColumn */ false, /* isCompetitorNationalityColumnVisible */ false, /* showCompetitorBoatInfoColumn */ false, /* showRaceRankColumn */ true); timer = new com.sap.sse.gwt.client.player.Timer( // perform the first request as "live" but don't by default auto-play PlayModes.Live, PlayStates.Playing, /* delayBetweenAutoAdvancesInMilliseconds */ LeaderboardEntryPoint.DEFAULT_REFRESH_INTERVAL_MILLIS); - leaderboardPanel = new SingleRaceLeaderboardPanel(null,null,sailingService, new AsyncActionsExecutor(), leaderboardSettings, - true, lifeRace, getPlace().getRaceMapSelectionProvider(), timer, null, - getSlideCtx().getContextDefinition().getLeaderboardName(), errorReporter, StringMessages.INSTANCE, + leaderboardPanel = new SingleRaceLeaderboardPanel(null, null, sailingService, new AsyncActionsExecutor(), + leaderboardSettings, true, liveRace, getPlace().getRaceMapSelectionProvider(), timer, null, + getSlideCtx().getContextDefinition().getLeaderboardName(), errorReporter, StringMessages.INSTANCE, false, null, false, null, false, true, false, false, false, new SixtyInchLeaderBoardStyle(true), FlagImageResolverImpl.get()); - - getPlace().getRaceMap().setQuickRanksDTOProvider(new QuickRanksDTOFromLeaderboardDTOProvider(new RaceCompetitorSet(getPlace().getRaceMapSelectionProvider()), lifeRace)); - + getPlace().getRaceMap().setQuickRanksDTOProvider(new QuickRanksDTOFromLeaderboardDTOProvider( + new RaceCompetitorSet(getPlace().getRaceMapSelectionProvider()), liveRace)); view.startingWith(this, panel, getPlace().getRaceMap(), leaderboardPanel); selectionTimer.schedule(SWITCH_COMPETITOR_DELAY); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/places/screens/preliveraceloop/leaderboard/PreLiveRaceLeaderBoardWithImagePresenterImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/places/screens/preliveraceloop/leaderboard/PreLiveRaceLeaderBoardWithImagePresenterImpl.java index a8ed96bd722..99945cf1e1a 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/places/screens/preliveraceloop/leaderboard/PreLiveRaceLeaderBoardWithImagePresenterImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/places/screens/preliveraceloop/leaderboard/PreLiveRaceLeaderBoardWithImagePresenterImpl.java @@ -14,8 +14,8 @@ import com.sap.sailing.gwt.autoplay.client.app.AnimationPanel; import com.sap.sailing.gwt.autoplay.client.app.AutoPlayClientFactory; import com.sap.sailing.gwt.autoplay.client.app.AutoPlayPresenterConfigured; import com.sap.sailing.gwt.settings.client.leaderboard.SingleRaceLeaderboardSettings; -import com.sap.sailing.gwt.ui.client.RaceCompetitorSelectionModel; import com.sap.sailing.gwt.ui.client.FlagImageResolverImpl; +import com.sap.sailing.gwt.ui.client.RaceCompetitorSelectionModel; import com.sap.sailing.gwt.ui.client.SailingServiceAsync; import com.sap.sailing.gwt.ui.client.StringMessages; import com.sap.sailing.gwt.ui.leaderboard.LeaderboardEntryPoint; @@ -97,7 +97,7 @@ public class PreLiveRaceLeaderBoardWithImagePresenterImpl final SingleRaceLeaderboardSettings leaderboardSettings = new SingleRaceLeaderboardSettings( /* maneuverDetailsToShow */ null, /* legDetailsToShow */ null, /* raceDetailsToShow */ null, /* overallDetailsToShow */ null, /* delayBetweenAutoAdvancesInMilliseconds */ null, - /* showAddedScores */ false, /* showCompetitorShortNameColumn */ false, + /* showAddedScores */ false, /* showCompetitorShortNameColumn */ true, /* showCompetitorFullNameColumn */ false, /* isCompetitorNationalityColumnVisible */ false, /* showCompetitorBoatInfoColumn */ false, /* showRaceRankColumn */ false); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/utils/AutoplayHelper.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/utils/AutoplayHelper.java old mode 100644 new mode 100755 index 041025e2c0c..a7efe457525 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/utils/AutoplayHelper.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/utils/AutoplayHelper.java @@ -3,6 +3,7 @@ package com.sap.sailing.gwt.autoplay.client.utils; import java.util.ArrayList; import java.util.Date; import java.util.Map; +import java.util.Map.Entry; import java.util.UUID; import com.google.gwt.core.client.GWT; @@ -197,16 +198,14 @@ public class AutoplayHelper { public void raceTimesInfosReceived(Map raceTimesInfo, long clientTimeWhenRequestWasSent, Date serverTimeDuringRequest, long clientTimeWhenResponseWasReceived) { - // keep the creationTime updated upon liveChanges! creationTimer.adjustClientServerOffset(clientTimeWhenRequestWasSent, serverTimeDuringRequest, clientTimeWhenResponseWasReceived); creationTimer .setLivePlayDelayInMillis(raceTimesInfo.get(regattaAndRaceIdentifier).delayToLiveInMs); creationTimer.play(); - if (regattaAndRaceIdentifier != null) { - if(!mapAlreadyCreated){ + if (!mapAlreadyCreated) { mapAlreadyCreated = true; sailingService.getCompetitorBoats(regattaAndRaceIdentifier, new AsyncCallback>() { @@ -280,16 +279,16 @@ public class AutoplayHelper { typesToConsiderOnZoom.add(ZoomTypes.BUOYS); typesToConsiderOnZoom.add(ZoomTypes.BOATS); RaceMapZoomSettings autoFollowRace = new RaceMapZoomSettings(typesToConsiderOnZoom, true); - RaceMapSettings settings = new RaceMapSettings(autoFollowRace, new RaceMapHelpLinesSettings(), false, 15, 100000l, false, RaceMapSettings.DEFAULT_BUOY_ZONE_RADIUS, false, true, false, false, false, false, RaceMapSettings.getDefaultManeuvers(), false, false); - RaceMapLifecycle raceMapLifecycle = new RaceMapLifecycle(StringMessages.INSTANCE); - final CompetitorColorProvider colorProvider = new CompetitorColorProviderImpl(currentLiveRace, result); RaceCompetitorSelectionModel competitorSelectionProvider = new RaceCompetitorSelectionModel( - /* hasMultiSelection */ true, colorProvider); + /* hasMultiSelection */ true, colorProvider, result); + for (Entry entry : result.entrySet()) { + competitorSelectionProvider.setBoat(entry.getKey(), entry.getValue()); + } competitorSelectionProvider.setCompetitors(competitors); RaceMap raceboardPerspective = new RaceMap(null, null, raceMapLifecycle, settings, sailingService, asyncActionsExecutor, errorReporter, raceboardTimer, competitorSelectionProvider, diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompetitorEditDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompetitorEditDialog.java index 28e66fcaab3..3ca32ce1378 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompetitorEditDialog.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompetitorEditDialog.java @@ -185,7 +185,9 @@ public class CompetitorEditDialog extends DataEntryDialog { color = new InvalidColor(iae); } } - CompetitorDTO result = new CompetitorDTOImpl(name.getText(), shortName.getText(), color, email.getText(), + CompetitorDTO result = new CompetitorDTOImpl(name.getText(), + shortName.getText().trim().isEmpty() ? null : shortName.getText(), color, + email.getText().trim().isEmpty() ? null : email.getText(), /* twoLetterIsoCountryCode */ null, threeLetterIocCountryCode.getValue(threeLetterIocCountryCode.getSelectedIndex()), /* countryName */ null, competitorToEdit.getIdAsString(), diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/CompetitorSelectionModel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/CompetitorSelectionModel.java index b9400ddab0a..3820f46b9ef 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/CompetitorSelectionModel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/CompetitorSelectionModel.java @@ -46,7 +46,7 @@ public class CompetitorSelectionModel implements CompetitorSelectionProvider { this.hasMultiSelection = hasMultiSelection; this.competitorColorProvider = competitorColorProvider; this.competitorsFilterSet = competitorsFilterSet; - this.allCompetitors = new LinkedHashSet(); + this.allCompetitors = new LinkedHashSet<>(); this.selectedCompetitors = new LinkedHashMap<>(); this.listeners = new HashSet(); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/CompetitorWithBoatSelectionModel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/CompetitorWithBoatSelectionModel.java deleted file mode 100644 index 844a3b2f6f4..00000000000 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/CompetitorWithBoatSelectionModel.java +++ /dev/null @@ -1,313 +0,0 @@ -package com.sap.sailing.gwt.ui.client; - -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.Map; -import java.util.Set; - -import com.sap.sailing.domain.common.dto.CompetitorDTO; -import com.sap.sse.common.Color; -import com.sap.sse.common.Util; -import com.sap.sse.common.filter.Filter; -import com.sap.sse.common.filter.FilterSet; - -public class CompetitorWithBoatSelectionModel implements CompetitorSelectionProvider { - private final Set allCompetitors; - - /** - * Keys are {@link CompetitorDTO#getIdAsString()} of their values - */ - private final LinkedHashMap selectedCompetitors; - - private final Set listeners; - - private final boolean hasMultiSelection; - - private FilterSet> competitorsFilterSet; - - private final CompetitorColorProvider competitorColorProvider; - - public CompetitorWithBoatSelectionModel(boolean hasMultiSelection) { - this(hasMultiSelection, new CompetitorColorProviderImpl(), null); - } - - public CompetitorWithBoatSelectionModel(boolean hasMultiSelection, CompetitorColorProvider competitorColorProvider) { - this(hasMultiSelection, competitorColorProvider, null); - } - - private CompetitorWithBoatSelectionModel(boolean hasMultiSelection, CompetitorColorProvider competitorColorProvider, FilterSet> competitorsFilterSet) { - super(); - this.hasMultiSelection = hasMultiSelection; - this.competitorColorProvider = competitorColorProvider; - this.competitorsFilterSet = competitorsFilterSet; - this.allCompetitors = new LinkedHashSet(); - this.selectedCompetitors = new LinkedHashMap<>(); - this.listeners = new HashSet(); - } - - /** - * Adds a competitor to the {@link #getAllCompetitors() set of all competitors}. If the competitor was not yet - * contained, it will be deselected. - */ - public void add(CompetitorDTO competitor) { - add(competitor, true); - } - - private void add(CompetitorDTO competitor, boolean notifyListeners) { - if (competitor.getColor() != null) { - competitorColorProvider.addBlockedColor(competitor.getColor()); - } - boolean changed = allCompetitors.add(competitor); - if (notifyListeners && changed) { - fireListChanged(getAllCompetitors()); - } - } - - @Override - public boolean hasMultiSelection() { - return hasMultiSelection; - } - - /** - * Removes all competitors. Afterwards, this model is empty. Deselection events are issued for all competitors that - * were selected so far. - */ - public void clear() { - Iterator selIter = getSelectedCompetitors().iterator(); - while (selIter.hasNext()) { - CompetitorDTO selected = selIter.next(); - setSelected(selected, false); - selIter = getSelectedCompetitors().iterator(); - } - assert selectedCompetitors.isEmpty(); - allCompetitors.clear(); - fireListChanged(getAllCompetitors()); - } - - public void addAll(Iterable competitors) { - boolean changed = false; - for (CompetitorDTO competitor : competitors) { - add(competitor, false); - changed = true; - } - if (changed) { - fireListChanged(getAllCompetitors()); - } - } - - /** - * Removes the competitor from the {@link #getAllCompetitors() set of all competitor}. If the competitor was previously - * contained and selected, it is deselected first. - */ - public void remove(CompetitorDTO competitor) { - if (isSelected(competitor)) { - setSelected(competitor, false); - } - if (competitor.getColor() != null) { - competitorColorProvider.removeBlockedColor(competitor.getColor()); - } - boolean changed = allCompetitors.remove(competitor); - if (changed) { - fireListChanged(getAllCompetitors()); - } - } - - @Override - public Iterable getSelectedCompetitors() { - return Collections.unmodifiableCollection(selectedCompetitors.values()); - } - - @Override - public Iterable getSelectedFilteredCompetitors() { - Set result = new HashSet<>(selectedCompetitors.values()); - result.retainAll(getFilteredCompetitors()); - return result; - } - - @Override - public Iterable getAllCompetitors() { - return Collections.unmodifiableCollection(allCompetitors); - } - - @Override - public Collection getFilteredCompetitors() { - Set currentFilteredList = new LinkedHashSet(allCompetitors); - if (competitorsFilterSet != null) { - for (Filter filter : competitorsFilterSet.getFilters()) { - for (Iterator i=currentFilteredList.iterator(); i.hasNext(); ) { - CompetitorDTO competitorDTO = i.next(); - if (!filter.matches(competitorDTO)) { - i.remove(); - } - } - } - } - return currentFilteredList; - } - - public void setSelected(CompetitorDTO competitor, boolean selected, CompetitorSelectionChangeListener... listenersNotToNotify) { - if (selected) { - if (allCompetitors.contains(competitor) && !selectedCompetitors.containsKey(competitor.getIdAsString())) { - selectedCompetitors.put(competitor.getIdAsString(), competitor); - fireAddedToSelection(competitor, listenersNotToNotify); - } - } else { - if (selectedCompetitors.containsKey(competitor.getIdAsString())) { - selectedCompetitors.remove(competitor.getIdAsString()); - fireRemovedFromSelection(competitor, listenersNotToNotify); - } - } - } - - private void fireAddedToSelection(CompetitorDTO competitor, CompetitorSelectionChangeListener... listenersNotToNotify) { - for (CompetitorSelectionChangeListener listener : listeners) { - if (listenersNotToNotify == null || !Arrays.asList(listenersNotToNotify).contains(listener)) { - listener.addedToSelection(competitor); - } - } - } - - private void fireRemovedFromSelection(CompetitorDTO competitor, CompetitorSelectionChangeListener... listenersNotToNotify) { - for (CompetitorSelectionChangeListener listener : listeners) { - if (listenersNotToNotify == null || !Arrays.asList(listenersNotToNotify).contains(listener)) { - listener.removedFromSelection(competitor); - } - } - } - - private void fireListChanged(Iterable competitors, CompetitorSelectionChangeListener... listenersNotToNotify) { - for (CompetitorSelectionChangeListener listener : listeners) { - if (listenersNotToNotify == null || !Arrays.asList(listenersNotToNotify).contains(listener)) { - listener.competitorsListChanged(competitors); - } - } - } - - @Override - public boolean isSelected(CompetitorDTO competitor) { - return selectedCompetitors.containsKey(competitor.getIdAsString()); - } - - @Override - public void addCompetitorSelectionChangeListener(CompetitorSelectionChangeListener listener) { - listeners.add(listener); - } - - @Override - public void removeCompetitorSelectionChangeListener(CompetitorSelectionChangeListener listener) { - listeners.remove(listener); - } - - @Override - public void setSelection(Iterable newSelection, CompetitorSelectionChangeListener... listenersNotToNotify) { - Set competitorsToRemoveFromSelection = new HashSet(selectedCompetitors.values()); - for (CompetitorDTO newSelectedCompetitor : newSelection) { - setSelected(newSelectedCompetitor, true, listenersNotToNotify); - competitorsToRemoveFromSelection.remove(newSelectedCompetitor); - } - for (CompetitorDTO competitorToRemoveFromSelection : competitorsToRemoveFromSelection) { - setSelected(competitorToRemoveFromSelection, false, listenersNotToNotify); - } - } - - @Override - public void setCompetitors(Iterable newCompetitors, CompetitorSelectionChangeListener... listenersNotToNotify) { - boolean changed = false; - Map oldCompetitorsToRemove = new HashMap(); - for (final CompetitorDTO c : allCompetitors) { - oldCompetitorsToRemove.put(c.getIdAsString(), c); - } - for (CompetitorDTO newCompetitor : newCompetitors) { - // Search by equality, including all CompetitorDTO fields; if equal object is found, leave it in place; - // non-equal objects may still represent the same competitor entity; yet, replace the object to obtain the - // new, changed state; make sure the selection keeps unchanged regarding the selected competitor "entities"; - // see also bug 3413 for more background. - if (allCompetitors.contains(newCompetitor)) { - oldCompetitorsToRemove.remove(newCompetitor.getIdAsString()); // the old competitor object remains in allCompetitors; it is equal but doesn't have to be identical to newCompetitor - } else { - boolean selected = isSelected(newCompetitor); - if (selected) { - remove(oldCompetitorsToRemove.get(newCompetitor.getIdAsString())); - oldCompetitorsToRemove.remove(newCompetitor.getIdAsString()); - } - add(newCompetitor, false); // due to the equals definition of CompetitorDTOImpl that compares most fields, state changes lead to a replacement here - if (selected) { - // restore selection for replaced element - setSelected(newCompetitor, selected, listenersNotToNotify); - } - changed = true; - } - } - for (CompetitorDTO oldCompetitorToRemove : oldCompetitorsToRemove.values()) { - remove(oldCompetitorToRemove); - changed = true; - } - - if (changed) { - fireListChanged(getAllCompetitors(), listenersNotToNotify); - } - } - - @Override - public Color getColor(CompetitorDTO competitor) { - return allCompetitors.contains(competitor) ? competitorColorProvider.getColor(competitor) : null; - } - - @Override - public FilterSet> getCompetitorsFilterSet() { - return competitorsFilterSet; - } - - @Override - public FilterSet> getOrCreateCompetitorsFilterSet(String nameToAssignToNewFilterSet) { - if (competitorsFilterSet == null) { - competitorsFilterSet = new FilterSet>(nameToAssignToNewFilterSet); - } - return getCompetitorsFilterSet(); - } - - @Override - public void setCompetitorsFilterSet(FilterSet> competitorsFilterSet) { - FilterSet> oldFilterSet = this.competitorsFilterSet; - this.competitorsFilterSet = competitorsFilterSet; - if (!Util.equalsWithNull(competitorsFilterSet, oldFilterSet)) { - for (CompetitorSelectionChangeListener listener : listeners) { - listener.filterChanged(oldFilterSet, competitorsFilterSet); - } - } - for (CompetitorSelectionChangeListener listener : listeners) { - listener.filteredCompetitorsListChanged(getFilteredCompetitors()); - } - } - - @Override - public boolean hasActiveFilters() { - return (competitorsFilterSet != null && !competitorsFilterSet.getFilters().isEmpty() - && getFilteredCompetitors().size() != allCompetitors.size()); - } - - @Override - public void clearAllFilters() { - if (hasActiveFilters()) { - Iterator selIter = getSelectedCompetitors().iterator(); - while (selIter.hasNext()) { - CompetitorDTO selected = selIter.next(); - setSelected(selected, false); - selIter = getSelectedCompetitors().iterator(); - } - competitorsFilterSet = null; - fireListChanged(getAllCompetitors()); - } - } - - @Override - public int getFilteredCompetitorsListSize() { - return getFilteredCompetitors().size(); - } -} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/RaceCompetitorSelectionModel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/RaceCompetitorSelectionModel.java index 4a17af31d65..dd58672ea10 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/RaceCompetitorSelectionModel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/RaceCompetitorSelectionModel.java @@ -9,14 +9,17 @@ import com.sap.sailing.domain.common.dto.CompetitorDTO; import com.sap.sse.common.Color; public class RaceCompetitorSelectionModel extends CompetitorSelectionModel implements RaceCompetitorSelectionProvider { - private final Map boatsOfCompetitors = new HashMap<>(); + private final Map boatsOfCompetitors; public RaceCompetitorSelectionModel(boolean hasMultiSelection) { super(hasMultiSelection); + boatsOfCompetitors = new HashMap<>(); } - public RaceCompetitorSelectionModel(boolean hasMultiSelection, CompetitorColorProvider competitorColorProvider) { + public RaceCompetitorSelectionModel(boolean hasMultiSelection, CompetitorColorProvider competitorColorProvider, Map boatsOfCompetitors) { super(hasMultiSelection, competitorColorProvider); + this.boatsOfCompetitors = boatsOfCompetitors; + addAll(boatsOfCompetitors.keySet()); } @Override 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 036ce003fde..77047229f3f 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 @@ -1869,7 +1869,7 @@ selectCompetitorsToEliminate=Select competitors to eliminate trackedRaceAlreadyLinked=The race is already linked to a race column. Do you really want to change the link? boatAlreadyLinked=The boat is already linked to a competitor chooseAAutoplayConfiguration=Choose an autoplay configuration -raceRankShort=Race rank: +raceRankShort=Race rank showRaceRankColumn=Show race rank estimatedDistance=Estimated Distance estimatedTime=Estimated Time diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/EmbeddedMapAndWindChartEntryPoint.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/EmbeddedMapAndWindChartEntryPoint.java index 9452d65a809..33bc3502c0e 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/EmbeddedMapAndWindChartEntryPoint.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/EmbeddedMapAndWindChartEntryPoint.java @@ -202,9 +202,9 @@ public class EmbeddedMapAndWindChartEntryPoint extends AbstractSailingEntryPoint final CompetitorColorProvider colorProvider = new CompetitorColorProviderImpl(selectedRaceIdentifier, competitorsAndBoats); final RaceCompetitorSelectionProvider competitorSelection; if (showCompetitors) { - competitorSelection = new RaceCompetitorSelectionModel(/* hasMultiSelection */ true, colorProvider); + competitorSelection = new RaceCompetitorSelectionModel(/* hasMultiSelection */ true, colorProvider, competitorsAndBoats); } else { - competitorSelection = createEmptyFilterCompetitorModel(colorProvider); // show no competitors + competitorSelection = createEmptyFilterCompetitorModel(colorProvider, competitorsAndBoats); // show no competitors } final RaceMap raceMap = new RaceMap(null, null, new RaceMapLifecycle(getStringMessages()), raceMapSettings, sailingService, asyncActionsExecutor, /* errorReporter */ EmbeddedMapAndWindChartEntryPoint.this, timer, @@ -232,8 +232,8 @@ public class EmbeddedMapAndWindChartEntryPoint extends AbstractSailingEntryPoint timer.setTime(timer.getTime().getTime()-1000l); } - private RaceCompetitorSelectionProvider createEmptyFilterCompetitorModel(CompetitorColorProvider colorProvider) { - final RaceCompetitorSelectionModel result = new RaceCompetitorSelectionModel(/* hasMultiSelection */ true, colorProvider); + private RaceCompetitorSelectionProvider createEmptyFilterCompetitorModel(CompetitorColorProvider colorProvider, Map competitorsAndBoats) { + final RaceCompetitorSelectionModel result = new RaceCompetitorSelectionModel(/* hasMultiSelection */ true, colorProvider, competitorsAndBoats); final FilterSet> filterSet = result.getOrCreateCompetitorsFilterSet("Empty"); filterSet.addFilter(new Filter() { @Override public boolean matches(CompetitorDTO object) { return false; } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardPanel.java index f0ad825a4b3..ca586d252ff 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardPanel.java @@ -211,7 +211,7 @@ public class RaceBoardPanel timeRangeWithZoomModel = new TimeRangeWithZoomModel(); final CompetitorColorProvider colorProvider = new CompetitorColorProviderImpl(selectedRaceIdentifier, competitorsAndTheirBoats); - competitorSelectionProvider = new RaceCompetitorSelectionModel(/* hasMultiSelection */ true, colorProvider); + competitorSelectionProvider = new RaceCompetitorSelectionModel(/* hasMultiSelection */ true, colorProvider, competitorsAndTheirBoats); raceMapResources.raceMapStyle().ensureInjected(); RaceMapLifecycle raceMapLifecycle = lifecycle.getRaceMapLifecycle(); diff --git a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/statistics/StatisticsTest.java b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/statistics/StatisticsTest.java index 7dcc02f55b1..2babe24c709 100755 --- a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/statistics/StatisticsTest.java +++ b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/statistics/StatisticsTest.java @@ -42,8 +42,6 @@ import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore; import com.sap.sailing.domain.tracking.DynamicTrackedRace; import com.sap.sailing.domain.tracking.DynamicTrackedRegatta; import com.sap.sailing.domain.tracking.MarkPassing; -import com.sap.sailing.domain.tracking.RaceListener; -import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.domain.tracking.impl.DynamicTrackedRaceImpl; import com.sap.sailing.domain.tracking.impl.DynamicTrackedRegattaImpl; import com.sap.sailing.domain.tracking.impl.EmptyWindStore; @@ -94,20 +92,6 @@ public class StatisticsTest { private TrackedRaceStatisticsCacheImpl getStatisticsCacheWithRegattaAdded() throws Exception { TrackedRaceStatisticsCacheImpl trackedRaceStatisticsCache = new TrackedRaceStatisticsCacheImpl(); trackedRaceStatisticsCache.regattaAdded(regatta); - - // The following lines ensure that we received all necessary events from the TrackedRegatta - // When removing a listener we get a Future whose get method will block until all previously triggered events are processed. - RaceListener raceListener = new RaceListener() { - @Override - public void raceRemoved(TrackedRace trackedRace) { - } - @Override - public void raceAdded(TrackedRace trackedRace) { - } - }; - regatta.addRaceListener(raceListener, Optional.empty()); - regatta.removeRaceListener(raceListener).get(); - return trackedRaceStatisticsCache; } diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/statistics/TrackedRaceStatisticsCacheImpl.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/statistics/TrackedRaceStatisticsCacheImpl.java index bcca439e7d8..9b23b81db2f 100644 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/statistics/TrackedRaceStatisticsCacheImpl.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/statistics/TrackedRaceStatisticsCacheImpl.java @@ -76,7 +76,12 @@ public class TrackedRaceStatisticsCacheImpl extends AbstractTrackedRegattaAndRac /** * For testing purposes only! */ - public TrackedRaceStatistics getStatisticsWaitingForLatest(TrackedRace trackedRace) { + public TrackedRaceStatistics getStatisticsWaitingForLatest(TrackedRace trackedRace) throws InterruptedException { + synchronized (listeners) { + while (!listeners.containsKey(trackedRace)) { + listeners.wait(); + } + } return cache.get(trackedRace, true); } @@ -84,9 +89,12 @@ public class TrackedRaceStatisticsCacheImpl extends AbstractTrackedRegattaAndRac protected void onRaceAdded(RegattaAndRaceIdentifier raceIdentifier, DynamicTrackedRegatta trackedRegatta, DynamicTrackedRace trackedRace) { Listener listener = new Listener(trackedRace); - listeners.put(trackedRace, listener); - trackedRace.addListener(listener); - triggerUpdateDirect(trackedRace); + synchronized (listeners) { + listeners.put(trackedRace, listener); + trackedRace.addListener(listener); + triggerUpdateDirect(trackedRace); + listeners.notifyAll(); + } } private void triggerUpdateScheduled(final DynamicTrackedRace trackedRace) { @@ -116,9 +124,12 @@ public class TrackedRaceStatisticsCacheImpl extends AbstractTrackedRegattaAndRac @Override protected void onRaceRemoved(DynamicTrackedRace trackedRace) { - Listener listener = listeners.remove(trackedRace); - if (listener != null) { - trackedRace.removeListener(listener); + synchronized (listeners) { + Listener listener = listeners.remove(trackedRace); + if (listener != null) { + trackedRace.removeListener(listener); + } + listeners.notifyAll(); } // To prevent leakage of TrackedRace instances as keys in the cache,