diff --git a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/DomainFactoryImpl.java b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/DomainFactoryImpl.java index b29efb1d772..28671d108b7 100644 --- a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/DomainFactoryImpl.java +++ b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/DomainFactoryImpl.java @@ -310,9 +310,14 @@ public class DomainFactoryImpl implements DomainFactory { @Override public void removeRace(com.tractrac.clientmodule.Event tractracEvent, Race tractracRace, TrackedEventRegistry trackedEventRegistry) { - RaceDefinition raceDefinition = getExistingRaceDefinitionForRace(tractracRace); - if (raceDefinition != null) { // otherwise, this domain factory doesn't seem to know about the race - raceCache.remove(tractracRace); + RaceDefinition raceDefinition; + synchronized (raceCache) { + raceDefinition = getExistingRaceDefinitionForRace(tractracRace); + if (raceDefinition != null) { // otherwise, this domain factory doesn't seem to know about the race + raceCache.remove(tractracRace); + } + } + if (raceDefinition != null) { Collection competitorClassList = new ArrayList(); for (com.tractrac.clientmodule.Competitor c : tractracEvent.getCompetitorList()) { competitorClassList.add(c.getCompetitorClass()); @@ -320,17 +325,26 @@ public class DomainFactoryImpl implements DomainFactory { BoatClass boatClass = getDominantBoatClass(competitorClassList); Pair key = new Pair(tractracEvent.getName(), boatClass == null ? null : boatClass.getName()); - Event event = eventCache.get(key); - if (event != null) { - event.removeRace(raceDefinition); - if (Util.size(event.getAllRaces()) == 0) { - eventCache.remove(key); - } - TrackedEvent trackedEvent = trackedEventRegistry.getTrackedEvent(event); - if (trackedEvent != null) { - trackedEvent.removeTrackedRace(raceDefinition); - if (Util.size(trackedEvent.getTrackedRaces()) == 0) { - trackedEventRegistry.removeTrackedEvent(event); + synchronized (eventCache) { + Event event = eventCache.get(key); + if (event != null) { + // The following fixes bug 202: when tracking of multiple races of the same event has been started, this may not + // remove any race; however, the event may already have been created by another tracker whose race hasn't + // arrived yet and therefore the races list is still empty; therefore, only remove the event if its + // race list became empty by the removal performed here. + int oldSize = Util.size(event.getAllRaces()); + event.removeRace(raceDefinition); + if (oldSize > 0 && Util.size(event.getAllRaces()) == 0) { + eventCache.remove(key); + } + TrackedEvent trackedEvent = trackedEventRegistry.getTrackedEvent(event); + if (trackedEvent != null) { + // see above; only remove tracked event if it *became* empty because of the tracked race removal here + int oldSizeOfTrackedRaces = Util.size(trackedEvent.getTrackedRaces()); + trackedEvent.removeTrackedRace(raceDefinition); + if (oldSizeOfTrackedRaces > 0 && Util.size(trackedEvent.getTrackedRaces()) == 0) { + trackedEventRegistry.removeTrackedEvent(event); + } } } } diff --git a/java/com.sap.sailing.gwt.ui/build.properties b/java/com.sap.sailing.gwt.ui/build.properties index c4e5216e84d..0f62bf0c2f2 100755 --- a/java/com.sap.sailing.gwt.ui/build.properties +++ b/java/com.sap.sailing.gwt.ui/build.properties @@ -7,7 +7,7 @@ bin.includes = META-INF/,\ WEB-INF/lib/gwt-servlet.jar,\ WEB-INF/classes/,\ WEB-INF/web.xml,\ - AdminConsole.css,\ + AdminConsole.css,\ AdminConsole.html,\ Leaderboard.css,\ Leaderboard.html,\ @@ -19,5 +19,9 @@ bin.includes = META-INF/,\ com.sap.sailing.gwt.ui.UserManagement/,\ crossdomain.xml,\ images/,\ - reset.css -output.. = WEB-INF/classes/ \ No newline at end of file + reset.css,\ + RaceBoard.css,\ + RaceBoard.html,\ + Spectator.html,\ + com.sap.sailing.gwt.ui.Spectator/ +output.. = WEB-INF/classes/ 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 e39fad57edb..26b2e4558a3 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 @@ -61,7 +61,7 @@ public class LeaderboardConfigPanel extends FormPanel implements EventDisplayer, private final TrackedEventsComposite trackedEventsComposite; - private final StringMessages stringConstants; + private final StringMessages stringMessages; private final SailingServiceAsync sailingService; @@ -90,7 +90,7 @@ public class LeaderboardConfigPanel extends FormPanel implements EventDisplayer, private TextBox filterLeaderboardTextbox; - final SingleSelectionModel raceTableSelectionModel; + final SingleSelectionModel raceColumnTableSelectionModel; private List availableLeaderboardList; @@ -115,7 +115,7 @@ public class LeaderboardConfigPanel extends FormPanel implements EventDisplayer, public LeaderboardConfigPanel(SailingServiceAsync sailingService, AdminConsole adminConsole, final ErrorReporter errorReporter, StringMessages theStringConstants) { - this.stringConstants = theStringConstants; + this.stringMessages = theStringConstants; this.sailingService = sailingService; leaderboardList = new ListDataProvider(); raceColumnList = new ListDataProvider(); @@ -125,11 +125,11 @@ public class LeaderboardConfigPanel extends FormPanel implements EventDisplayer, VerticalPanel mainPanel = new VerticalPanel(); this.setWidget(mainPanel); - Label lblLeaderboards = new Label(stringConstants.leaderboards()); + Label lblLeaderboards = new Label(stringMessages.leaderboards()); lblLeaderboards.setStyleName("bold"); mainPanel.add(lblLeaderboards); - Label lblFilterEvents = new Label(stringConstants.filterLeaderboardsByName() + ": "); + Label lblFilterEvents = new Label(stringMessages.filterLeaderboardsByName() + ": "); HorizontalPanel filterPanel = new HorizontalPanel(); filterPanel.setSpacing(5); filterPanel.add(lblFilterEvents); @@ -190,7 +190,7 @@ public class LeaderboardConfigPanel extends FormPanel implements EventDisplayer, } }; ImagesBarColumn leaderboardActionColumn = new ImagesBarColumn( - new LeaderboardConfigImagesBarCell(stringConstants)); + new LeaderboardConfigImagesBarCell(stringMessages)); leaderboardActionColumn.setFieldUpdater(new FieldUpdater() { @Override public void update(int index, LeaderboardDTO object, String value) { @@ -204,7 +204,7 @@ public class LeaderboardConfigPanel extends FormPanel implements EventDisplayer, otherExistingLeaderboard.addAll(leaderboardList.getList()); otherExistingLeaderboard.remove(object); LeaderboardEditDialog dialog = new LeaderboardEditDialog(Collections - .unmodifiableCollection(otherExistingLeaderboard), object, stringConstants, errorReporter, + .unmodifiableCollection(otherExistingLeaderboard), object, stringMessages, errorReporter, new AsyncCallback() { @Override public void onFailure(Throwable arg0) { @@ -254,7 +254,7 @@ public class LeaderboardConfigPanel extends FormPanel implements EventDisplayer, HorizontalPanel splitPanel = new HorizontalPanel(); mainPanel.add(splitPanel); - selectedLeaderBoardPanel = new CaptionPanel(stringConstants.leaderboard()); + selectedLeaderBoardPanel = new CaptionPanel(stringMessages.leaderboard()); selectedLeaderBoardPanel.setWidth("50%"); splitPanel.add(selectedLeaderBoardPanel); @@ -262,7 +262,7 @@ public class LeaderboardConfigPanel extends FormPanel implements EventDisplayer, vPanel.setWidth("100%"); selectedLeaderBoardPanel.setContentWidget(vPanel); - trackedRacesCaptionPanel = new CaptionPanel(stringConstants.trackedRaces()); + trackedRacesCaptionPanel = new CaptionPanel(stringMessages.trackedRaces()); trackedRacesCaptionPanel.setWidth("50%"); splitPanel.add(trackedRacesCaptionPanel); @@ -273,7 +273,7 @@ public class LeaderboardConfigPanel extends FormPanel implements EventDisplayer, raceSelectionProvider = new RaceSelectionModel(); trackedEventsComposite = new TrackedEventsComposite(sailingService, errorReporter, adminConsole, - raceSelectionProvider, stringConstants, /* multiselection */false); + raceSelectionProvider, stringMessages, /* multiselection */false); trackedRacesPanel.add(trackedEventsComposite); trackedEventsComposite.addTrackedRaceChangeListener(this); raceSelectionProvider.addRaceSelectionChangeListener(this); @@ -282,7 +282,7 @@ public class LeaderboardConfigPanel extends FormPanel implements EventDisplayer, hPanel.setSpacing(5); vPanel.add(hPanel); - Button stressTestButton = new Button(stringConstants.stressTest()); + Button stressTestButton = new Button(stringMessages.stressTest()); hPanel.add(stressTestButton); stressTestButton.addClickHandler(new ClickHandler() { @Override @@ -327,18 +327,18 @@ public class LeaderboardConfigPanel extends FormPanel implements EventDisplayer, @Override public String getValue(RaceInLeaderboardDTO race) { boolean isTrackedRace = race.isTrackedRace(); - return isTrackedRace ? stringConstants.yes() : stringConstants.no(); + return isTrackedRace ? stringMessages.yes() : stringMessages.no(); } }; ImagesBarColumn raceActionColumn = new ImagesBarColumn( - new LeaderboardRaceConfigImagesBarCell(stringConstants)); + new LeaderboardRaceConfigImagesBarCell(stringMessages)); raceActionColumn.setFieldUpdater(new FieldUpdater() { @Override public void update(int index, RaceInLeaderboardDTO object, String value) { if ("ACTION_REMOVE".equals(value)) { - if (Window.confirm("Do you really want to remove the race: '" + object + "' ?")) { + if (Window.confirm(stringMessages.reallyRemoveRace(object.toString()))) { removeRaceColumn(object); } } else if ("ACTION_EDIT".equals(value)) { @@ -349,21 +349,21 @@ public class LeaderboardConfigPanel extends FormPanel implements EventDisplayer, } }); - Label lblRaceNamesIn = new Label(stringConstants.races()); + Label lblRaceNamesIn = new Label(stringMessages.races()); vPanel.add(lblRaceNamesIn); raceColumnTable = new CellTable(/* pageSize */200, tableRes); - raceColumnTable.addColumn(raceLinkColumn, stringConstants.name()); - raceColumnTable.addColumn(isMedalRaceCheckboxColumn, stringConstants.medalRace()); - raceColumnTable.addColumn(isLinkedRaceColumn, stringConstants.islinked()); - raceColumnTable.addColumn(raceActionColumn, stringConstants.actions()); + raceColumnTable.addColumn(raceLinkColumn, stringMessages.name()); + raceColumnTable.addColumn(isMedalRaceCheckboxColumn, stringMessages.medalRace()); + raceColumnTable.addColumn(isLinkedRaceColumn, stringMessages.islinked()); + raceColumnTable.addColumn(raceActionColumn, stringMessages.actions()); raceColumnList.addDataDisplay(raceColumnTable); raceColumnTable.setWidth("500px"); - raceTableSelectionModel = new SingleSelectionModel(); + raceColumnTableSelectionModel = new SingleSelectionModel(); - raceColumnTable.setSelectionModel(raceTableSelectionModel); + raceColumnTable.setSelectionModel(raceColumnTableSelectionModel); - raceTableSelectionModel.addSelectionChangeHandler(new SelectionChangeEvent.Handler() { + raceColumnTableSelectionModel.addSelectionChangeHandler(new SelectionChangeEvent.Handler() { public void onSelectionChange(SelectionChangeEvent event) { leaderboardRaceColumnSelectionChanged(); } @@ -384,7 +384,7 @@ public class LeaderboardConfigPanel extends FormPanel implements EventDisplayer, } }); - columnMoveUpButton = new Button(stringConstants.columnMoveUp()); + columnMoveUpButton = new Button(stringMessages.columnMoveUp()); selectedLeaderboardRaceButtonPanel.add(columnMoveUpButton); columnMoveUpButton.addClickHandler(new ClickHandler() { @Override @@ -392,7 +392,7 @@ public class LeaderboardConfigPanel extends FormPanel implements EventDisplayer, moveSelectedRaceColumnUp(); } }); - columnMoveDownButton = new Button(stringConstants.columnMoveDown()); + columnMoveDownButton = new Button(stringMessages.columnMoveDown()); selectedLeaderboardRaceButtonPanel.add(columnMoveDownButton); columnMoveDownButton.addClickHandler(new ClickHandler() { @Override @@ -473,6 +473,9 @@ public class LeaderboardConfigPanel extends FormPanel implements EventDisplayer, @Override public void onSuccess(Void arg0) { raceColumnList.getList().remove(raceColumn); + if (raceColumnTableSelectionModel.isSelected(raceColumn)) { + raceColumnTableSelectionModel.setSelected(raceColumn, false); + } // selectedLeaderboard.raceNamesAndMedalRaceAndTracked.remove(raceColumn); selectedLeaderboard.removeRace(raceColumnString); selectedLeaderboard.invalidateCompetitorOrdering(); @@ -481,7 +484,7 @@ public class LeaderboardConfigPanel extends FormPanel implements EventDisplayer, } private void moveSelectedRaceColumnDown() { - final String selectedRaceColumnName = raceTableSelectionModel.getSelectedObject().getRaceColumnName(); + final String selectedRaceColumnName = raceColumnTableSelectionModel.getSelectedObject().getRaceColumnName(); sailingService.moveLeaderboardColumnDown(getSelectedLeaderboardName(), selectedRaceColumnName, new AsyncCallback() { @@ -504,7 +507,7 @@ public class LeaderboardConfigPanel extends FormPanel implements EventDisplayer, } private int getRowIndexOfSelectedRaceColumn() { - final RaceInLeaderboardDTO selectedRaceColumnName = raceTableSelectionModel.getSelectedObject(); + final RaceInLeaderboardDTO selectedRaceColumnName = raceColumnTableSelectionModel.getSelectedObject(); int rowIndex = -1; if (selectedRaceColumnName != null) { @@ -520,7 +523,7 @@ public class LeaderboardConfigPanel extends FormPanel implements EventDisplayer, } private void moveSelectedRaceColumnUp() { - final String selectedRaceColumnName = raceTableSelectionModel.getSelectedObject().getRaceColumnName(); + final String selectedRaceColumnName = raceColumnTableSelectionModel.getSelectedObject().getRaceColumnName(); sailingService.moveLeaderboardColumnUp(getSelectedLeaderboardName(), selectedRaceColumnName, new AsyncCallback() { @@ -585,7 +588,7 @@ public class LeaderboardConfigPanel extends FormPanel implements EventDisplayer, } private RaceInLeaderboardDTO getSelectedRaceInLeaderboard() { - RaceInLeaderboardDTO raceInLeaderboard = raceTableSelectionModel.getSelectedObject(); + RaceInLeaderboardDTO raceInLeaderboard = raceColumnTableSelectionModel.getSelectedObject(); return raceInLeaderboard; } @@ -595,7 +598,7 @@ public class LeaderboardConfigPanel extends FormPanel implements EventDisplayer, existingRacesWithoutThisRace.addAll(raceColumnList.getList()); existingRacesWithoutThisRace.remove(raceInLeaderboard); final RaceInLeaderboardDialog raceDialog = new RaceInLeaderboardDialog(existingRacesWithoutThisRace, - raceInLeaderboard, stringConstants, new AsyncCallback() { + raceInLeaderboard, stringMessages, new AsyncCallback() { @Override public void onFailure(Throwable caught) { } @@ -668,7 +671,7 @@ public class LeaderboardConfigPanel extends FormPanel implements EventDisplayer, final RaceInLeaderboardDTO raceInLeaderboard = new RaceInLeaderboardDTO(); final String leaderboardName = getSelectedLeaderboardName(); final RaceInLeaderboardDialog raceDialog = new RaceInLeaderboardDialog(raceColumnList.getList(), - raceInLeaderboard, stringConstants, new AsyncCallback() { + raceInLeaderboard, stringMessages, new AsyncCallback() { @Override public void onFailure(Throwable caught) { @@ -771,7 +774,7 @@ public class LeaderboardConfigPanel extends FormPanel implements EventDisplayer, for (LeaderboardDTO dao : leaderboardList.getList()) leaderboardNames.add(dao.name); LeaderboardCreateDialog dialog = new LeaderboardCreateDialog(Collections.unmodifiableCollection(leaderboardList - .getList()), stringConstants, errorReporter, new AsyncCallback() { + .getList()), stringMessages, errorReporter, new AsyncCallback() { @Override public void onFailure(Throwable arg0) { } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/TrackedEventsComposite.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/TrackedEventsComposite.java index f53f43e3cd7..77c25d5b34d 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/TrackedEventsComposite.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/TrackedEventsComposite.java @@ -131,7 +131,7 @@ public class TrackedEventsComposite extends FormPanel implements EventDisplayer, TextColumn eventNameColumn = new TextColumn() { @Override public String getValue(RaceDTO raceDTO) { - return raceDTO.name; + return raceDTO.getRegatta().getEvent().name; } }; eventNameColumn.setSortable(true); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/RaceSelectionModel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/RaceSelectionModel.java index 1c62989c303..78064a4478d 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/RaceSelectionModel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/RaceSelectionModel.java @@ -40,7 +40,7 @@ public class RaceSelectionModel implements RaceSelectionProvider { @Override public void setSelection(List newSelection, RaceSelectionChangeListener... listenersNotToNotify) { - boolean notify = selection.equals(newSelection); + boolean notify = !selection.equals(newSelection); selection.clear(); selection.addAll(newSelection); if (notify) { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingService.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingService.java index 8d63cbf3864..1c6bfa647bb 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingService.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingService.java @@ -23,6 +23,7 @@ import com.sap.sailing.gwt.ui.shared.LeaderboardDTO; import com.sap.sailing.gwt.ui.shared.ManeuverDTO; import com.sap.sailing.gwt.ui.shared.MarkDTO; import com.sap.sailing.gwt.ui.shared.QuickRankDTO; +import com.sap.sailing.gwt.ui.shared.RaceDTO; import com.sap.sailing.gwt.ui.shared.SwissTimingConfigurationDTO; import com.sap.sailing.gwt.ui.shared.SwissTimingRaceRecordDTO; import com.sap.sailing.gwt.ui.shared.TracTracConfigurationDTO; @@ -145,4 +146,6 @@ public interface SailingService extends RemoteService { Map> getManeuvers(RaceIdentifier raceIdentifier, Map from, Map to) throws NoWindException; + + List getLeaderboardsByRace(RaceDTO race); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingServiceAsync.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingServiceAsync.java index 012c913e264..854d96a2b53 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingServiceAsync.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingServiceAsync.java @@ -22,6 +22,7 @@ import com.sap.sailing.gwt.ui.shared.LeaderboardEntryDTO; import com.sap.sailing.gwt.ui.shared.ManeuverDTO; import com.sap.sailing.gwt.ui.shared.MarkDTO; import com.sap.sailing.gwt.ui.shared.QuickRankDTO; +import com.sap.sailing.gwt.ui.shared.RaceDTO; import com.sap.sailing.gwt.ui.shared.RaceInLeaderboardDTO; import com.sap.sailing.gwt.ui.shared.SwissTimingConfigurationDTO; import com.sap.sailing.gwt.ui.shared.SwissTimingRaceRecordDTO; @@ -145,6 +146,8 @@ public interface SailingServiceAsync { */ void getLeaderboardsByEvent(EventDTO event, AsyncCallback> callback); + void getLeaderboardsByRace(RaceDTO race, AsyncCallback> callback); + void updateLeaderboard(String leaderboardName, String newLeaderboardName, int[] newDiscardingThreasholds, 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 29ddcf11a56..280c7041765 100644 --- 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 @@ -212,4 +212,5 @@ public interface StringMessages extends Messages { String failedToLoadRaceInformation(String message); String competitorPassedMarkAtDate(String competitorName, String markName, String dateTime); String valueForCompetitorAt(String competitorName, String dateTime, String valueWithUnit); + String reallyRemoveRace(String string); } \ 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.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties index ff9c9700bd0..50195c2e52a 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 @@ -64,7 +64,7 @@ averageSpeedInKnots=\u2205 Speed averageSpeedInKnotsUnit=[kts] rankGain=Rank Gain sapSailingAnalytics=Sailing Analytics -leaderboard=LEADERBOARD +leaderboard=Leaderboard leaderboards=Leaderboards leaderboardSettings=Leaderboard Settings settings=Settings (e.g., leg detail selection) @@ -206,4 +206,5 @@ unknownManeuver=Unknown maneuver tailLengthMustBeNonNegative=Tail length must be a number greater than or equal to zero failedToLoadRaceInformation=Failed to load race information: {0} competitorPassedMarkAtDate={0} passed {1} at {2} -valueForCompetitorAt={0} at {1}: {2} \ No newline at end of file +valueForCompetitorAt={0} at {1}: {2} +reallyRemoveRace=Do you really want to remove the race ''{0}''? 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 f5041037875..4cd50187e82 100644 --- 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 @@ -207,4 +207,5 @@ unknownManeuver=Unbekanntes Manöver tailLengthMustBeNonNegative=Schweiflänge muss eine positive Zahl oder 0 sein failedToLoadRaceInformation=Daten zur Wettfahrt konnten nicht geladen werden: {0} competitorPassedMarkAtDate={0} passierte {1} um {2} -valueForCompetitorAt={0} um {1}: {2} \ No newline at end of file +valueForCompetitorAt={0} um {1}: {2} +reallyRemoveRace=Möchten Sie das Rennen ''{0}'' wirklich entfernen? \ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/AbstractChartPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/AbstractChartPanel.java index 8dfb6eabb60..c5c91fefdd6 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/AbstractChartPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/AbstractChartPanel.java @@ -479,54 +479,33 @@ implements CompetitorSelectionChangeListener, RaceSelectionChangeListener { }, true); plot.addSelectionListener(new SelectionListener() { public void selected(double x1, double y1, double x2, double y2) { - //Refactoring the selection range, if there is no mark passing between x1 and x2 - //Needed to prevent white space in the displayed selection - ArrayList x1Values = new ArrayList(); - ArrayList x2Values = new ArrayList(); - boolean markPassingInRange = false; + //Refactoring the selection range to prevent white space in the displayed selection + ArrayList> x1Values = new ArrayList>(); + ArrayList> x2Values = new ArrayList>(); + ArrayList> markPassingInRange = new ArrayList>(); - competitorLoop: - for (CompetitorDTO competitor : competitorID) { - ArrayList> negativeDeltas = new ArrayList>(); - ArrayList> positiveDeltas = new ArrayList>(); - Pair[] markPassingTimes = competitorsAndTimePointsDTO.getMarkPassings(competitor); - Double[] markPassingValues = chartData.getMarkPassings(competitor); - - for (int i = 0; i < markPassingValues.length; i++) { - double markPassing = markPassingTimes[i].getB().doubleValue(); - if (markPassingValues[i] != null && (markPassing < x1 || markPassing > x2)) { - double delta = markPassing < x1 ? markPassing - x1 : markPassing - x2; - Pair p = new Pair(delta, markPassing); - if (delta < 0) { - negativeDeltas.add(p); - } else { - positiveDeltas.add(p); - } - } else if (markPassingValues[i] != null) { - markPassingInRange = true; - break competitorLoop; - } + Comparator> comp = new Comparator>() { + @Override + public int compare(Pair p1, Pair p2) { + return p1.getA().compareTo(p2.getA()); } - - Comparator> comp = new Comparator>() { - @Override - public int compare(Pair p1, Pair p2) { - return p1.getA().compareTo(p2.getA()); - } - }; - - Collections.sort(negativeDeltas, comp); - x1Values.add(negativeDeltas.get(negativeDeltas.size() - 1).getB()); - Collections.sort(positiveDeltas, comp); - x2Values.add(positiveDeltas.get(0).getB()); - } + }; - //If there are mark passings between x1 and x2 no refactoring is needed - if (!markPassingInRange) { - Collections.sort(x1Values); - x1 = x1Values.get(0) - 2000; //Increase x1 by 2 seconds prevent small blank space - Collections.sort(x2Values); - x2 = x2Values.get(x2Values.size() - 1) + 2000; //Increase x2 by 2 seconds prevent small blank space + fillPotentialXValues(x1, x2, x1Values, x2Values, markPassingInRange); + + Pair passingRelationToSelection = checkPassingRelationToSelection(markPassingInRange); + Boolean everyPassingInRange = passingRelationToSelection.getA(); + Boolean twoPassingsInRangeBeforeError = passingRelationToSelection.getB(); + + if (!everyPassingInRange) { + if (twoPassingsInRangeBeforeError == null || !twoPassingsInRangeBeforeError) { + Collections.sort(x1Values, comp); + x1 = x1Values.get(x1Values.size() - 1).getB() - 2000; //Decrease x1 by 2 seconds to prevent small blank space + } + if (twoPassingsInRangeBeforeError == null || twoPassingsInRangeBeforeError) { + Collections.sort(x2Values, comp); + x2 = x2Values.get(x2Values.size() - 1).getB() + 2000; //Increase x2 by 2 seconds to prevent small blank space + } } plot.setLinearSelection(x1, x2); @@ -688,4 +667,93 @@ implements CompetitorSelectionChangeListener, RaceSelectionChangeListener { loadData(); } + /** + * Fills the to lists with potential values for a new selection to prevent white space in the display. + * Also fills a boolean matrix with information, if a mark passing is in the selection range or not. + * @param x1 The left range border + * @param x2 The right range border + * @param x1Values The list which is be filled with potential values for the left side + * @param x2Values The list which is be filled with potential values for the right side + * @param markPassingInRange The boolean matrix which is be filled with the mark passing informations + */ + public void fillPotentialXValues(double x1, double x2, ArrayList> x1Values, + ArrayList> x2Values, ArrayList> markPassingInRange) { + Comparator> comp = new Comparator>() { + @Override + public int compare(Pair p1, Pair p2) { + return p1.getA().compareTo(p2.getA()); + } + }; + + for (int i = 0; i < competitorID.size(); i++) { + CompetitorDTO competitor = competitorID.get(i); + ArrayList> negativeDeltas = new ArrayList>(); + ArrayList> positiveDeltas = new ArrayList>(); + Pair[] markPassingTimes = competitorsAndTimePointsDTO.getMarkPassings(competitor); + Double[] markPassingValues = chartData.getMarkPassings(competitor); + + markPassingInRange.add(new ArrayList()); + for (int j = 0; j < markPassingValues.length; j++) { + double markPassing = markPassingTimes[j].getB().doubleValue(); + if (markPassingValues[j] != null && (markPassing < x1 || markPassing > x2)) { + double delta = markPassing < x1 ? markPassing - x1 : markPassing - x2; + Pair p = new Pair(delta, markPassing); + if (delta < 0) { + negativeDeltas.add(p); + } else { + positiveDeltas.add(p); + } + markPassingInRange.get(i).add(false); + } else { + markPassingInRange.get(i).add(markPassingValues[j] != null ? true : null); + } + } + + Collections.sort(negativeDeltas, comp); + x1Values.add(negativeDeltas.get(negativeDeltas.size() - 1)); + Collections.sort(positiveDeltas, comp); + x2Values.add(positiveDeltas.get(0)); + } + } + + /** + * Checks the relation of the mark passings to the selection range. + * + * @param markPassingInRange + * A Boolean matrix filled by + * {@link AbstractChartPanel#fillPotentialXValues(double, double, ArrayList, ArrayList, ArrayList) + * fillPotentialXValues(...)} + * @return A pair of Booleans. Value A contains false if a passing is not in the selection (error), so that the + * selection range needs to be refactored. Value B returns true if two passings are in range before the + * error happened or false, if the error happens before two passings were in the selection. B can be + * null. + */ + public Pair checkPassingRelationToSelection(ArrayList> markPassingInRange) { + boolean everyPassingInRange = true; + Boolean twoPassingsInRangeBeforeError = null; + ArrayList competitorPassings = markPassingInRange.get(0); + for (int i = 0; i < competitorPassings.size(); i++) { + Boolean passingInRange = competitorPassings.get(i); + for (int j = 1; j < markPassingInRange.size(); j++) { + Boolean passingToCompare = markPassingInRange.get(j).get(i); + if (passingInRange != null) { + if (passingToCompare != null && everyPassingInRange) { + everyPassingInRange = passingInRange.equals(passingToCompare); + if (passingInRange && passingToCompare) { + twoPassingsInRangeBeforeError = true; + } + } else if (passingToCompare != null) { + if (passingInRange && passingToCompare) { + twoPassingsInRangeBeforeError = false; + } + } + } else { + passingInRange = passingToCompare; + } + } + } + + return new Pair(everyPassingInRange, twoPassingsInRangeBeforeError); + } + } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardPanel.java index a296b889218..c62ad3e351e 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardPanel.java @@ -6,9 +6,11 @@ import java.util.Collections; import java.util.Comparator; import java.util.Date; 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 com.google.gwt.cell.client.Cell; import com.google.gwt.cell.client.Cell.Context; @@ -1206,11 +1208,15 @@ public class LeaderboardPanel extends FormPanel implements TimeListener, PlaySta * Removes all Columns of type racecolumns of leaderboardTable */ private void removeRaceColumnsNotSelected(List selectedRaceColumns) { + Set selectedRaceColumnNames = new HashSet(); + for (RaceInLeaderboardDTO selectedRaceColumn : selectedRaceColumns) { + selectedRaceColumnNames.add(selectedRaceColumn.getRaceColumnName()); + } List> columnsToRemove = new ArrayList>(); for (int i = 0; i < getLeaderboardTable().getColumnCount(); i++) { Column c = getLeaderboardTable().getColumn(i); if (c instanceof RaceColumn - && (leaderboard == null || !selectedRaceColumns.contains(((RaceColumn) c).getRaceName()))) { + && (leaderboard == null || !selectedRaceColumnNames.contains(((RaceColumn) c).getRaceName()))) { columnsToRemove.add(c); } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java index 7eb17a32f2c..1bd56c4aecc 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java @@ -870,20 +870,34 @@ public class SailingServiceImpl extends RemoteServiceServlet implements SailingS for (RegattaDTO regatta : event.regattas) { for (RaceDTO race : regatta.races) { - List leaderboard = getLeaderboardByRace(race); + List leaderboard = getLeaderboardsByRace(race); if (leaderboard != null && !leaderboard.isEmpty()) { results.addAll(leaderboard); } } } + //Removing duplicates + HashSet set = new HashSet(results); + results.clear(); + results.addAll(set); return results; } - private List getLeaderboardByRace(RaceDTO race) { + @Override + public List getLeaderboardsByRace(RaceDTO race) { List results = new ArrayList(); - @SuppressWarnings("unused") // see below Map leaderboards = getService().getLeaderboards(); - // TODO Lennart, was this written by you? Please at least leave a TODO comment before pushing such empty bodies + for (Leaderboard leaderboard : leaderboards.values()) { + Iterable races = leaderboard.getRaceColumns(); + for (RaceInLeaderboard raceInLeaderboard : races) { + TrackedRace trackedRace = raceInLeaderboard.getTrackedRace(); + RaceDefinition trackedRaceDef = trackedRace != null ? trackedRace.getRace() : null; + if (trackedRaceDef != null && trackedRaceDef.getName().equals(race.name)) { + results.add(createStrippedLeaderboardDTO(leaderboard)); + break; + } + } + } return results; } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/LeaderboardDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/LeaderboardDTO.java index 21c50dd78ac..ef5bf6b8a02 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/LeaderboardDTO.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/LeaderboardDTO.java @@ -1,6 +1,7 @@ package com.sap.sailing.gwt.ui.shared; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.List; @@ -347,4 +348,62 @@ public class LeaderboardDTO implements IsSerializable { getRaceInLeaderboardByName(raceColumnName).setMedalRace(medalRace); } + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((competitorDisplayNames == null) ? 0 : competitorDisplayNames.hashCode()); + result = prime * result + ((competitors == null) ? 0 : competitors.hashCode()); + result = prime * result + (competitorsOrderedAccordingToTotalRank ? 1231 : 1237); + result = prime * result + Arrays.hashCode(discardThresholds); + result = prime * result + (hasCarriedPoints ? 1231 : 1237); + result = prime * result + ((name == null) ? 0 : name.hashCode()); + result = prime * result + ((races == null) ? 0 : races.hashCode()); + result = prime * result + ((rows == null) ? 0 : rows.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + LeaderboardDTO other = (LeaderboardDTO) obj; + if (competitorDisplayNames == null) { + if (other.competitorDisplayNames != null) + return false; + } else if (!competitorDisplayNames.equals(other.competitorDisplayNames)) + return false; + if (competitors == null) { + if (other.competitors != null) + return false; + } else if (!competitors.equals(other.competitors)) + return false; + if (competitorsOrderedAccordingToTotalRank != other.competitorsOrderedAccordingToTotalRank) + return false; + if (!Arrays.equals(discardThresholds, other.discardThresholds)) + return false; + if (hasCarriedPoints != other.hasCarriedPoints) + return false; + if (name == null) { + if (other.name != null) + return false; + } else if (!name.equals(other.name)) + return false; + if (races == null) { + if (other.races != null) + return false; + } else if (!races.equals(other.races)) + return false; + if (rows == null) { + if (other.rows != null) + return false; + } else if (!rows.equals(other.rows)) + return false; + return true; + } + } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/LeaderboardEntryDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/LeaderboardEntryDTO.java index dd1a3780566..e8da8c23deb 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/LeaderboardEntryDTO.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/LeaderboardEntryDTO.java @@ -30,5 +30,45 @@ public class LeaderboardEntryDTO implements IsSerializable { * {@link Course} being sailed in the race for which this object holds the scoring details. */ public List legDetails; + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (discarded ? 1231 : 1237); + result = prime * result + ((legDetails == null) ? 0 : legDetails.hashCode()); + result = prime * result + netPoints; + result = prime * result + ((reasonForMaxPoints == null) ? 0 : reasonForMaxPoints.hashCode()); + result = prime * result + totalPoints; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + LeaderboardEntryDTO other = (LeaderboardEntryDTO) obj; + if (discarded != other.discarded) + return false; + if (legDetails == null) { + if (other.legDetails != null) + return false; + } else if (!legDetails.equals(other.legDetails)) + return false; + if (netPoints != other.netPoints) + return false; + if (reasonForMaxPoints == null) { + if (other.reasonForMaxPoints != null) + return false; + } else if (!reasonForMaxPoints.equals(other.reasonForMaxPoints)) + return false; + if (totalPoints != other.totalPoints) + return false; + return true; + } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/LeaderboardRowDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/LeaderboardRowDTO.java index 886fef828d8..dc10600d823 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/LeaderboardRowDTO.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/LeaderboardRowDTO.java @@ -14,4 +14,41 @@ public class LeaderboardRowDTO implements IsSerializable { public CompetitorDTO competitor; public Map fieldsByRaceName; public Integer carriedPoints; + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((carriedPoints == null) ? 0 : carriedPoints.hashCode()); + result = prime * result + ((competitor == null) ? 0 : competitor.hashCode()); + result = prime * result + ((fieldsByRaceName == null) ? 0 : fieldsByRaceName.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + LeaderboardRowDTO other = (LeaderboardRowDTO) obj; + if (carriedPoints == null) { + if (other.carriedPoints != null) + return false; + } else if (!carriedPoints.equals(other.carriedPoints)) + return false; + if (competitor == null) { + if (other.competitor != null) + return false; + } else if (!competitor.equals(other.competitor)) + return false; + if (fieldsByRaceName == null) { + if (other.fieldsByRaceName != null) + return false; + } else if (!fieldsByRaceName.equals(other.fieldsByRaceName)) + return false; + return true; + } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/LegEntryDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/LegEntryDTO.java index a187f30e74f..c799c1e6734 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/LegEntryDTO.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/LegEntryDTO.java @@ -25,4 +25,104 @@ public class LegEntryDTO implements IsSerializable { public Integer numberOfJibes; public Integer numberOfTacks; public Integer numberOfPenaltyCircles; + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + + ((averageSpeedOverGroundInKnots == null) ? 0 : averageSpeedOverGroundInKnots.hashCode()); + result = prime * result + + ((currentSpeedOverGroundInKnots == null) ? 0 : currentSpeedOverGroundInKnots.hashCode()); + result = prime * result + ((distanceTraveledInMeters == null) ? 0 : distanceTraveledInMeters.hashCode()); + result = prime + * result + + ((estimatedTimeToNextWaypointInSeconds == null) ? 0 : estimatedTimeToNextWaypointInSeconds.hashCode()); + result = prime * result + (finished ? 1231 : 1237); + result = prime * result + ((gapToLeaderInSeconds == null) ? 0 : gapToLeaderInSeconds.hashCode()); + result = prime * result + ((numberOfJibes == null) ? 0 : numberOfJibes.hashCode()); + result = prime * result + ((numberOfPenaltyCircles == null) ? 0 : numberOfPenaltyCircles.hashCode()); + result = prime * result + ((numberOfTacks == null) ? 0 : numberOfTacks.hashCode()); + result = prime * result + ((rank == null) ? 0 : rank.hashCode()); + result = prime * result + (started ? 1231 : 1237); + result = prime * result + (int) (timeInMilliseconds ^ (timeInMilliseconds >>> 32)); + result = prime * result + ((velocityMadeGoodInKnots == null) ? 0 : velocityMadeGoodInKnots.hashCode()); + result = prime * result + + ((windwardDistanceToGoInMeters == null) ? 0 : windwardDistanceToGoInMeters.hashCode()); + return result; + } + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + LegEntryDTO other = (LegEntryDTO) obj; + if (averageSpeedOverGroundInKnots == null) { + if (other.averageSpeedOverGroundInKnots != null) + return false; + } else if (!averageSpeedOverGroundInKnots.equals(other.averageSpeedOverGroundInKnots)) + return false; + if (currentSpeedOverGroundInKnots == null) { + if (other.currentSpeedOverGroundInKnots != null) + return false; + } else if (!currentSpeedOverGroundInKnots.equals(other.currentSpeedOverGroundInKnots)) + return false; + if (distanceTraveledInMeters == null) { + if (other.distanceTraveledInMeters != null) + return false; + } else if (!distanceTraveledInMeters.equals(other.distanceTraveledInMeters)) + return false; + if (estimatedTimeToNextWaypointInSeconds == null) { + if (other.estimatedTimeToNextWaypointInSeconds != null) + return false; + } else if (!estimatedTimeToNextWaypointInSeconds.equals(other.estimatedTimeToNextWaypointInSeconds)) + return false; + if (finished != other.finished) + return false; + if (gapToLeaderInSeconds == null) { + if (other.gapToLeaderInSeconds != null) + return false; + } else if (!gapToLeaderInSeconds.equals(other.gapToLeaderInSeconds)) + return false; + if (numberOfJibes == null) { + if (other.numberOfJibes != null) + return false; + } else if (!numberOfJibes.equals(other.numberOfJibes)) + return false; + if (numberOfPenaltyCircles == null) { + if (other.numberOfPenaltyCircles != null) + return false; + } else if (!numberOfPenaltyCircles.equals(other.numberOfPenaltyCircles)) + return false; + if (numberOfTacks == null) { + if (other.numberOfTacks != null) + return false; + } else if (!numberOfTacks.equals(other.numberOfTacks)) + return false; + if (rank == null) { + if (other.rank != null) + return false; + } else if (!rank.equals(other.rank)) + return false; + if (started != other.started) + return false; + if (timeInMilliseconds != other.timeInMilliseconds) + return false; + if (velocityMadeGoodInKnots == null) { + if (other.velocityMadeGoodInKnots != null) + return false; + } else if (!velocityMadeGoodInKnots.equals(other.velocityMadeGoodInKnots)) + return false; + if (windwardDistanceToGoInMeters == null) { + if (other.windwardDistanceToGoInMeters != null) + return false; + } else if (!windwardDistanceToGoInMeters.equals(other.windwardDistanceToGoInMeters)) + return false; + return true; + } + + } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/RaceInLeaderboardDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/RaceInLeaderboardDTO.java old mode 100644 new mode 100755 index a88e72373a1..88a3b775fa5 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/RaceInLeaderboardDTO.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/RaceInLeaderboardDTO.java @@ -45,7 +45,9 @@ public class RaceInLeaderboardDTO implements IsSerializable { public int hashCode() { final int prime = 31; int result = 1; + result = prime * result + (medalRace ? 1231 : 1237); result = prime * result + ((raceColumnName == null) ? 0 : raceColumnName.hashCode()); + result = prime * result + ((trackedRaceIdentifier == null) ? 0 : trackedRaceIdentifier.hashCode()); return result; } @@ -58,11 +60,18 @@ public class RaceInLeaderboardDTO implements IsSerializable { if (getClass() != obj.getClass()) return false; RaceInLeaderboardDTO other = (RaceInLeaderboardDTO) obj; + if (medalRace != other.medalRace) + return false; if (raceColumnName == null) { if (other.raceColumnName != null) return false; } else if (!raceColumnName.equals(other.raceColumnName)) return false; + if (trackedRaceIdentifier == null) { + if (other.trackedRaceIdentifier != null) + return false; + } else if (!trackedRaceIdentifier.equals(other.trackedRaceIdentifier)) + return false; return true; } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/spectator/OverviewEventPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/spectator/OverviewEventPanel.java index 1c62225cf36..d7799009679 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/spectator/OverviewEventPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/spectator/OverviewEventPanel.java @@ -6,6 +6,8 @@ import java.util.Comparator; import java.util.Date; import java.util.List; +import com.google.gwt.cell.client.AbstractCell; +import com.google.gwt.cell.client.Cell; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.event.dom.client.KeyUpEvent; @@ -13,6 +15,9 @@ import com.google.gwt.event.dom.client.KeyUpHandler; import com.google.gwt.event.logical.shared.ValueChangeEvent; import com.google.gwt.event.logical.shared.ValueChangeHandler; import com.google.gwt.i18n.client.DateTimeFormat; +import com.google.gwt.safehtml.shared.SafeHtmlBuilder; +import com.google.gwt.safehtml.shared.SafeHtmlUtils; +import com.google.gwt.user.cellview.client.CellList; import com.google.gwt.user.cellview.client.CellTable; import com.google.gwt.user.cellview.client.ColumnSortEvent; import com.google.gwt.user.cellview.client.ColumnSortEvent.Handler; @@ -32,11 +37,14 @@ import com.google.gwt.view.client.ListDataProvider; import com.google.gwt.view.client.SelectionChangeEvent; import com.google.gwt.view.client.SingleSelectionModel; import com.sap.sailing.gwt.ui.client.AbstractEventPanel; +import com.sap.sailing.gwt.ui.client.CompetitorSelectionModel; import com.sap.sailing.gwt.ui.client.ErrorReporter; import com.sap.sailing.gwt.ui.client.EventRefresher; import com.sap.sailing.gwt.ui.client.SailingServiceAsync; import com.sap.sailing.gwt.ui.client.StringMessages; +import com.sap.sailing.gwt.ui.leaderboard.LeaderboardPanel; import com.sap.sailing.gwt.ui.shared.EventDTO; +import com.sap.sailing.gwt.ui.shared.LeaderboardDTO; /** * @@ -51,20 +59,29 @@ public class OverviewEventPanel extends AbstractEventPanel { private TextBox textBoxUntil; private CheckBox checkBoxLive; - private CaptionPanel captionPanelEvents; + private CaptionPanel eventsCaptionPanel; private Button btnShowLeaderboards; - private CellTable eventTable; - private ListDataProvider eventTableProvider; - private SingleSelectionModel eventTableSelectionModel; + private CellTable eventsTable; + private ListDataProvider eventsTableProvider; + private SingleSelectionModel eventsTableSelectionModel; - private CaptionPanel captionPanelLeaderboards; + private CaptionPanel leaderboardsCaptionPanel; + private CellList leaderboardsList; + private ListDataProvider leaderboardsListProvider; + private SingleSelectionModel leaderboardsListSelectionModel; + + private CaptionPanel leaderboardCaptionPanel; + private LeaderboardDTO currentLeaderboard; + private LeaderboardPanel displayedLeaderboardPanel; private List availableEvents; + private List availableLeaderboards; public OverviewEventPanel(SailingServiceAsync sailingService, EventRefresher eventRefresher, ErrorReporter errorReporter, final StringMessages stringConstants) { super(sailingService, eventRefresher, errorReporter, stringConstants); availableEvents = new ArrayList(); + availableLeaderboards = new ArrayList(); VerticalPanel mainPanel = new VerticalPanel(); this.setWidget(mainPanel); @@ -72,8 +89,8 @@ public class OverviewEventPanel extends AbstractEventPanel { // Build search GUI CaptionPanel captionPanelSearch = new CaptionPanel(stringConstants.searchEvents()); - mainPanel.add(captionPanelSearch); captionPanelSearch.setWidth("100%"); + mainPanel.add(captionPanelSearch); HorizontalPanel panelSearch = new HorizontalPanel(); captionPanelSearch.add(panelSearch); @@ -137,12 +154,12 @@ public class OverviewEventPanel extends AbstractEventPanel { listsSplitPanel.setWidth("100%"); // Build events GUI - captionPanelEvents = new CaptionPanel(stringConstants.events()); - captionPanelEvents.setWidth("100%"); - listsSplitPanel.add(captionPanelEvents); + eventsCaptionPanel = new CaptionPanel(stringConstants.events()); + eventsCaptionPanel.setWidth("100%"); + listsSplitPanel.add(eventsCaptionPanel); VerticalPanel eventsPanel = new VerticalPanel(); - captionPanelEvents.setContentWidget(eventsPanel); + eventsCaptionPanel.setContentWidget(eventsPanel); eventsPanel.setWidth("100%"); // Create event functional elements @@ -164,7 +181,7 @@ public class OverviewEventPanel extends AbstractEventPanel { btnShowLeaderboards.addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent c) { - if (eventTableSelectionModel.getSelectedObject() != null) { + if (eventsTableSelectionModel.getSelectedObject() != null) { setLeaderboardsPanelVisible(true); } else { Window.alert(stringConstants.noEventSelected()); @@ -175,68 +192,67 @@ public class OverviewEventPanel extends AbstractEventPanel { functionPanelEvents.add(btnShowLeaderboards); // Create event table - { - eventTable = new CellTable(); - eventTable.setWidth("100%"); - - //Creating location column - TextColumn locationColumn = new TextColumn() { - @Override - public String getValue(EventDTO eventDTO) { - String locations = eventDTO.locations; - return locations != null ? locations : stringConstants.locationNotAvailable(); - } - }; - locationColumn.setSortable(true); - //Creating event name column - TextColumn nameColumn = new TextColumn() { - @Override - public String getValue(EventDTO eventDTO) { - return eventDTO.name; - } - }; - nameColumn.setSortable(true); - //Creating start date column - TextColumn startDateColumn = new TextColumn() { - @Override - public String getValue(EventDTO eventDTO) { - Date start = eventDTO.regattas.get(0).races.get(0).startOfRace; - return start != null ? dateFormatter.render(start) : stringConstants.startDateNotAvailable(); - } - }; - startDateColumn.setSortable(true); - - eventTable.addColumn(locationColumn, stringConstants.location()); - eventTable.addColumn(nameColumn, stringConstants.eventName()); - eventTable.addColumn(startDateColumn, stringConstants.startDate()); - - //Adding the data provider and creating the sort handler - eventTableProvider = new ListDataProvider(); - eventTableProvider.addDataDisplay(eventTable); - Handler eventSortHandler = getEventSortHandler(eventTableProvider.getList(), locationColumn, nameColumn, startDateColumn); - eventTable.addColumnSortHandler(eventSortHandler); - - //Adding the selection model - eventTableSelectionModel = new SingleSelectionModel(); - eventTable.setSelectionModel(eventTableSelectionModel); - eventTableSelectionModel.addSelectionChangeHandler(new SelectionChangeEvent.Handler() { - @Override - public void onSelectionChange(SelectionChangeEvent event) { - eventSelectionChanged(event); - } - }); - - eventsPanel.add(eventTable); - } + eventsTable = new CellTable(); + eventsTable.setWidth("100%"); + + // Creating location column + TextColumn locationColumn = new TextColumn() { + @Override + public String getValue(EventDTO eventDTO) { + String locations = eventDTO.locations; + return locations != null ? locations : stringConstants.locationNotAvailable(); + } + }; + locationColumn.setSortable(true); + // Creating event name column + TextColumn nameColumn = new TextColumn() { + @Override + public String getValue(EventDTO eventDTO) { + return eventDTO.name; + } + }; + nameColumn.setSortable(true); + // Creating start date column + TextColumn startDateColumn = new TextColumn() { + @Override + public String getValue(EventDTO eventDTO) { + Date start = eventDTO.regattas.get(0).races.get(0).startOfRace; + return start != null ? dateFormatter.render(start) : stringConstants.startDateNotAvailable(); + } + }; + startDateColumn.setSortable(true); + + eventsTable.addColumn(locationColumn, stringConstants.location()); + eventsTable.addColumn(nameColumn, stringConstants.eventName()); + eventsTable.addColumn(startDateColumn, stringConstants.startDate()); + + // Adding the data provider and creating the sort handler + eventsTableProvider = new ListDataProvider(); + eventsTableProvider.addDataDisplay(eventsTable); + Handler eventSortHandler = getEventSortHandler(eventsTableProvider.getList(), locationColumn, nameColumn, + startDateColumn); + eventsTable.addColumnSortHandler(eventSortHandler); + + // Adding the selection model + eventsTableSelectionModel = new SingleSelectionModel(); + eventsTable.setSelectionModel(eventsTableSelectionModel); + eventsTableSelectionModel.addSelectionChangeHandler(new SelectionChangeEvent.Handler() { + @Override + public void onSelectionChange(SelectionChangeEvent event) { + eventSelectionChanged(); + } + }); + + eventsPanel.add(eventsTable); // Build leaderboards GUI - captionPanelLeaderboards = new CaptionPanel(stringConstants.leaderboards()); - captionPanelLeaderboards.setVisible(false); - captionPanelLeaderboards.setWidth("100%"); - listsSplitPanel.add(captionPanelLeaderboards); + leaderboardsCaptionPanel = new CaptionPanel(stringConstants.leaderboards()); + leaderboardsCaptionPanel.setVisible(false); + leaderboardsCaptionPanel.setWidth("100%"); + listsSplitPanel.add(leaderboardsCaptionPanel); VerticalPanel leaderboardsPanel = new VerticalPanel(); - captionPanelLeaderboards.setContentWidget(leaderboardsPanel); + leaderboardsCaptionPanel.setContentWidget(leaderboardsPanel); leaderboardsPanel.setWidth("100%"); // Create leaderboard functional elements @@ -255,13 +271,43 @@ public class OverviewEventPanel extends AbstractEventPanel { functionPanelLeaderboards.add(btnHideLeaderboards); // Create leaderboard list - // TODO - + Cell leaderboardCell = new AbstractCell() { + @Override + public void render(Context context, LeaderboardDTO leaderboard, SafeHtmlBuilder safeHtmlBuilder) { + safeHtmlBuilder.append(SafeHtmlUtils.fromString(leaderboard.name)); + } + }; + leaderboardsList = new CellList(leaderboardCell); + + leaderboardsListProvider = new ListDataProvider(); + leaderboardsListProvider.addDataDisplay(leaderboardsList); + + leaderboardsListSelectionModel = new SingleSelectionModel(); + leaderboardsListSelectionModel.addSelectionChangeHandler(new SelectionChangeEvent.Handler() { + @Override + public void onSelectionChange(SelectionChangeEvent event) { + leaderboardSelectionChanged(); + } + }); + leaderboardsList.setSelectionModel(leaderboardsListSelectionModel); + + leaderboardsPanel.add(leaderboardsList); + + //Create leaderboard container + //TODO + leaderboardCaptionPanel = new CaptionPanel(); + leaderboardCaptionPanel.setVisible(false); + leaderboardCaptionPanel.setWidth("100%"); + mainPanel.add(leaderboardCaptionPanel); + + displayedLeaderboardPanel = null; + + //Loading the data Runnable displayEvents = new Runnable() { @Override public void run() { - eventTableProvider.getList().clear(); - eventTableProvider.getList().addAll(availableEvents); + eventsTableProvider.getList().clear(); + eventsTableProvider.getList().addAll(availableEvents); } }; loadEvents(displayEvents); @@ -290,13 +336,13 @@ public class OverviewEventPanel extends AbstractEventPanel { @Override public void onFailure(Throwable caught) { - errorReporter.reportError("Error trying to obtain list of leaderboards: " + caught.getMessage()); + errorReporter.reportError("Error trying to obtain list of events: " + caught.getMessage()); } }); } private void refreshEventsTable() { - final EventDTO selectedEvent = eventTableSelectionModel.getSelectedObject(); + final EventDTO selectedEvent = eventsTableSelectionModel.getSelectedObject(); //Clear Search criteria textBoxLocation.setText(""); @@ -310,15 +356,15 @@ public class OverviewEventPanel extends AbstractEventPanel { Runnable displayAndReselect = new Runnable() { @Override public void run() { - eventTableProvider.getList().clear(); - eventTableProvider.getList().addAll(availableEvents); + eventsTableProvider.getList().clear(); + eventsTableProvider.getList().addAll(availableEvents); //Now sort again according to selected criterion - ColumnSortEvent.fire(eventTable, eventTable.getColumnSortList()); + ColumnSortEvent.fire(eventsTable, eventsTable.getColumnSortList()); //Reselect the event if (selectedEvent != null) { - for (EventDTO event : eventTableProvider.getList()) { + for (EventDTO event : eventsTableProvider.getList()) { if (event.equals(selectedEvent)) { - eventTableSelectionModel.setSelected(selectedEvent, true); + eventsTableSelectionModel.setSelected(selectedEvent, true); break; } } @@ -327,27 +373,87 @@ public class OverviewEventPanel extends AbstractEventPanel { }; loadEvents(displayAndReselect); //Now sort again according to selected criterion - ColumnSortEvent.fire(eventTable, eventTable.getColumnSortList()); + ColumnSortEvent.fire(eventsTable, eventsTable.getColumnSortList()); } private void setLeaderboardsPanelVisible(boolean visible) { - captionPanelLeaderboards.setVisible(visible); - captionPanelEvents.setWidth(visible ? "95%" : "100%"); + leaderboardsCaptionPanel.setVisible(visible); + eventsCaptionPanel.setWidth(visible ? "95%" : "100%"); btnShowLeaderboards.setEnabled(!visible); } - private void eventSelectionChanged(SelectionChangeEvent event) { - // TODO Actions when the event selection changed - EventDTO selectedEvent = eventTableSelectionModel.getSelectedObject(); + private void eventSelectionChanged() { + EventDTO selectedEvent = eventsTableSelectionModel.getSelectedObject(); if (selectedEvent != null) { setLeaderboardsPanelVisible(true); - //TODO Display data in leaderboard list + + Runnable displayLeaderboards = new Runnable() { + @Override + public void run() { + leaderboardsListProvider.getList().clear(); + leaderboardsListProvider.getList().addAll(availableLeaderboards); + } + }; + loadLeaderboards(selectedEvent, displayLeaderboards); } else { setLeaderboardsPanelVisible(false); btnShowLeaderboards.setEnabled(false); } } + private void loadLeaderboards(EventDTO forEvent, final Runnable actionAfterLoading) { + sailingService.getLeaderboardsByEvent(forEvent, new AsyncCallback>() { + + @Override + public void onSuccess(List leaderboards) { + if (leaderboards != null) { + availableLeaderboards = leaderboards; + } else { + availableLeaderboards.clear(); + } + if (actionAfterLoading != null) { + actionAfterLoading.run(); + } + } + + @Override + public void onFailure(Throwable caught) { + errorReporter.reportError("Error trying to obtain list of leaderboards: " + caught.getMessage()); + } + }); + } + + private void leaderboardSelectionChanged() { + //TODO Actions when the leaderboard selection changes + LeaderboardDTO selectedLeaderboard = leaderboardsListSelectionModel.getSelectedObject(); + if (selectedLeaderboard != null) { + setDisplayedLeaderboard(selectedLeaderboard); + setLeaderboardPanelVisible(true); + } else { + setLeaderboardPanelVisible(false); + } + } + + private void setLeaderboardPanelVisible(boolean visible) { + leaderboardCaptionPanel.setVisible(visible); + //TODO button management + } + + private void setDisplayedLeaderboard(LeaderboardDTO boardToDisplay) { + //If the currentLeaderboard equals the boardToDisplay, there is no need to create a new LeaderboardPanel + if (!boardToDisplay.equals(currentLeaderboard)) { + if (displayedLeaderboardPanel != null) { + leaderboardCaptionPanel.remove(displayedLeaderboardPanel); + } + currentLeaderboard = boardToDisplay; + CompetitorSelectionModel competitorSelectionModel = new CompetitorSelectionModel(true); + displayedLeaderboardPanel = new LeaderboardPanel(sailingService, competitorSelectionModel, + currentLeaderboard.name, errorReporter, stringConstants); + leaderboardCaptionPanel.add(displayedLeaderboardPanel); + leaderboardCaptionPanel.setCaptionText(stringConstants.leaderboard() + " - " + boardToDisplay.name); + } + } + private void onCheckBoxLiveChange() { if (checkBoxLive.getValue()) { String today = DateTimeFormat.getFormat("dd.MM.yyyy").format(new Date()).toString(); @@ -381,14 +487,14 @@ public class OverviewEventPanel extends AbstractEventPanel { until = new Date(time); } catch (IllegalArgumentException e) {} //Filter list by criteria - eventTableProvider.getList().clear(); + eventsTableProvider.getList().clear(); for (EventDTO event : availableEvents) { if (checkSearchCriteria(event, location, name, onlyLive, from, until)) { - eventTableProvider.getList().add(event); + eventsTableProvider.getList().add(event); } } //Now sort again according to selected criterion - ColumnSortEvent.fire(eventTable, eventTable.getColumnSortList()); + ColumnSortEvent.fire(eventsTable, eventsTable.getColumnSortList()); } private boolean checkSearchCriteria(EventDTO forEvent, String location, String name, boolean onlyLive, Date from, Date until) { diff --git a/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/Spectator.gwt.xml b/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/Spectator.gwt.xml index e31becd7081..0aad8b7da04 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/Spectator.gwt.xml +++ b/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/Spectator.gwt.xml @@ -47,5 +47,6 @@ + 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 1f320244e03..ace7f5f2b8d 100755 --- 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 @@ -290,7 +290,7 @@ public class RacingEventServiceImpl implements RacingEventService, EventFetcher, } } DynamicTrackedEvent trackedEvent = tracker.getTrackedEvent(); - ensureEventIsObservedForDefaultLeaderboard(trackedEvent); + ensureEventIsObservedForDefaultLeaderboardAndAutoLeaderboardLinking(trackedEvent); if (timeoutInMilliseconds != -1) { scheduleAbortTrackerAfterInitialTimeout(tracker, timeoutInMilliseconds); } @@ -335,14 +335,14 @@ public class RacingEventServiceImpl implements RacingEventService, EventFetcher, } } DynamicTrackedEvent trackedEvent = tracker.getTrackedEvent(); - ensureEventIsObservedForDefaultLeaderboard(trackedEvent); + ensureEventIsObservedForDefaultLeaderboardAndAutoLeaderboardLinking(trackedEvent); if (timeoutInMilliseconds != -1) { scheduleAbortTrackerAfterInitialTimeout(tracker, timeoutInMilliseconds); } return tracker.getRacesHandle(); } - private void ensureEventIsObservedForDefaultLeaderboard(DynamicTrackedEvent trackedEvent) { + private void ensureEventIsObservedForDefaultLeaderboardAndAutoLeaderboardLinking(DynamicTrackedEvent trackedEvent) { synchronized (eventsObservedForDefaultLeaderboard) { if (!eventsObservedForDefaultLeaderboard.contains(trackedEvent)) { trackedEvent.addRaceListener(new RaceListener() { @@ -352,6 +352,7 @@ public class RacingEventServiceImpl implements RacingEventService, EventFetcher, @Override public void raceAdded(TrackedRace trackedRace) { + linkRaceToConfiguredLeaderboardColumns(trackedRace); leaderboardsByName.get(DefaultLeaderboardName.DEFAULT_LEADERBOARD_NAME).addRace(trackedRace, trackedRace.getRace().getName(), /* medalRace */ false); } @@ -361,6 +362,23 @@ public class RacingEventServiceImpl implements RacingEventService, EventFetcher, } } + /** + * Based on the trackedRace's {@link TrackedRace#getRaceIdentifier() race identifier}, the tracked race + * is (re-)associated to all {@link RaceInLeaderboard race columns} that currently have no + * {@link RaceInLeaderboard#getTrackedRace() tracked race assigned} and whose + * {@link RaceInLeaderboard#getRaceIdentifier() race identifier} equals that of trackedRace. + */ + private void linkRaceToConfiguredLeaderboardColumns(TrackedRace trackedRace) { + RaceIdentifier trackedRaceIdentifier = trackedRace.getRaceIdentifier(); + for (Leaderboard leaderboard : getLeaderboards().values()) { + for (RaceInLeaderboard column : leaderboard.getRaceColumns()) { + if (trackedRaceIdentifier.equals(column.getRaceIdentifier()) && column.getTrackedRace() == null) { + column.setTrackedRace(trackedRace); + } + } + } + } + @Override public synchronized void stopTracking(Event event) throws MalformedURLException, IOException, InterruptedException { if (raceTrackersByEvent.containsKey(event)) {