+ * + * The back-end knows the race-specific set of competitors and can tell the client. However, we'd like to keep the + * network traffic required to keep this information up to date at a minimum. The key idea is to use an MD5 hash across + * the ordered race's competitor IDs and send that with each {@link GetRaceMapDataAction} request. The server can then + * compare this hash with the current race's competitor set and send an update if and only if there is a difference + * detected. This will, for the most part, reduce the communication to an initial request by the client as long as the + * race's competitors are not known, and the additional MD5 hash being sent with each request. + *
+ *
+ * Objects of this type start out with an "unknown" race competitor set. When the
+ * {@link #getMd5OfIdsAsStringOfCompetitorParticipatingInRaceInAlphanumericOrderOfTheirID() MD5 hash} for the currently
+ * known race competitors is asked, null will initially be returned. When the client has received the race
+ * competitors the client is expected to call {@link #setIdsAsStringsOfCompetitorsInRace(Iterable)}. If the argument to
+ * this call was not null,
+ * {@link #getMd5OfIdsAsStringOfCompetitorParticipatingInRaceInAlphanumericOrderOfTheirID()} will from then on return
+ * the MD5 hash of the competitor IDs in order.
+ *
+ * @author Axel Uhl (D043530)
+ *
+ */
+public class RaceCompetitorIdsAsStringWithMD5Hash implements Serializable {
+ private static final long serialVersionUID = 6881670366184539259L;
+
+ /**
+ * The {@link #competitorSelection} manages the competitors for the entire leaderboard which may be a
+ * superset of the competitors participating in the race shown by this map. For certain operations,
+ * however, it is useful to know exactly the competitors participating in the race shown by this map.
+ * For example, boat positions should only be requested from the server for those competitors, not all
+ * in the regatta, particularly when racing in split fleets.
+ * + * To keep this set consistent, the client sends a secure hash of this set to the server where the hash + * will be compared with the hash across the set of competitor IDs as string.
+ *
+ * If this field is
- *
- * See also bug 2604 and bug 2035.
- */
- public int getLegCount(String raceColumnName, RaceIdentifier preselectedRace) {
- int result = -1;
- final boolean preselectedRaceIsInRaceColumn = isRaceInColumn(preselectedRace, raceColumnName);
- for (LeaderboardRowDTO row : rows.values()) {
- LeaderboardEntryDTO leaderboardEntryDTO = row.fieldsByRaceColumnName.get(raceColumnName);
- if (leaderboardEntryDTO != null && leaderboardEntryDTO.legDetails != null
- // when no race is pre-selected, always use the actual leg count;
- // otherwise, if the pre-selected race is in the column identified by
- // raceColumnName, use the entry only if it's in the pre-selected race;
- // otherwise (a race is pre-selected but a different column's leg count
- // is requested), use the regular leg count as is.
- && (preselectedRace == null || !preselectedRaceIsInRaceColumn || preselectedRace.equals(leaderboardEntryDTO.race))) {
- result = Math.max(result, leaderboardEntryDTO.legDetails.size());
- }
- }
- return result;
- }
-
- private boolean isRaceInColumn(RaceIdentifier preselectedRace, String raceColumnName) {
- RaceColumnDTO raceColumn = getRaceColumnByName(raceColumnName);
- return raceColumn.containsRace(preselectedRace);
- }
-
- /**
- * Tells if the
+ *
+ * See also bug 2604 and bug 2035.
+ */
+ public int getLegCount(String raceColumnName, RaceIdentifier preselectedRace) {
+ int result = -1;
+ final boolean preselectedRaceIsInRaceColumn = isRaceInColumn(preselectedRace, raceColumnName);
+ for (LeaderboardRowDTO row : rows.values()) {
+ LeaderboardEntryDTO leaderboardEntryDTO = row.fieldsByRaceColumnName.get(raceColumnName);
+ if (leaderboardEntryDTO != null && leaderboardEntryDTO.legDetails != null
+ // when no race is pre-selected, always use the actual leg count;
+ // otherwise, if the pre-selected race is in the column identified by
+ // raceColumnName, use the entry only if it's in the pre-selected race;
+ // otherwise (a race is pre-selected but a different column's leg count
+ // is requested), use the regular leg count as is.
+ && (preselectedRace == null || !preselectedRaceIsInRaceColumn || preselectedRace.equals(leaderboardEntryDTO.race))) {
+ result = Math.max(result, leaderboardEntryDTO.legDetails.size());
+ }
+ }
+ return result;
+ }
+
+ private boolean isRaceInColumn(RaceIdentifier preselectedRace, String raceColumnName) {
+ RaceColumnDTO raceColumn = getRaceColumnByName(raceColumnName);
+ return raceColumn.containsRace(preselectedRace);
+ }
+
+ /**
+ * Tells if the
+ *
+ * @author Axel Uhl (d043530)
+ *
+ */
+public class BasicRaceDTO extends NamedDTO {
+ private static final long serialVersionUID = -7884808503795229609L;
+
+ public Date startOfRace;
+ public Date endOfRace;
+ public TrackedRaceDTO trackedRace;
+
+ public BasicRaceDTO() {} // for GWT serialization only
+
+ public BasicRaceDTO(RegattaAndRaceIdentifier raceIdentifier, TrackedRaceDTO trackedRace) {
+ super(raceIdentifier.getRaceName());
+ this.trackedRace = trackedRace;
+ }
+
+ /**
+ * @see {@link TrackedRace#isLive} for further explanation.
+ * @param serverTimePointAsMillis
+ * the time point (in server clock time) at which to determine whether the race is/was live
+ * @return
@@ -12,7 +9,7 @@ import com.sap.sailing.domain.common.TimingConstants;
* @author Axel Uhl (d043530)
*
*/
-public class RaceDTO extends NamedDTO {
+public class RaceDTO extends BasicRaceDTO {
private static final long serialVersionUID = 2613189982608149975L;
public enum RaceLiveState { NOT_TRACKED, TRACKED, TRACKED_AND_LIVE, TRACKED_BUT_NOT_SCHEDULED };
@@ -24,13 +21,10 @@ public class RaceDTO extends NamedDTO {
*/
public boolean isTracked;
- public Date startOfRace;
- public Date endOfRace;
public RaceStatusDTO status;
public PlacemarkOrderDTO places;
- public TrackedRaceDTO trackedRace;
public TrackedRaceStatisticsDTO trackedRaceStatistics;
private String regattaName;
@@ -43,21 +37,20 @@ public class RaceDTO extends NamedDTO {
}
public RaceDTO(RegattaAndRaceIdentifier raceIdentifier, TrackedRaceDTO trackedRace, boolean isCurrentlyTracked) {
- super(raceIdentifier.getRaceName());
+ super(raceIdentifier, trackedRace);
this.regattaName = raceIdentifier.getRegattaName();
- this.trackedRace = trackedRace;
this.isTracked = isCurrentlyTracked;
}
public RaceLiveState getLiveState(long serverTimePointAsMillis) {
- RaceLiveState result = RaceLiveState.NOT_TRACKED;
- if(trackedRace != null && trackedRace.hasGPSData && trackedRace.hasWindData) {
+ RaceLiveState result = RaceLiveState.NOT_TRACKED;
+ if (trackedRace != null && trackedRace.hasGPSData && trackedRace.hasWindData) {
result = RaceLiveState.TRACKED;
- if(isLive(serverTimePointAsMillis)) {
+ if (isLive(serverTimePointAsMillis)) {
result = RaceLiveState.TRACKED_AND_LIVE;
- if(startOfRace == null) {
+ if (startOfRace == null) {
result = RaceLiveState.TRACKED_BUT_NOT_SCHEDULED;
- }
+ }
}
}
return result;
@@ -71,64 +64,15 @@ public class RaceDTO extends NamedDTO {
return regattaName;
}
- /**
- * @see {@link TrackedRace#isLive} for further explanation.
- * @param serverTimePointAsMillis
- * the time point (in server clock time) at which to determine whether the race is/was live
- * @return
- *
- * Note that this class is not thread safe. If multiple threads can access the same instance concurrently, callers have
- * to ensure proper synchronization. Concurrent reads are permissible while any write should block all other operations.
+ * A spatial data structure that provides efficient (O(log n)) access to nearest neighbors and
+ * to objects in a certain radius around another object. The location of objects is provided as
+ * {@link Position}.
*
* @param null, nothing is known yet about the competitor-to-race assignment.
+ */
+ private Setnull if it hasn't been computed before
+ * or if {@link #idsAsStringOfCompetitorsParticipatingInRace} is still null.
+ */
+ private byte[] md5OfIdsAsStringOfCompetitorParticipatingInRaceInAlphanumericOrderOfTheirID;
+
+ /**
+ * Starts out with an unknown set of race competitors. The {@link #getIdsOfCompetitorsParticipatingInRaceAsStrings()}
+ * and the {@link #getMd5OfIdsAsStringOfCompetitorParticipatingInRaceInAlphanumericOrderOfTheirID()} will return
+ * null until a call to {@link #setIdsAsStringsOfCompetitorsInRace(Iterable)} with a non-
+ * null argument has been performed.
+ */
+ public RaceCompetitorIdsAsStringWithMD5Hash() {
+ }
+
+ /**
+ * Immediately calls {@link #setIdsAsStringsOfCompetitorsInRace(Set)} with the argument provided. If that
+ * was null then this has the same effect as calling {@link #RaceCompetitorIdsAsStringWithMD5Hash()}.
+ */
+ public RaceCompetitorIdsAsStringWithMD5Hash(Setnull regatta name if this DTO represents a RegattaLeaderboard.
- */
- public String regattaName;
- public String displayName;
- public UUID defaultCourseAreaId;
- public String defaultCourseAreaName;
- public ScoringSchemeType scoringScheme;
- public LeaderboardType type;
-
- private Long delayToLiveInMillisForLatestRace;
-
- public AbstractLeaderboardDTO() {
- races = new ArrayListraceColumnName has at least one competitor who has valid
- * {@link LeaderboardEntryDTO#legDetails} for that race column, the maximum number of entries of all such competitors in
- * the leg details is returned, telling the number of legs that the race column shall display. Otherwise, -1 is returned.
- * If you specify an non null preselectedRace the leg count is calculated only for that race for the column
- * holding that race. All other columns will be unaffected by preselectedRace and will return their regular
- * leg count. Note that this may lead to a situation where some competitors may have sailed more legs than others because
- * their fleet association may have varied across races.competitor scored (and therefore presumably participated) in a medal race represented
- * in this leaderboard.
- */
- public boolean scoredInMedalRace(CompetitorDTO competitor) {
- LeaderboardRowDTO row = rows.get(competitor);
- for (RaceColumnDTO race : races) {
- if (race.isMedalRace() && row.fieldsByRaceColumnName.get(race.getRaceColumnName()).totalPoints > 0) {
- return true;
- }
- }
- return false;
- }
-
- public Double getNetPoints(CompetitorDTO competitor, String nameOfLastRaceSoFar) {
- Double netPoints = null;
- LeaderboardRowDTO row = rows.get(competitor);
- if (row != null) {
- LeaderboardEntryDTO field = row.fieldsByRaceColumnName.get(nameOfLastRaceSoFar);
- if (field != null && field.netPoints != null) {
- netPoints = field.netPoints;
- }
- }
- return netPoints;
- }
-
- public boolean raceIsTracked(String raceColumnName) {
- for (RaceColumnDTO race : races) {
- if (race.getRaceColumnName().equals(raceColumnName)) {
- for (FleetDTO fleet : race.getFleets()) {
- if (race.isTrackedRace(fleet)) {
- return true;
- }
- }
- }
- }
- return false;
-
- }
-
- public boolean raceIsMedalRace(String raceColumnName) {
- return getRaceColumnByName(raceColumnName).isMedalRace();
- }
-
- /**
- * If the {@link RaceColumnDTO} by the name raceColumnName doesn't exist yet within this leaderboard
- * DTO, it is created, setting is {@link RaceColumnDTO#isValidInTotalScore()} to true. This method
- * ensures that a fleet named fleetName is present. If it's not present yet, it's added to the race
- * column's fleet name list. The trackedRaceIdentifier and race are associated with the
- * column for the fleet identified by fleetName.
- *
- * @param explicitFactor
- * factor by which to multiply the race column's points for the overall score; if null, the
- * default will be determined by whether or not the column is marked as medal race
- * @param effectiveFactor
- * is what you get when you call {@link RaceColumn#getFactor()} on the race column that the resulting
- * {@link RaceColumnDTO} represents
- * @param regattaName
- * must not be null if seriesName is not null; specified
- * separately from trackedRaceIdentifier because a column may belong to regatta/series
- * despite not having a tracked race associated
- * @param seriesName
- * if null, this method will produce a {@link RaceColumnDTO}, otherwise a
- * {@link RaceColumnInSeriesDTO}
- * @param fleetDTO
- * must not be null
- */
- public RaceColumnDTO addRace(String raceColumnName, Double explicitFactor, double effectiveFactor,
- String regattaName, String seriesName, FleetDTO fleetDTO, boolean medalRace, RegattaAndRaceIdentifier trackedRaceIdentifier, RaceDTO race) {
- assert fleetDTO != null;
- RaceColumnDTO raceColumnDTO = getRaceColumnByName(raceColumnName);
- if (raceColumnDTO == null) {
- raceColumnDTO = RaceColumnDTOFactory.INSTANCE.createRaceColumnDTO(raceColumnName, medalRace,
- explicitFactor, regattaName, seriesName);
- races.add(raceColumnDTO);
- }
- raceColumnDTO.setEffectiveFactor(effectiveFactor);
- boolean contains = false;
- for (FleetDTO fleet : raceColumnDTO.getFleets()) {
- if (fleet.getName().equals(fleetDTO.getName())) {
- contains = true;
- break;
- }
- }
- if (!contains) {
- raceColumnDTO.addFleet(fleetDTO);
- }
- raceColumnDTO.setRaceIdentifier(fleetDTO, trackedRaceIdentifier);
- raceColumnDTO.setRace(fleetDTO, race);
- return raceColumnDTO;
- }
-
- public RaceColumnDTO createEmptyRaceColumn(String raceColumnName, boolean medalRace, String regattaName, String seriesName) {
- final RaceColumnDTO raceColumn = RaceColumnDTOFactory.INSTANCE.createRaceColumnDTO(raceColumnName,
- medalRace, /* explicit factor */ null, regattaName, seriesName);
- races.add(raceColumn);
- return raceColumn;
- }
-
- public RaceColumnDTO getRaceColumnByName(String raceColumnName) {
- for (RaceColumnDTO race : races) {
- if (race.getRaceColumnName().equals(raceColumnName)) {
- return race;
- }
- }
- return null;
- }
-
- public int getRaceColumnsCount() {
- return races.size();
- }
-
- public int getRacesCount() {
- int result = 0;
- for (RaceColumnDTO race : getRaceList()) {
- result += race.getFleets().size();
- }
- return result;
- }
-
- public int getTrackedRacesCount() {
- int result = 0;
- for (RaceColumnDTO raceColumn : getRaceList()) {
- for (FleetDTO fleet : raceColumn.getFleets()) {
- RaceDTO race = raceColumn.getRace(fleet);
- if(race != null && race.trackedRace != null && race.trackedRace.hasGPSData && race.trackedRace.hasWindData) {
- result++;
- }
- }
- }
- return result;
- }
-
- public Listnull if no start dates of the races are available.
- */
- public Date getStartDate() {
- Date leaderboardStart = null;
- for (RaceColumnDTO race : getRaceList()) {
- for (FleetDTO fleet: race.getFleets()) {
- Date raceStart = race.getStartDate(fleet);
- if (raceStart != null) {
- if (leaderboardStart == null) {
- leaderboardStart = new Date();
- } else {
- leaderboardStart = leaderboardStart.before(raceStart) ? leaderboardStart : raceStart;
- }
- }
- }
- }
- return leaderboardStart;
- }
-
- /**
- * Takes the {@link PlacemarkOrderDTO} of all races in this leaderboard, if the PlacemarkOrderDTO for the race is
- * available, and fills all {@link PlacemarkDTO} in a new PlacemarkOrderDTO.
- * The order of the races in this leaderboard determine the order of the PlacemarkDTOs in the PlacemarkOrderDTO.
- *
- * @return The places of this leaderboard in form of a {@link PlacemarkOrderDTO}, or null if the
- * {@link PlacemarkOrderDTO places} of no race are available
- */
- public PlacemarkOrderDTO getPlaces() {
- PlacemarkOrderDTO leaderboardPlaces = null;
- for (RaceColumnDTO race : getRaceList()) {
- PlacemarkOrderDTO racePlaces = race.getPlaces();
- if (racePlaces != null) {
- if (leaderboardPlaces == null) {
- leaderboardPlaces = new PlacemarkOrderDTO();
- }
- leaderboardPlaces.getPlacemarks().addAll(racePlaces.getPlacemarks());
- }
- }
- return leaderboardPlaces;
- }
-
- /**
- * @return true if the leaderboard contains a race which is live
- */
- public boolean hasLiveRace(long serverTimePointAsMillis) {
- for (RaceColumnDTO race : getRaceList()) {
- for (FleetDTO fleet : race.getFleets()) {
- if (race.isLive(fleet, serverTimePointAsMillis)) {
- return true;
- }
- }
- }
- return false;
- }
-
- public Listnull regatta name if this DTO represents a RegattaLeaderboard.
+ */
+ public String regattaName;
+ public String displayName;
+ public UUID defaultCourseAreaId;
+ public String defaultCourseAreaName;
+ public ScoringSchemeType scoringScheme;
+ public LeaderboardType type;
+
+ private Long delayToLiveInMillisForLatestRace;
+
+ public AbstractLeaderboardDTO() {
+ races = new ArrayListraceColumnName has at least one competitor who has valid
+ * {@link LeaderboardEntryDTO#legDetails} for that race column, the maximum number of entries of all such competitors in
+ * the leg details is returned, telling the number of legs that the race column shall display. Otherwise, -1 is returned.
+ * If you specify an non null preselectedRace the leg count is calculated only for that race for the column
+ * holding that race. All other columns will be unaffected by preselectedRace and will return their regular
+ * leg count. Note that this may lead to a situation where some competitors may have sailed more legs than others because
+ * their fleet association may have varied across races.competitor scored (and therefore presumably participated) in a medal race represented
+ * in this leaderboard.
+ */
+ public boolean scoredInMedalRace(CompetitorDTO competitor) {
+ LeaderboardRowDTO row = rows.get(competitor);
+ for (RaceColumnDTO race : races) {
+ if (race.isMedalRace() && row.fieldsByRaceColumnName.get(race.getRaceColumnName()).totalPoints > 0) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public Double getNetPoints(CompetitorDTO competitor, String nameOfLastRaceSoFar) {
+ Double netPoints = null;
+ LeaderboardRowDTO row = rows.get(competitor);
+ if (row != null) {
+ LeaderboardEntryDTO field = row.fieldsByRaceColumnName.get(nameOfLastRaceSoFar);
+ if (field != null && field.netPoints != null) {
+ netPoints = field.netPoints;
+ }
+ }
+ return netPoints;
+ }
+
+ public boolean raceIsTracked(String raceColumnName) {
+ for (RaceColumnDTO race : races) {
+ if (race.getRaceColumnName().equals(raceColumnName)) {
+ for (FleetDTO fleet : race.getFleets()) {
+ if (race.isTrackedRace(fleet)) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+
+ }
+
+ public boolean raceIsMedalRace(String raceColumnName) {
+ return getRaceColumnByName(raceColumnName).isMedalRace();
+ }
+
+ /**
+ * If the {@link RaceColumnDTO} by the name raceColumnName doesn't exist yet within this leaderboard
+ * DTO, it is created, setting is {@link RaceColumnDTO#isValidInTotalScore()} to true. This method
+ * ensures that a fleet named fleetName is present. If it's not present yet, it's added to the race
+ * column's fleet name list. The trackedRaceIdentifier and race are associated with the
+ * column for the fleet identified by fleetName.
+ *
+ * @param explicitFactor
+ * factor by which to multiply the race column's points for the overall score; if null, the
+ * default will be determined by whether or not the column is marked as medal race
+ * @param effectiveFactor
+ * is what you get when you call {@link RaceColumn#getFactor()} on the race column that the resulting
+ * {@link RaceColumnDTO} represents
+ * @param regattaName
+ * must not be null if seriesName is not null; specified
+ * separately from trackedRaceIdentifier because a column may belong to regatta/series
+ * despite not having a tracked race associated
+ * @param seriesName
+ * if null, this method will produce a {@link RaceColumnDTO}, otherwise a
+ * {@link RaceColumnInSeriesDTO}
+ * @param fleetDTO
+ * must not be null
+ */
+ public RaceColumnDTO addRace(String raceColumnName, Double explicitFactor, double effectiveFactor,
+ String regattaName, String seriesName, FleetDTO fleetDTO, boolean medalRace,
+ RegattaAndRaceIdentifier trackedRaceIdentifier, RaceDTO race, boolean isMetaLeaderboardColumn) {
+ assert fleetDTO != null;
+ RaceColumnDTO raceColumnDTO = getRaceColumnByName(raceColumnName);
+ if (raceColumnDTO == null) {
+ raceColumnDTO = RaceColumnDTOFactory.INSTANCE.createRaceColumnDTO(raceColumnName, medalRace,
+ explicitFactor, regattaName, seriesName, isMetaLeaderboardColumn);
+ races.add(raceColumnDTO);
+ }
+ raceColumnDTO.setEffectiveFactor(effectiveFactor);
+ boolean contains = false;
+ for (FleetDTO fleet : raceColumnDTO.getFleets()) {
+ if (fleet.getName().equals(fleetDTO.getName())) {
+ contains = true;
+ break;
+ }
+ }
+ if (!contains) {
+ raceColumnDTO.addFleet(fleetDTO);
+ }
+ raceColumnDTO.setRaceIdentifier(fleetDTO, trackedRaceIdentifier);
+ raceColumnDTO.setRace(fleetDTO, race);
+ return raceColumnDTO;
+ }
+
+ public RaceColumnDTO createEmptyRaceColumn(String raceColumnName, boolean medalRace, String regattaName,
+ String seriesName, boolean isMetaLeaderboardColumn) {
+ final RaceColumnDTO raceColumn = RaceColumnDTOFactory.INSTANCE.createRaceColumnDTO(raceColumnName,
+ medalRace, /* explicit factor */ null, regattaName, seriesName, isMetaLeaderboardColumn);
+ races.add(raceColumn);
+ return raceColumn;
+ }
+
+ public RaceColumnDTO getRaceColumnByName(String raceColumnName) {
+ for (RaceColumnDTO race : races) {
+ if (race.getRaceColumnName().equals(raceColumnName)) {
+ return race;
+ }
+ }
+ return null;
+ }
+
+ public int getRaceColumnsCount() {
+ return races.size();
+ }
+
+ public int getRacesCount() {
+ int result = 0;
+ for (RaceColumnDTO race : getRaceList()) {
+ result += race.getFleets().size();
+ }
+ return result;
+ }
+
+ public int getTrackedRacesCount() {
+ int result = 0;
+ for (RaceColumnDTO raceColumn : getRaceList()) {
+ for (FleetDTO fleet : raceColumn.getFleets()) {
+ RaceDTO race = raceColumn.getRace(fleet);
+ if(race != null && race.trackedRace != null && race.trackedRace.hasGPSData && race.trackedRace.hasWindData) {
+ result++;
+ }
+ }
+ }
+ return result;
+ }
+
+ public Listnull if no start dates of the races are available.
+ */
+ public Date getStartDate() {
+ Date leaderboardStart = null;
+ for (RaceColumnDTO race : getRaceList()) {
+ for (FleetDTO fleet: race.getFleets()) {
+ Date raceStart = race.getStartDate(fleet);
+ if (raceStart != null) {
+ if (leaderboardStart == null) {
+ leaderboardStart = new Date();
+ } else {
+ leaderboardStart = leaderboardStart.before(raceStart) ? leaderboardStart : raceStart;
+ }
+ }
+ }
+ }
+ return leaderboardStart;
+ }
+
+ /**
+ * Takes the {@link PlacemarkOrderDTO} of all races in this leaderboard, if the PlacemarkOrderDTO for the race is
+ * available, and fills all {@link PlacemarkDTO} in a new PlacemarkOrderDTO.
+ * The order of the races in this leaderboard determine the order of the PlacemarkDTOs in the PlacemarkOrderDTO.
+ *
+ * @return The places of this leaderboard in form of a {@link PlacemarkOrderDTO}, or null if the
+ * {@link PlacemarkOrderDTO places} of no race are available
+ */
+ public PlacemarkOrderDTO getPlaces() {
+ PlacemarkOrderDTO leaderboardPlaces = null;
+ for (RaceColumnDTO race : getRaceList()) {
+ PlacemarkOrderDTO racePlaces = race.getPlaces();
+ if (racePlaces != null) {
+ if (leaderboardPlaces == null) {
+ leaderboardPlaces = new PlacemarkOrderDTO();
+ }
+ leaderboardPlaces.getPlacemarks().addAll(racePlaces.getPlacemarks());
+ }
+ }
+ return leaderboardPlaces;
+ }
+
+ /**
+ * @return true if the leaderboard contains a race which is live
+ */
+ public boolean hasLiveRace(long serverTimePointAsMillis) {
+ for (RaceColumnDTO race : getRaceList()) {
+ for (FleetDTO fleet : race.getFleets()) {
+ if (race.isLive(fleet, serverTimePointAsMillis)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public Listtrue if serverTimePointAsMillis is between (inclusively) the start and end time
+ * point of the "live" interval as defined above.
+ */
+ public boolean isLive(long serverTimePointAsMillis) {
+ final Date startOfLivePeriod;
+ final Date endOfLivePeriod;
+ if (trackedRace == null || !trackedRace.hasGPSData || !trackedRace.hasWindData) {
+ startOfLivePeriod = null;
+ endOfLivePeriod = null;
+ } else {
+ if (startOfRace == null) {
+ startOfLivePeriod = trackedRace.startOfTracking;
+ } else {
+ startOfLivePeriod = new Date(startOfRace.getTime() - TimingConstants.PRE_START_PHASE_DURATION_IN_MILLIS);
+ }
+ if (endOfRace == null) {
+ if (trackedRace.timePointOfNewestEvent != null) {
+ endOfLivePeriod = new Date(trackedRace.timePointOfNewestEvent.getTime()
+ + TimingConstants.IS_LIVE_GRACE_PERIOD_IN_MILLIS);
+ } else {
+ endOfLivePeriod = null;
+ }
+ } else {
+ endOfLivePeriod = new Date(endOfRace.getTime() + TimingConstants.IS_LIVE_GRACE_PERIOD_IN_MILLIS);
+ }
+ }
+
+ // if an empty timepoint is given then take the start of the race
+ if (serverTimePointAsMillis == 0) {
+ serverTimePointAsMillis = startOfLivePeriod.getTime()+1;
+ }
+
+ // whenLastTrackedRaceWasLive is null if there is no tracked race for fleet, or the tracked race hasn't started yet at the server time
+ // when this DTO was assembled, or there were no GPS or wind data
+ final boolean result =
+ startOfLivePeriod != null &&
+ endOfLivePeriod != null &&
+ startOfLivePeriod.getTime() <= serverTimePointAsMillis &&
+ serverTimePointAsMillis <= endOfLivePeriod.getTime();
+ return result;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = super.hashCode();
+ result = prime * result + ((endOfRace == null) ? 0 : endOfRace.hashCode());
+ result = prime * result + ((startOfRace == null) ? 0 : startOfRace.hashCode());
+ result = prime * result + ((trackedRace == null) ? 0 : trackedRace.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (!super.equals(obj))
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ BasicRaceDTO other = (BasicRaceDTO) obj;
+ if (endOfRace == null) {
+ if (other.endOfRace != null)
+ return false;
+ } else if (!endOfRace.equals(other.endOfRace))
+ return false;
+ if (startOfRace == null) {
+ if (other.startOfRace != null)
+ return false;
+ } else if (!startOfRace.equals(other.startOfRace))
+ return false;
+ if (trackedRace == null) {
+ if (other.trackedRace != null)
+ return false;
+ } else if (!trackedRace.equals(other.trackedRace))
+ return false;
+ return true;
+ }
+}
diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/LeaderboardDTO.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/LeaderboardDTO.java
index 64012fec26c..dd9b5e94981 100644
--- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/LeaderboardDTO.java
+++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/LeaderboardDTO.java
@@ -62,7 +62,6 @@ public class LeaderboardDTO extends AbstractLeaderboardDTO implements Serializab
/**
* @param uuidGenerator used to provide the {@link #id ID} for this object (see also {@link #getId()}) and for any clones produced
* from it by the {@link #clone()} operation.
- * @param hasOverallDetails TODO
*/
public LeaderboardDTO(Date timePointOfLastCorrectionsValidity, String comment, ScoringSchemeType scoringScheme, boolean higherScoreIsBetter, UUIDGenerator uuidGenerator, boolean hasOverallDetails) {
initCollections();
@@ -79,7 +78,7 @@ public class LeaderboardDTO extends AbstractLeaderboardDTO implements Serializab
this.suppressedCompetitors = new ArrayListnull if seriesName is not null
* @param seriesName
- * when null, a regular {@link RaceColumnDTO} will be produced and initialized from the
- * properties passed; otherwise, a {@link RaceColumnInSeriesDTO} will be created.
+ * when null, a regular {@link RaceColumnDTO} or {@link MetaLeaderboardRaceColumnDTO} will
+ * be produced (depending on isMetaLeaderboardColumn) and initialized from the properties
+ * passed; otherwise, a {@link RaceColumnInSeriesDTO} will be created.
*/
- public RaceColumnDTO createRaceColumnDTO(String columnName, boolean isMedal, Double explicitFactor, String regattaName, String seriesName) {
+ public RaceColumnDTO createRaceColumnDTO(String columnName, boolean isMedal, Double explicitFactor,
+ String regattaName, String seriesName, boolean isMetaLeaderboardColumn) {
final RaceColumnDTO raceColumnDTO;
if (seriesName != null) {
raceColumnDTO = new RaceColumnInSeriesDTO(seriesName, regattaName);
} else {
- raceColumnDTO = new RaceColumnDTO();
+ raceColumnDTO = isMetaLeaderboardColumn ? new MetaLeaderboardRaceColumnDTO() : new RaceColumnDTO();
}
fillRaceColumnDTO(raceColumnDTO, columnName, isMedal, explicitFactor);
return raceColumnDTO;
diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/RaceDTO.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/RaceDTO.java
index eba9caf39b7..6a8e90c617f 100755
--- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/RaceDTO.java
+++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/RaceDTO.java
@@ -1,10 +1,7 @@
package com.sap.sailing.domain.common.dto;
-import java.util.Date;
-
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
import com.sap.sailing.domain.common.RegattaNameAndRaceName;
-import com.sap.sailing.domain.common.TimingConstants;
/**
* Master data about a single race that is to be transferred to the client.true if serverTimePointAsMillis is between (inclusively) the start and end time
- * point of the "live" interval as defined above.
- */
- public boolean isLive(long serverTimePointAsMillis) {
- final Date startOfLivePeriod;
- final Date endOfLivePeriod;
- if (trackedRace == null || !trackedRace.hasGPSData || !trackedRace.hasWindData) {
- startOfLivePeriod = null;
- endOfLivePeriod = null;
- } else {
- if (startOfRace == null) {
- startOfLivePeriod = trackedRace.startOfTracking;
- } else {
- startOfLivePeriod = new Date(startOfRace.getTime() - TimingConstants.PRE_START_PHASE_DURATION_IN_MILLIS);
- }
- if (endOfRace == null) {
- if (trackedRace.timePointOfNewestEvent != null) {
- endOfLivePeriod = new Date(trackedRace.timePointOfNewestEvent.getTime()
- + TimingConstants.IS_LIVE_GRACE_PERIOD_IN_MILLIS);
- } else {
- endOfLivePeriod = null;
- }
- } else {
- endOfLivePeriod = new Date(endOfRace.getTime() + TimingConstants.IS_LIVE_GRACE_PERIOD_IN_MILLIS);
- }
- }
-
- // if an empty timepoint is given then take the start of the race
- if (serverTimePointAsMillis == 0) {
- serverTimePointAsMillis = startOfLivePeriod.getTime()+1;
- }
-
- // whenLastTrackedRaceWasLive is null if there is no tracked race for fleet, or the tracked race hasn't started yet at the server time
- // when this DTO was assembled, or there were no GPS or wind data
- final boolean result =
- startOfLivePeriod != null &&
- endOfLivePeriod != null &&
- startOfLivePeriod.getTime() <= serverTimePointAsMillis &&
- serverTimePointAsMillis <= endOfLivePeriod.getTime();
- return result;
- }
-
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + ((boatClass == null) ? 0 : boatClass.hashCode());
- result = prime * result + ((endOfRace == null) ? 0 : endOfRace.hashCode());
result = prime * result + (isTracked ? 1231 : 1237);
result = prime * result + ((places == null) ? 0 : places.hashCode());
result = prime * result + ((regattaName == null) ? 0 : regattaName.hashCode());
- result = prime * result + ((startOfRace == null) ? 0 : startOfRace.hashCode());
result = prime * result + ((status == null) ? 0 : status.hashCode());
- result = prime * result + ((trackedRace == null) ? 0 : trackedRace.hashCode());
return result;
}
@@ -146,11 +90,6 @@ public class RaceDTO extends NamedDTO {
return false;
} else if (!boatClass.equals(other.boatClass))
return false;
- if (endOfRace == null) {
- if (other.endOfRace != null)
- return false;
- } else if (!endOfRace.equals(other.endOfRace))
- return false;
if (isTracked != other.isTracked)
return false;
if (places == null) {
@@ -163,21 +102,11 @@ public class RaceDTO extends NamedDTO {
return false;
} else if (!regattaName.equals(other.regattaName))
return false;
- if (startOfRace == null) {
- if (other.startOfRace != null)
- return false;
- } else if (!startOfRace.equals(other.startOfRace))
- return false;
if (status == null) {
if (other.status != null)
return false;
} else if (!status.equals(other.status))
return false;
- if (trackedRace == null) {
- if (other.trackedRace != null)
- return false;
- } else if (!trackedRace.equals(other.trackedRace))
- return false;
return true;
}
}
diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/quadtree/QuadTree.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/quadtree/QuadTree.java
index 51f42e5f100..798f3ffbe55 100755
--- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/quadtree/QuadTree.java
+++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/quadtree/QuadTree.java
@@ -1,164 +1,90 @@
-// **********************************************************************
-//
-//