Merge branch 'bug2822_boatcolors'

This commit is contained in:
Axel Uhl committed 2018-03-30 16:09:13 +02:00
commit c2afc5fc5c
521 files changed
+13919 -4192

No files matched your search

@@ -1,9 +1,13 @@
package com.sap.sailing.dashboards.gwt.client.actions;
import java.util.List;
import com.google.gwt.core.shared.GwtIncompatible;
import com.sap.sailing.dashboards.gwt.shared.dispatch.DashboardAction;
import com.sap.sailing.dashboards.gwt.shared.dispatch.DashboardDispatchContext;
import com.sap.sailing.dashboards.gwt.shared.dto.LeaderboardCompetitorsDTO;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.common.dto.CompetitorDTO;
import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sse.common.impl.MillisecondsTimePoint;
import com.sap.sse.gwt.dispatch.shared.exceptions.DispatchException;
@@ -29,7 +33,9 @@ public class GetCompetitorInLeaderboardAction implements DashboardAction<Leaderb
public LeaderboardCompetitorsDTO execute(DashboardDispatchContext dashboardDispatchContext) throws DispatchException {
LeaderboardCompetitorsDTO result = new LeaderboardCompetitorsDTO();
Leaderboard lb = dashboardDispatchContext.getRacingEventService().getLeaderboardByName(this.leaderboarName);
result.setCompetitors(dashboardDispatchContext.getRacingEventService().getBaseDomainFactory().getCompetitorDTOList(lb.getCompetitorsFromBestToWorst(MillisecondsTimePoint.now())));
List<Competitor> competitorsFromBestToWorst = lb.getCompetitorsFromBestToWorst(MillisecondsTimePoint.now());
List<CompetitorDTO> competitorDTOs = dashboardDispatchContext.getRacingEventService().getBaseDomainFactory().getCompetitorDTOList(competitorsFromBestToWorst);
result.setCompetitors(competitorDTOs);
return result;
}
}
@@ -26,7 +26,7 @@ import com.sap.sailing.dashboards.gwt.shared.dto.StartAnalysisDTO;
import com.sap.sailing.domain.common.racelog.RacingProcedureType;
import com.sap.sailing.gwt.ui.client.CompetitorColorProvider;
import com.sap.sailing.gwt.ui.client.CompetitorColorProviderImpl;
import com.sap.sailing.gwt.ui.client.CompetitorSelectionModel;
import com.sap.sailing.gwt.ui.client.RaceCompetitorSelectionModel;
import com.sap.sailing.gwt.ui.client.RaceTimesInfoProvider;
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
import com.sap.sailing.gwt.ui.client.StringMessages;
@@ -74,7 +74,7 @@ public class StartAnalysisCard extends Composite implements HasWidgets, StartAna
private SailingServiceAsync sailingServiceAsync;
private StringMessages stringMessages;
private final CompetitorSelectionModel competitorSelectionModel;
private final RaceCompetitorSelectionModel competitorSelectionModel;
private final CompetitorColorProvider colorProvider;
private final double WIND_LINE_ADVANTAGE_DIV_WIDTH_IN_PT = 185;
@@ -88,7 +88,7 @@ public class StartAnalysisCard extends Composite implements HasWidgets, StartAna
this.sailingServiceAsync = sailingServiceAsync;
this.raceMapResources = raceMapResources;
colorProvider = new CompetitorColorProviderImpl();
competitorSelectionModel = new CompetitorSelectionModel(/* hasMultiSelection */true, colorProvider);
competitorSelectionModel = new RaceCompetitorSelectionModel(/* hasMultiSelection */true, colorProvider);
competitorSelectionModel.setCompetitors(startAnalysisDTO.getCompetitorDTOsFromStartAnaylsisCompetitorDTOs(),
raceMap);
initWidget(uiBinder.createAndBindUi(this));
@@ -15,6 +15,7 @@ import com.sap.sailing.dashboards.gwt.shared.dto.StartAnalysisDTO;
import com.sap.sailing.dashboards.gwt.shared.dto.StartAnalysisRankingTableEntryDTO;
import com.sap.sailing.dashboards.gwt.shared.dto.StartLineAdvantageDTO;
import com.sap.sailing.dashboards.gwt.shared.dto.WindAndAdvantagesInfoForStartLineDTO;
import com.sap.sailing.domain.base.Boat;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Waypoint;
import com.sap.sailing.domain.common.LegType;
@@ -59,7 +60,8 @@ public final class StartAnalysisDTOFactory extends AbstractStartAnalysisCreation
competitors.add(createStartAnalysisCompetitorDTO(dashboardDispatchContext, trackedRace, rankOfCompetitorWhilePassingSecondWaypoint, competitor));
}
if (competitor != null) {
startAnalysisDTO.competitor = dashboardDispatchContext.getRacingEventService().getBaseDomainFactory().getCompetitorStore().convertToCompetitorDTO(competitor);
Boat boatOfCompetitor = trackedRace.getBoatOfCompetitor(competitor);
startAnalysisDTO.competitor = dashboardDispatchContext.getRacingEventService().getBaseDomainFactory().getCompetitorStore().convertToCompetitorWithBoatDTO(competitor, boatOfCompetitor);
logger.log(Level.INFO, "Created startanalysis for competitor"+competitor);
}
startAnalysisDTO.startAnalysisCompetitorDTOs = competitors;
@@ -191,8 +193,9 @@ public final class StartAnalysisDTOFactory extends AbstractStartAnalysisCreation
private static StartAnalysisCompetitorDTO createStartAnalysisCompetitorDTO(DashboardDispatchContext dashboardDispatchContext, TrackedRace trackedRace, int rank, Competitor competitor) {
StartAnalysisCompetitorDTO startAnalysisCompetitorDTOsForRace = new StartAnalysisCompetitorDTO();
Boat boatOfCompetitor = trackedRace.getBoatOfCompetitor(competitor);
startAnalysisCompetitorDTOsForRace.competitorDTO = dashboardDispatchContext.getRacingEventService().getBaseDomainFactory().getCompetitorStore()
.convertToCompetitorDTO(competitor);
.convertToCompetitorWithBoatDTO(competitor, boatOfCompetitor);
startAnalysisCompetitorDTOsForRace.rankingTableEntryDTO = createRankTableEntry(trackedRace, rank, competitor);
return startAnalysisCompetitorDTOsForRace;
}
@@ -2,6 +2,7 @@ package com.sap.sailing.domain.common;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import com.sap.sailing.domain.common.dto.PersonDTO;
import com.sap.sse.common.CountryCode;
@@ -14,6 +15,9 @@ import com.sap.sse.common.impl.NamedImpl;
public class CompetitorDescriptor extends NamedImpl implements Named {
private static final long serialVersionUID = -6645726854552711646L;
private final UUID competitorUUID;
private final UUID boatUUID;
private final String shortName;
private final String eventName;
private final String regattaName;
private final String boatClassName;
@@ -27,23 +31,27 @@ public class CompetitorDescriptor extends NamedImpl implements Named {
private final Double timeOnTimeFactor;
private final Duration timeOnDistanceAllowancePerNauticalMile;
public CompetitorDescriptor(String eventName, String regattaName, String boatClassName, String raceName,
String fleetName, String sailNumber, String name, String teamName, String boatName, CountryCode countryCode,
Iterable<PersonDTO> persons, Double timeOnTimeFactor, Duration timeOnDistanceAllowancePerNauticalMile) {
super(name);
public CompetitorDescriptor(String eventName, String regattaName, String raceName, String fleetName,
UUID competitorUUID, String fullName, String shortName, String teamName, Iterable<PersonDTO> persons,
CountryCode countryCode, Double timeOnTimeFactor,
Duration timeOnDistanceAllowancePerNauticalMile, UUID boatUUID, String boatName, String boatClassName, String sailNumber) {
super(fullName);
this.shortName = shortName;
this.eventName = eventName;
this.regattaName = regattaName;
this.boatClassName = boatClassName;
this.boatName = boatName;
this.raceName = raceName;
this.fleetName = fleetName;
this.sailNumber = sailNumber;
this.competitorUUID = competitorUUID;
this.teamName = teamName;
this.countryCode = countryCode;
this.persons = new ArrayList<>();
Util.addAll(persons, this.persons);
this.timeOnTimeFactor = timeOnTimeFactor;
this.timeOnDistanceAllowancePerNauticalMile = timeOnDistanceAllowancePerNauticalMile;
this.boatUUID = boatUUID;
this.boatName = boatName;
this.boatClassName = boatClassName;
this.sailNumber = sailNumber;
}
public String getEventName() {
@@ -90,10 +98,22 @@ public class CompetitorDescriptor extends NamedImpl implements Named {
return teamName;
}
public String getShortName() {
return shortName;
}
public Duration getTimeOnDistanceAllowancePerNauticalMile() {
return timeOnDistanceAllowancePerNauticalMile;
}
public UUID getCompetitorUUID() {
return competitorUUID;
}
public UUID getBoatUUID() {
return boatUUID;
}
@Override
public String toString() {
return "CompetitorDescriptor [eventName=" + eventName + ", regattaName=" + regattaName + ", boatClassName="
@@ -37,6 +37,7 @@ public abstract class AbstractLeaderboardDTO implements Serializable {
public String defaultCourseAreaName;
public ScoringSchemeType scoringScheme;
public LeaderboardType type;
public boolean canBoatsOfCompetitorsChangePerRace;
private Long delayToLiveInMillisForLatestRace;
@@ -2,23 +2,28 @@ package com.sap.sailing.domain.common.dto;
import java.io.Serializable;
import com.sap.sailing.domain.common.racelog.tracking.MappableToDevice;
import com.sap.sse.common.Color;
public class BoatDTO extends NamedDTO implements Serializable {
public class BoatDTO extends NamedDTO implements Serializable, MappableToDevice {
private static final long serialVersionUID = -4076992788294272162L;
private String idAsString;
private BoatClassDTO boatClass;
private String sailId;
private Color color;
// for GWT
BoatDTO() {}
public BoatDTO() {}
public BoatDTO(String name, String sailId) {
this(name, sailId, null);
public BoatDTO(String idAsString, String name, BoatClassDTO boatClass, String sailId) {
this(idAsString, name, boatClass, sailId, null);
}
public BoatDTO(String name, String sailId, Color color) {
public BoatDTO(String idAsString, String name, BoatClassDTO boatClass, String sailId, Color color) {
super(name);
this.idAsString = idAsString;
this.boatClass = boatClass;
this.sailId = sailId;
this.color = color;
}
@@ -31,10 +36,37 @@ public class BoatDTO extends NamedDTO implements Serializable {
return sailId;
}
@Override
public String getIdAsString() {
return idAsString;
}
public BoatClassDTO getBoatClass() {
return boatClass;
}
public String getDisplayName() {
final StringBuilder sb = new StringBuilder();
final boolean hasSailId = sailId != null && !sailId.isEmpty();
if (getName() != null) {
sb.append(getName());
if (hasSailId) {
sb.append(" (").append(sailId).append(')');
}
} else if (hasSailId) {
sb.append(sailId);
} else {
sb.append(idAsString);
}
return sb.toString();
}
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + ((boatClass == null) ? 0 : boatClass.hashCode());
result = prime * result + ((idAsString == null) ? 0 : idAsString.hashCode());
result = prime * result + ((color == null) ? 0 : color.hashCode());
result = prime * result + ((sailId == null) ? 0 : sailId.hashCode());
return result;
@@ -49,6 +81,16 @@ public class BoatDTO extends NamedDTO implements Serializable {
if (getClass() != obj.getClass())
return false;
BoatDTO other = (BoatDTO) obj;
if (boatClass == null) {
if (other.boatClass != null)
return false;
} else if (!boatClass.equals(other.boatClass))
return false;
if (idAsString == null) {
if (other.idAsString != null)
return false;
} else if (!idAsString.equals(other.idAsString))
return false;
if (color == null) {
if (other.color != null)
return false;
@@ -61,4 +103,9 @@ public class BoatDTO extends NamedDTO implements Serializable {
return false;
return true;
}
@Override
public String toString() {
return getName() == null ? (getBoatClass().getName() + " / " + getSailId()) : getName();
}
}
@@ -1,55 +1,36 @@
package com.sap.sailing.domain.common.dto;
import java.io.Serializable;
import com.sap.sailing.domain.common.racelog.tracking.MappableToDevice;
import com.sap.sse.common.Color;
import com.sap.sse.common.Duration;
public interface CompetitorDTO extends Serializable, MappableToDevice {
String getTwoLetterIsoCountryCode();
String getThreeLetterIocCountryCode();
String getCountryName();
String getIdAsString();
/**
* Equality and hash code are based on the {@link #getIdAsString() ID}, the {@link #getSailID() sail number}, the
* {@link #getBoatClass() boat class} (whose equality and hash code, in turn, depends on its name) and the
* {@link #getThreeLetterIocCountryCode() IOC country code}. Note that the three latter properties are subject
* to change for a competitor while the ID remains unchanged.
*
* @author Axel Uhl (d043530)
*
*/
public interface CompetitorDTO extends CompetitorWithoutBoatDTO {
// TODO bug2822: Should we remove this?
String getSailID();
String getSearchTag();
/**
* If the {@code searchTag} is not {@link String#contains(CharSequence) contained} in {@link #getSearchTag()},
* appends it to the search tag, separated by a space character
* Returns a derived short information about a competitor depending on the information available
* If we have a short name set on the competitor this name will be returned.
* If no short name exist but a boat the either the sailId or the boat name will returned.
* If all these attributes have no value null is returned.
*/
void addToSearchTag(String searchTag);
String getShortInfo();
BoatDTO getBoat();
void setBoat(BoatDTO boat);
// TODO bug2822: Should we remove this?
BoatClassDTO getBoatClass();
String getName();
Color getColor();
String getEmail();
boolean hasEmail();
/**
* A regular instance will simply return this object. A compacted version may compute the result by looking it up
* from the previous version of the enclosing leaderboard.
*/
CompetitorDTO getCompetitorFromPrevious(LeaderboardDTO previousVersion);
String getFlagImageURL();
String getImageURL();
Double getTimeOnTimeFactor();
Duration getTimeOnDistanceAllowancePerNauticalMile();
}
@@ -5,67 +5,36 @@ import java.io.Serializable;
import com.sap.sse.common.Color;
import com.sap.sse.common.Duration;
/**
* Equality and hash code are based on the {@link #getIdAsString() ID}, the {@link #getSailID() sail number}, the
* {@link #getBoatClass() boat class} (whose equality and hash code, in turn, depends on its name) and the
* {@link #getThreeLetterIocCountryCode() IOC country code}. Note that the three latter properties are subject
* to change for a competitor while the ID remains unchanged.
*
* @author Axel Uhl (d043530)
*
*/
public class CompetitorDTOImpl extends NamedDTO implements CompetitorDTO, Serializable {
public class CompetitorDTOImpl extends CompetitorWithoutBoatDTOImpl implements CompetitorDTO, Serializable {
private static final long serialVersionUID = -4997852354821083154L;
private String countryName;
private String twoLetterIsoCountryCode;
private String threeLetterIocCountryCode;
private Color color;
private String email;
private String searchTag;
private String idAsString;
private BoatClassDTO boatClass;
private BoatDTO boat;
private String imageURL;
private String flagImageURL;
private Double timeOnTimeFactor;
private Duration timeOnDistanceAllowancePerNauticalMile;
public CompetitorDTOImpl() {}
public CompetitorDTOImpl() {
boat = new BoatDTO();
}
public CompetitorDTOImpl(String name, Color color, String email, String twoLetterIsoCountryCode, String threeLetterIocCountryCode,
public CompetitorDTOImpl(String name, String shortName, Color color, String email, String twoLetterIsoCountryCode, String threeLetterIocCountryCode,
String countryName, String idAsString, String imageURL, String flagImageURL,
BoatDTO boat, BoatClassDTO boatClass, Double timeOnTimeFactor, Duration timeOnDistanceAllowancePerNauticalMile, String searchTag) {
super(name);
this.color = color;
this.email = email;
this.twoLetterIsoCountryCode = twoLetterIsoCountryCode;
this.threeLetterIocCountryCode = threeLetterIocCountryCode;
this.countryName = countryName;
this.idAsString = idAsString;
this.imageURL = imageURL;
this.flagImageURL = flagImageURL;
Double timeOnTimeFactor, Duration timeOnDistanceAllowancePerNauticalMile, String searchTag, BoatDTO boat) {
super(name, shortName, color, email, twoLetterIsoCountryCode, threeLetterIocCountryCode,
countryName, idAsString, imageURL, flagImageURL,
timeOnTimeFactor, timeOnDistanceAllowancePerNauticalMile, searchTag);
this.boat = boat;
}
public CompetitorDTOImpl(CompetitorWithoutBoatDTO c, BoatDTO boat) {
super(c.getName(), c.getShortName(), c.getColor(), c.getEmail(),
c.getTwoLetterIsoCountryCode(), c.getThreeLetterIocCountryCode(), c.getCountryName(), c.getIdAsString(),
c.getImageURL(), c.getFlagImageURL(), c.getTimeOnTimeFactor(),
c.getTimeOnDistanceAllowancePerNauticalMile(), c.getSearchTag());
this.boat = boat;
this.boatClass = boatClass;
this.timeOnTimeFactor = timeOnTimeFactor;
this.timeOnDistanceAllowancePerNauticalMile = timeOnDistanceAllowancePerNauticalMile;
this.searchTag = searchTag;
}
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + ((boatClass == null) ? 0 : boatClass.hashCode());
result = prime * result + ((boat == null) ? 0 : boat.hashCode());
result = prime * result + ((idAsString == null) ? 0 : idAsString.hashCode());
result = prime * result + ((color == null) ? 0 : color.hashCode());
result = prime * result + ((email == null) ? 0 : email.hashCode());
result = prime * result + ((threeLetterIocCountryCode == null) ? 0 : threeLetterIocCountryCode.hashCode());
result = prime * result + ((imageURL == null) ? 0 : imageURL.hashCode());
result = prime * result + ((flagImageURL == null) ? 0 : flagImageURL.hashCode());
result = prime * result + ((timeOnTimeFactor == null) ? 0 : timeOnTimeFactor.hashCode());
result = prime * result + ((timeOnDistanceAllowancePerNauticalMile == null) ? 0 : timeOnDistanceAllowancePerNauticalMile.hashCode());
result = prime * result + ((searchTag == null) ? 0 : searchTag.hashCode());
return result;
}
@@ -78,150 +47,47 @@ public class CompetitorDTOImpl extends NamedDTO implements CompetitorDTO, Serial
if (getClass() != obj.getClass())
return false;
CompetitorDTOImpl other = (CompetitorDTOImpl) obj;
if (boatClass == null) {
if (other.boatClass != null)
return false;
} else if (!boatClass.equals(other.boatClass))
return false;
if (boat == null) {
if (other.boat != null)
return false;
} else if (!boat.equals(other.boat))
return false;
if (idAsString == null) {
if (other.idAsString != null)
return false;
} else if (!idAsString.equals(other.idAsString))
return false;
if (threeLetterIocCountryCode == null) {
if (other.threeLetterIocCountryCode != null)
return false;
} else if (!threeLetterIocCountryCode.equals(other.threeLetterIocCountryCode))
return false;
if (color == null) {
if (other.color != null)
return false;
} else if (!color.equals(other.color))
return false;
if (email == null) {
if (other.email != null)
return false;
} else if (!email.equals(other.email))
return false;
if (imageURL == null) {
if (other.imageURL != null)
return false;
} else if (!imageURL.equals(other.imageURL))
return false;
if (flagImageURL == null) {
if (other.flagImageURL != null)
return false;
} else if (!flagImageURL.equals(other.flagImageURL))
return false;
if (timeOnTimeFactor == null) {
if (other.timeOnTimeFactor != null)
return false;
} else if (!timeOnTimeFactor.equals(other.timeOnTimeFactor))
return false;
if (timeOnDistanceAllowancePerNauticalMile == null) {
if (other.timeOnDistanceAllowancePerNauticalMile != null)
return false;
} else if (!timeOnDistanceAllowancePerNauticalMile.equals(other.timeOnDistanceAllowancePerNauticalMile))
return false;
if (searchTag == null) {
if (other.searchTag != null)
return false;
} else if (!searchTag.equals(other.searchTag))
return false;
return true;
}
@Override
public String getTwoLetterIsoCountryCode() {
return twoLetterIsoCountryCode;
}
@Override
public String getThreeLetterIocCountryCode() {
return threeLetterIocCountryCode;
}
@Override
public String getCountryName() {
return countryName;
}
@Override
public String getSailID() {
return boat==null?null:boat.getSailId();
}
@Override
public String getImageURL() {
return imageURL;
}
@Override
public String getFlagImageURL() {
return flagImageURL;
}
@Override
public String getIdAsString() {
return idAsString;
}
@Override
public BoatClassDTO getBoatClass() {
return boatClass;
}
@Override
public CompetitorDTO getCompetitorFromPrevious(LeaderboardDTO previousVersion) {
return this;
}
@Override
public Color getColor() {
return color;
}
@Override
public String getSearchTag() {
return searchTag;
}
@Override
public void addToSearchTag(String searchTag) {
if (this.searchTag == null) {
this.searchTag = searchTag;
} else if (!this.searchTag.contains(searchTag)) {
this.searchTag += " "+searchTag;
public String getShortInfo() {
String result = null;
if (getShortName() != null) {
result = getShortName();
} else if (getBoat() != null) {
result = getBoat().getSailId() != null ? getBoat().getSailId() : getBoat().getName();
}
return result;
}
@Override
public String getEmail() {
return email;
public String getSailID() {
return boat == null ? null : boat.getSailId();
}
@Override
public boolean hasEmail() {
return email != null && !email.isEmpty();
public BoatClassDTO getBoatClass() {
return boat == null ? null : boat.getBoatClass();
}
@Override
public Double getTimeOnTimeFactor() {
return timeOnTimeFactor;
}
@Override
public Duration getTimeOnDistanceAllowancePerNauticalMile() {
return timeOnDistanceAllowancePerNauticalMile;
}
public void setBoat(BoatDTO boat) {
this.boat = boat;
}
@Override
public BoatDTO getBoat() {
return boat;
}
}
}
@@ -0,0 +1,47 @@
package com.sap.sailing.domain.common.dto;
import java.io.Serializable;
import com.sap.sailing.domain.common.racelog.tracking.MappableToDevice;
import com.sap.sse.common.Color;
import com.sap.sse.common.Duration;
/**
* Equality and hash code are based on the {@link #getIdAsString() ID} and all contained attributes like name, shortName, email, etc.
*/
public interface CompetitorWithoutBoatDTO extends Serializable, MappableToDevice {
String getTwoLetterIsoCountryCode();
String getThreeLetterIocCountryCode();
String getCountryName();
String getIdAsString();
String getSearchTag();
/**
* If the {@code searchTag} is not contained in {@link #getSearchTag()}, appends it to the search tag, separated by a space character
*/
void addToSearchTag(String searchTag);
String getName();
String getShortName();
Color getColor();
String getEmail();
boolean hasEmail();
String getFlagImageURL();
String getImageURL();
Double getTimeOnTimeFactor();
Duration getTimeOnDistanceAllowancePerNauticalMile();
}
@@ -0,0 +1,204 @@
package com.sap.sailing.domain.common.dto;
import java.io.Serializable;
import com.sap.sse.common.Color;
import com.sap.sse.common.Duration;
/**
* Equality and hash code are based on the {@link #getIdAsString() ID}, the {@link #getSailID() sail number}, the
* {@link #getBoatClass() boat class} (whose equality and hash code, in turn, depends on its name) and the
* {@link #getThreeLetterIocCountryCode() IOC country code}. Note that the three latter properties are subject
* to change for a competitor while the ID remains unchanged.
*
* @author Axel Uhl (d043530)
*
*/
public class CompetitorWithoutBoatDTOImpl extends NamedDTO implements CompetitorWithoutBoatDTO, Serializable {
private static final long serialVersionUID = 3019126418065082622L;
private String countryName;
private String twoLetterIsoCountryCode;
private String threeLetterIocCountryCode;
private Color color;
private String shortName;
private String email;
private String searchTag;
private String idAsString;
private String imageURL;
private String flagImageURL;
private Double timeOnTimeFactor;
private Duration timeOnDistanceAllowancePerNauticalMile;
public CompetitorWithoutBoatDTOImpl() {}
public CompetitorWithoutBoatDTOImpl(String name, String shortName, Color color, String email, String twoLetterIsoCountryCode, String threeLetterIocCountryCode,
String countryName, String idAsString, String imageURL, String flagImageURL,
Double timeOnTimeFactor, Duration timeOnDistanceAllowancePerNauticalMile, String searchTag) {
super(name);
this.shortName = shortName;
this.color = color;
this.email = email;
this.twoLetterIsoCountryCode = twoLetterIsoCountryCode;
this.threeLetterIocCountryCode = threeLetterIocCountryCode;
this.countryName = countryName;
this.idAsString = idAsString;
this.imageURL = imageURL;
this.flagImageURL = flagImageURL;
this.timeOnTimeFactor = timeOnTimeFactor;
this.timeOnDistanceAllowancePerNauticalMile = timeOnDistanceAllowancePerNauticalMile;
this.searchTag = searchTag;
}
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + ((idAsString == null) ? 0 : idAsString.hashCode());
result = prime * result + ((color == null) ? 0 : color.hashCode());
result = prime * result + ((email == null) ? 0 : email.hashCode());
result = prime * result + ((shortName == null) ? 0 : shortName.hashCode());
result = prime * result + ((threeLetterIocCountryCode == null) ? 0 : threeLetterIocCountryCode.hashCode());
result = prime * result + ((imageURL == null) ? 0 : imageURL.hashCode());
result = prime * result + ((flagImageURL == null) ? 0 : flagImageURL.hashCode());
result = prime * result + ((timeOnTimeFactor == null) ? 0 : timeOnTimeFactor.hashCode());
result = prime * result + ((timeOnDistanceAllowancePerNauticalMile == null) ? 0 : timeOnDistanceAllowancePerNauticalMile.hashCode());
result = prime * result + ((searchTag == null) ? 0 : searchTag.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;
CompetitorWithoutBoatDTOImpl other = (CompetitorWithoutBoatDTOImpl) obj;
if (idAsString == null) {
if (other.idAsString != null)
return false;
} else if (!idAsString.equals(other.idAsString))
return false;
if (threeLetterIocCountryCode == null) {
if (other.threeLetterIocCountryCode != null)
return false;
} else if (!threeLetterIocCountryCode.equals(other.threeLetterIocCountryCode))
return false;
if (color == null) {
if (other.color != null)
return false;
} else if (!color.equals(other.color))
return false;
if (email == null) {
if (other.email != null)
return false;
} else if (!email.equals(other.email))
return false;
if (shortName == null) {
if (other.shortName != null)
return false;
} else if (!shortName.equals(other.shortName))
return false;
if (imageURL == null) {
if (other.imageURL != null)
return false;
} else if (!imageURL.equals(other.imageURL))
return false;
if (flagImageURL == null) {
if (other.flagImageURL != null)
return false;
} else if (!flagImageURL.equals(other.flagImageURL))
return false;
if (timeOnTimeFactor == null) {
if (other.timeOnTimeFactor != null)
return false;
} else if (!timeOnTimeFactor.equals(other.timeOnTimeFactor))
return false;
if (timeOnDistanceAllowancePerNauticalMile == null) {
if (other.timeOnDistanceAllowancePerNauticalMile != null)
return false;
} else if (!timeOnDistanceAllowancePerNauticalMile.equals(other.timeOnDistanceAllowancePerNauticalMile))
return false;
if (searchTag == null) {
if (other.searchTag != null)
return false;
} else if (!searchTag.equals(other.searchTag))
return false;
return true;
}
@Override
public String getTwoLetterIsoCountryCode() {
return twoLetterIsoCountryCode;
}
@Override
public String getThreeLetterIocCountryCode() {
return threeLetterIocCountryCode;
}
@Override
public String getCountryName() {
return countryName;
}
@Override
public String getShortName() {
return shortName;
}
@Override
public String getImageURL() {
return imageURL;
}
@Override
public String getFlagImageURL() {
return flagImageURL;
}
@Override
public String getIdAsString() {
return idAsString;
}
@Override
public Color getColor() {
return color;
}
@Override
public String getSearchTag() {
return searchTag;
}
@Override
public void addToSearchTag(String searchTag) {
if (this.searchTag == null) {
this.searchTag = searchTag;
} else if (!this.searchTag.contains(searchTag)) {
this.searchTag += " "+searchTag;
}
}
@Override
public String getEmail() {
return email;
}
@Override
public boolean hasEmail() {
return email != null && !email.isEmpty();
}
@Override
public Double getTimeOnTimeFactor() {
return timeOnTimeFactor;
}
@Override
public Duration getTimeOnDistanceAllowancePerNauticalMile() {
return timeOnDistanceAllowancePerNauticalMile;
}
}
@@ -117,6 +117,45 @@ public class LeaderboardDTO extends AbstractLeaderboardDTO implements Serializab
return higherScoresIsBetter;
}
public BoatDTO getBoatOfCompetitor(String raceColumnName, CompetitorDTO competitor) {
BoatDTO result = null;
if (rows != null) {
LeaderboardRowDTO leaderboardRow = rows.get(competitor);
if (leaderboardRow != null) {
if (this.canBoatsOfCompetitorsChangePerRace == false) {
result = leaderboardRow.boat;
} else {
LeaderboardEntryDTO leaderboardEntry = leaderboardRow.fieldsByRaceColumnName.get(raceColumnName);
if (leaderboardEntry != null) {
result = leaderboardEntry.boat;
}
}
}
}
return result;
}
public void setBoatOfCompetitor(CompetitorDTO competitor, BoatDTO boat) {
if (rows != null && this.canBoatsOfCompetitorsChangePerRace == false) {
LeaderboardRowDTO leaderboardRowDTO = rows.get(competitor);
if (leaderboardRowDTO != null) {
leaderboardRowDTO.boat = boat;
}
}
}
public void setBoatOfCompetitorForRace(String raceColumnName, CompetitorDTO competitor, BoatDTO boat) {
if (rows != null && this.canBoatsOfCompetitorsChangePerRace == true) {
LeaderboardRowDTO leaderboardRow = rows.get(competitor);
if (leaderboardRow != null) {
LeaderboardEntryDTO leaderboardEntry = leaderboardRow.fieldsByRaceColumnName.get(raceColumnName);
if (leaderboardEntry != null) {
leaderboardEntry.boat = boat;
}
}
}
}
public void setCompetitorsFromBestToWorst(String raceColumnName, List<CompetitorDTO> orderedCompetitors) {
competitorOrderingPerRaceColumnName.put(raceColumnName, orderedCompetitors);
}
@@ -27,7 +27,10 @@ public class LeaderboardEntryDTO implements Serializable {
* fleet the competitor started in this column.
*/
public RaceIdentifier race;
/** the boat of the competitor in case of regattas where boat can change during racing, null otherwise */
public BoatDTO boat;
/**
* Either <code>null</code> in case no max points, or one of "DNS", "DNF", "OCS", "DND", "RAF", "BFD", "DNC", or "DSQ"
*/
@@ -264,6 +267,7 @@ public class LeaderboardEntryDTO implements Serializable {
result = prime * result + ((totalPointsUncorrected == null) ? 0 : totalPointsUncorrected.hashCode());
result = prime * result + (totalPointsCorrected ? 1231 : 1237);
result = prime * result + ((race == null) ? 0 : race.hashCode());
result = prime * result + ((boat == null) ? 0 : boat.hashCode());
result = prime * result + ((reasonForMaxPoints == null) ? 0 : reasonForMaxPoints.hashCode());
result = prime
* result
@@ -366,6 +370,11 @@ public class LeaderboardEntryDTO implements Serializable {
return false;
} else if (!race.equals(other.race))
return false;
if (boat == null) {
if (other.boat != null)
return false;
} else if (!boat.equals(other.boat))
return false;
if (reasonForMaxPoints != other.reasonForMaxPoints)
return false;
if (speedOverGroundAtPassingStartWaypointInKnots == null) {
@@ -16,6 +16,7 @@ import com.sap.sse.common.Duration;
public class LeaderboardRowDTO implements Serializable {
private static final long serialVersionUID = -5421934148931661900L;
public CompetitorDTO competitor;
public BoatDTO boat;
public Map<String, LeaderboardEntryDTO> fieldsByRaceColumnName;
public Double carriedPoints;
public Double netPoints;
@@ -36,6 +37,7 @@ public class LeaderboardRowDTO implements Serializable {
int result = 1;
result = prime * result + ((carriedPoints == null) ? 0 : carriedPoints.hashCode());
result = prime * result + ((competitor == null) ? 0 : competitor.hashCode());
result = prime * result + ((boat == null) ? 0 : boat.hashCode());
result = prime * result + ((fieldsByRaceColumnName == null) ? 0 : fieldsByRaceColumnName.hashCode());
result = prime * result
+ ((maximumSpeedOverGroundInKnots == null) ? 0 : maximumSpeedOverGroundInKnots.hashCode());
@@ -79,6 +81,11 @@ public class LeaderboardRowDTO implements Serializable {
return false;
} else if (!competitor.equals(other.competitor))
return false;
if (boat == null) {
if (other.boat != null)
return false;
} else if (!boat.equals(other.boat))
return false;
if (fieldsByRaceColumnName == null) {
if (other.fieldsByRaceColumnName != null)
return false;
@@ -6,31 +6,31 @@ import java.util.List;
import com.sap.sse.common.Util.Pair;
public class PairingListDTO implements Serializable{
public class PairingListDTO implements Serializable {
private static final long serialVersionUID = 102220422437194196L;
private List<List<List<Pair<CompetitorDTO, BoatDTO>>>> pairingList;
private List<List<List<Pair<CompetitorWithoutBoatDTO, BoatDTO>>>> pairingList;
private List<String> raceColumnNames;
public PairingListDTO() { }
public PairingListDTO(List<List<List<Pair<CompetitorDTO, BoatDTO>>>> result) {
public PairingListDTO(List<List<List<Pair<CompetitorWithoutBoatDTO, BoatDTO>>>> result) {
this(result, null);
}
public PairingListDTO(List<List<List<Pair<CompetitorDTO, BoatDTO>>>> result, List<String> raceColumnNames) {
public PairingListDTO(List<List<List<Pair<CompetitorWithoutBoatDTO, BoatDTO>>>> result, List<String> raceColumnNames) {
this.pairingList = result;
this.raceColumnNames = raceColumnNames;
}
public List<List<List<Pair<CompetitorDTO, BoatDTO>>>> getPairingList() {
public List<List<List<Pair<CompetitorWithoutBoatDTO, BoatDTO>>>> getPairingList() {
return pairingList;
}
public List<BoatDTO> getBoats() {
List<BoatDTO> boats = new ArrayList<>();
for (List<Pair<CompetitorDTO, BoatDTO>> fleet : this.pairingList.get(0)) {
for (Pair<CompetitorDTO, BoatDTO> competitorAndBoatPair : fleet) {
for (List<Pair<CompetitorWithoutBoatDTO, BoatDTO>> fleet : this.pairingList.get(0)) {
for (Pair<CompetitorWithoutBoatDTO, BoatDTO> competitorAndBoatPair : fleet) {
if (!boats.contains(competitorAndBoatPair.getB())) {
boats.add(competitorAndBoatPair.getB());
}
@@ -18,22 +18,23 @@ public class PairingListTemplateDTO implements Serializable {
public PairingListTemplateDTO() { }
public PairingListTemplateDTO(int competitorCount, int flightMultiplier) {
this(0, 0, competitorCount, flightMultiplier, null, 0.0, null);
this(0, 0, competitorCount, flightMultiplier, null, 0.0, /* selected flight names */ null);
}
public PairingListTemplateDTO(int competitorCount, int flightMultiplier, int[][] pairingListTemplate, double quality) {
this(0, 0, competitorCount, flightMultiplier, pairingListTemplate, quality, null);
this(0, 0, competitorCount, flightMultiplier, pairingListTemplate, quality, /* selected flight names */ null);
}
public PairingListTemplateDTO(int competitorCount, int[][] pairingListTemplate, double quality) {
this(0, 0, competitorCount, 0, pairingListTemplate, quality, null);
this(0, 0, competitorCount, 0, pairingListTemplate, quality, /* selected flight names */ null);
}
public PairingListTemplateDTO(int flightCount, int groupCount, int competitorCount, int flightMultiplier, int[][] pairingListTemplate, double quality) {
this(flightCount, groupCount, competitorCount, flightMultiplier, pairingListTemplate, quality, null);
this(flightCount, groupCount, competitorCount, flightMultiplier, pairingListTemplate, quality, /* selected flight names */ null);
}
public PairingListTemplateDTO(int flightCount, int groupCount, int competitorCount, int flightMultiplier, int[][] pairingListTemplate, double quality, Iterable<String> selectedFlightNames) {
public PairingListTemplateDTO(int flightCount, int groupCount, int competitorCount, int flightMultiplier,
int[][] pairingListTemplate, double quality, Iterable<String> selectedFlightNames) {
this.flightCount = flightCount;
this.groupCount = groupCount;
this.competitorCount = competitorCount;
@@ -46,6 +47,7 @@ public class PairingListTemplateDTO implements Serializable {
public void setFlightCount(int flightCount){
this.flightCount=flightCount;
}
public void setGroupCount(int groupCount) {
this.groupCount = groupCount;
}
@@ -64,6 +64,18 @@ public class PreviousCompetitorDTOImpl implements CompetitorDTO {
" need to be replaced by an object of "+CompetitorDTOImpl.class.getName()+" after deserialization");
}
@Override
public String getShortName() {
throw new RuntimeException("Internal error. Objects of type "+PreviousCompetitorDTOImpl.class.getName()+
" need to be replaced by an object of "+CompetitorDTOImpl.class.getName()+" after deserialization");
}
@Override
public String getShortInfo() {
throw new RuntimeException("Internal error. Objects of type "+PreviousCompetitorDTOImpl.class.getName()+
" need to be replaced by an object of "+CompetitorDTOImpl.class.getName()+" after deserialization");
}
@Override
public String getSailID() {
throw new RuntimeException("Internal error. Objects of type "+PreviousCompetitorDTOImpl.class.getName()+
@@ -76,6 +88,12 @@ public class PreviousCompetitorDTOImpl implements CompetitorDTO {
" need to be replaced by an object of "+CompetitorDTOImpl.class.getName()+" after deserialization");
}
@Override
public void setBoat(BoatDTO boat) {
throw new RuntimeException("Internal error. Objects of type "+PreviousCompetitorDTOImpl.class.getName()+
" need to be replaced by an object of "+CompetitorDTOImpl.class.getName()+" after deserialization");
}
@Override
public Color getColor() {
throw new RuntimeException("Internal error. Objects of type "+PreviousCompetitorDTOImpl.class.getName()+
@@ -26,11 +26,13 @@ public class DeviceMappingConstants {
public static final String URL_EVENT_ID = "event_id";
public static final String URL_LEADERBOARD_NAME = "leaderboard_name";
public static final String URL_COMPETITOR_ID_AS_STRING = "competitor_id";
public static final String URL_BOAT_ID_AS_STRING = "boat_id";
public static final String URL_MARK_ID_AS_STRING = "mark_id";
public static final String URL_FROM_MILLIS = "from_millis";
public static final String URL_TO_MILLIS = "to_millis";
public static final String JSON_COMPETITOR_ID_AS_STRING = "competitorId";
public static final String JSON_BOAT_ID_AS_STRING = "boatId";
public static final String JSON_MARK_ID_AS_STRING = "markId";
public static final String JSON_DEVICE_UUID = "deviceUuid";
public static final String JSON_DEVICE_TYPE = "deviceType";
@@ -12,6 +12,7 @@ public enum Permission implements com.sap.sse.security.shared.Permission {
SHOW_TRACKED_RACES,
MANAGE_RACELOG_TRACKING,
MANAGE_ALL_COMPETITORS,
MANAGE_ALL_BOATS,
MANAGE_COURSE_LAYOUT,
MANAGE_WIND,
MANAGE_IGTIMI_ACCOUNTS,
@@ -23,6 +23,7 @@ public class SailingPermissionsForRoleProvider implements PermissionsForRoleProv
// AdminConsole:
Permission.MANAGE_ALL_COMPETITORS,
Permission.MANAGE_ALL_BOATS,
Permission.MANAGE_COURSE_LAYOUT,
Permission.MANAGE_DEVICE_CONFIGURATION,
Permission.MANAGE_EVENTS,
@@ -0,0 +1,14 @@
package com.sap.sailing.domain.common.tracking.impl;
/**
* Used to assemble a compatible JSON string by themselves, using the constants from here.The field identified by this
* name tells the fully-qualified class name from which the value of the field identified by {@link #FIELD_ID} has been
* created. When deserializing, the ID needs to be re-constructed as an instance of that type.
*/
public class BoatJsonConstants {
public static final String FIELD_ID = "id";
public static final String FIELD_NAME = "name";
public static final String FIELD_SAIL_ID = "sailID";
public static final String FIELD_COLOR = "color";
public static final String FIELD_BOAT_CLASS_NAME = "boatClassName";
}
@@ -8,6 +8,7 @@ package com.sap.sailing.domain.common.tracking.impl;
public class CompetitorJsonConstants {
public static final String FIELD_ID = "id";
public static final String FIELD_NAME = "name";
public static final String FIELD_SHORT_NAME = "shortName";
public static final String FIELD_SAIL_ID = "sailID";
public static final String FIELD_NATIONALITY = "nationality";
public static final String FIELD_COUNTRY_CODE = "countryCode";
@@ -20,6 +21,8 @@ public class CompetitorJsonConstants {
public static final String FIELD_NATIONALITY_ISO3 = "nationalityISO3";
public static final String FIELD_TEAM = "team";
public static final String FIELD_BOAT = "boat";
public static final String FIELD_BOAT_ID_TYPE = "boatidtype";
public static final String FIELD_BOAT_ID = "boatId";
public static final String FIELD_DISPLAY_COLOR = "displayColor";
public static final String FIELD_EMAIL = "email";
public static final String FIELD_SEARCHTAG = "searchTag";
@@ -12,8 +12,10 @@ import java.util.function.Consumer;
import com.mongodb.DBObject;
import com.sap.sailing.domain.abstractlog.race.RaceLog;
import com.sap.sailing.domain.abstractlog.regatta.RegattaLog;
import com.sap.sailing.domain.base.Boat;
import com.sap.sailing.domain.anniversary.DetailedRaceInfo;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.CompetitorWithBoat;
import com.sap.sailing.domain.base.DomainFactory;
import com.sap.sailing.domain.base.Event;
import com.sap.sailing.domain.base.RaceDefinition;
@@ -129,13 +131,25 @@ public interface DomainObjectFactory {
RaceLog loadRaceLog(RaceLogIdentifier identifier);
RegattaLog loadRegattaLog(RegattaLikeIdentifier identifier);
/**
* Loads all competitors, and resolves them via the domain factory.
* @return
* Migrates the old COMPETITORS collection and the new BOATS collection.
* The old COLLECTION is will be renamed to COMPETITORS_BAK for deveopment and test purposes
* @return a collection of the old type where all competitors contain their boats or null if migration was not required.
*/
Iterable<CompetitorWithBoat> migrateLegacyCompetitorsIfRequired();
/**
* Loads all competitors (with and without embedded boats) and resolves them via the domain factory.
* Returns a collection of {@link Competitor} or {@link CompetitorWithBoat} objects.
*/
Collection<Competitor> loadAllCompetitors();
/**
* Loads all boats and resolves them via the domain factory.
*/
Collection<Boat> loadAllBoats();
DomainFactory getBaseDomainFactory();
Iterable<Entry<DeviceConfigurationMatcher, DeviceConfiguration>> loadAllDeviceConfigurations();
@@ -8,6 +8,7 @@ import java.util.concurrent.ConcurrentHashMap;
import com.mongodb.DB;
import com.mongodb.DBObject;
import com.sap.sailing.domain.anniversary.DetailedRaceInfo;
import com.sap.sailing.domain.base.Boat;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Event;
import com.sap.sailing.domain.base.RaceDefinition;
@@ -122,9 +123,9 @@ public interface MongoObjectFactory {
void storeRegattaForRaceID(String id, Regatta regatta);
void removeRegattaForRaceID(String raceIDAsString, Regatta regatta);
/**
* Stores a competitor, including the team and boat. This should not be done for competitors for which
* Stores a competitor. This should not be done for competitors for which
* the master data is supplied by other systems, such as TracTrac, but rather for smartphone tracking,
* where this data is otherwise not recoverable.
* @param competitor the competitor to store/update in the database
@@ -135,11 +136,29 @@ public interface MongoObjectFactory {
* Like {@link #storeCompetitor(Competitor)}, but for a collection of competitors that are all
* expected to be new, having a unique {@link Competitor#getId() ID}.
*/
void storeCompetitors(Iterable<Competitor> competitors);
void storeCompetitors(Iterable<? extends Competitor> competitors);
void removeAllCompetitors();
void removeCompetitor(Competitor competitor);
/**
* Stores a boat. This should not be done for boats for which
* the master data is supplied by other systems, such as TracTrac, but rather for smartphone tracking,
* where this data is otherwise not recoverable.
* @param boat the boat to store/update in the database
*/
void storeBoat(Boat boat);
/**
* Like {@link #storeBoat(Boat)}, but for a collection of boats that are all
* expected to be new, having a unique {@link Boat#getId() ID}.
*/
void storeBoats(Iterable<Boat> boats);
void removeAllBoats();
void removeBoat(Boat boat);
DB getDatabase();
@@ -68,16 +68,27 @@ public enum CollectionNames {
*/
REGATTA_FOR_RACE_ID,
/**
* Stores the race log events for a tracked race.
*/
/**
* Stores competitors for smartphone tracking.
* Legacy store for competitors before implementation of bug2822
* Contains the old competitors with contained boats.
*/
COMPETITORS_BAK,
/**
* Stores competitors with or without boat references.
*/
COMPETITORS,
/**
* Stores boats.
*/
BOATS,
/**
* Stores the race log events for a tracked race.
*/
RACE_LOGS,
REGATTA_LOGS,
@@ -38,6 +38,7 @@ public enum FieldNames {
REGATTA_BUOY_ZONE_RADIUS_IN_HULL_LENGTHS,
REGATTA_USE_START_TIME_INFERENCE,
REGATTA_CONTROL_TRACKING_FROM_START_AND_FINISH_TIMES,
REGATTA_CAN_BOATS_OF_COMPETITORS_CHANGE_PER_RACE,
SERIES_NAME,
SERIES_IS_MEDAL,
SERIES_IS_FLEETS_CAN_RUN_IN_PARALLEL,
@@ -102,7 +103,7 @@ public enum FieldNames {
RACE_LOG_POSITIONED_COMPETITORS, RACE_LOG_PATHFINDER_ID, RACE_LOG_GATE_LINE_OPENING_TIME, RACE_LOG_GOLF_DOWN_TIME, RACE_LOG_START_PROCEDURE_TYPE,
RACE_LOG_PROTEST_START_TIME, RACE_LOG_PROTEST_END_TIME, RACE_LOG_COURSE_DESIGN_NAME, RACE_LOG_REVOKED_EVENT_ID, RACE_LOG_REVOKED_EVENT_TYPE,
RACE_LOG_REVOKED_EVENT_SHORT_INFO, RACE_LOG_REVOKED_REASON, RACE_LOG_DEVICE_MAPPING_EVENT_ID,
RACE_LOG_CLOSING_TIMEPOINT, TIMEPOINT_OF_FIXED_MARKPASSING, INDEX_OF_PASSED_WAYPOINT,INDEX_OF_FIRST_SUPPRESSED_WAYPOINT, MARK, RACE_LOG_COMPETITOR_ID,
RACE_LOG_CLOSING_TIMEPOINT, TIMEPOINT_OF_FIXED_MARKPASSING, INDEX_OF_PASSED_WAYPOINT,INDEX_OF_FIRST_SUPPRESSED_WAYPOINT, MARK, RACE_LOG_COMPETITOR_ID, RACE_LOG_BOAT_ID,
RACE_LOG_DEPDENDENT_ON_FLEET, RACE_LOG_DEPDENDENT_ON_RACECOLUMN, RACE_LOG_DEPDENDENT_ON_REGATTALIKE,
RACE_LOG_START_TIME_DIFFERENCE_IN_MS, RACE_LOG_FINISHING_TIME_AS_MILLIS,
/** @deprecated Replaced by {@link #REGATTA_LOG_FROM}; auto-migration from race log device mappings to regatta log is implemented */ @Deprecated RACE_LOG_FROM,
@@ -118,7 +119,7 @@ public enum FieldNames {
// did not reuse RACE_LOG* types, as this would require handling old versions of all events
REGATTA_LOG_IDENTIFIER_TYPE, REGATTA_LOG_IDENTIFIER_NAME, REGATTA_LOG_EVENT_ID, REGATTA_LOG_EVENT_CREATED_AT, REGATTA_LOG_EVENT_AUTHOR_NAME,
REGATTA_LOG_EVENT_AUTHOR_PRIORITY, REGATTA_LOG_EVENT_CLASS, REGATTA_LOG_REVOKED_EVENT_ID, REGATTA_LOG_REVOKED_EVENT_TYPE, REGATTA_LOG_REVOKED_EVENT_SHORT_INFO,
REGATTA_LOG_REVOKED_REASON, REGATTA_LOG_COMPETITOR_ID, REGATTA_LOG_DEVICE_MAPPING_EVENT_ID, REGATTA_LOG_CLOSING_TIMEPOINT,
REGATTA_LOG_REVOKED_REASON, REGATTA_LOG_COMPETITOR_ID, REGATTA_LOG_BOAT_ID, REGATTA_LOG_DEVICE_MAPPING_EVENT_ID, REGATTA_LOG_CLOSING_TIMEPOINT,
REGATTA_LOG_FROM, REGATTA_LOG_TO, REGATTA_LOG_EVENT, REGATTA_LOG_MARK,
// remote server reference fields
@@ -0,0 +1,50 @@
package com.sap.sailing.domain.persistence.impl;
import java.io.Serializable;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.sap.sailing.domain.base.BoatClass;
import com.sap.sailing.domain.base.CourseArea;
import com.sap.sailing.domain.base.MigratableRegatta;
import com.sap.sailing.domain.base.Series;
import com.sap.sailing.domain.base.impl.RegattaImpl;
import com.sap.sailing.domain.leaderboard.ScoringScheme;
import com.sap.sailing.domain.persistence.MongoObjectFactory;
import com.sap.sailing.domain.racelog.RaceLogStore;
import com.sap.sailing.domain.ranking.RankingMetricConstructor;
import com.sap.sailing.domain.regattalog.RegattaLogStore;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util;
public class MigratableRegattaImpl extends RegattaImpl implements MigratableRegatta {
private static final long serialVersionUID = -3545488249832218320L;
private static final Logger logger = Logger.getLogger(MigratableRegattaImpl.class.getName());
/**
* To be used for storing a migrated regatta
*/
private transient final MongoObjectFactory mongoObjectFactory;
public <S extends Series> MigratableRegattaImpl(RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, String name,
BoatClass boatClass, boolean canBoatsOfCompetitorsChangePerRace, TimePoint startDate, TimePoint endDate,
Iterable<S> series, boolean persistent, ScoringScheme scoringScheme, Serializable id, CourseArea courseArea,
Double buoyZoneRadiusInHullLengths, boolean useStartTimeInference,
boolean controlTrackingFromStartAndFinishTimes, RankingMetricConstructor rankingMetricConstructor, MongoObjectFactory mongoObjectFactory) {
super(raceLogStore, regattaLogStore, name, boatClass, canBoatsOfCompetitorsChangePerRace, startDate, endDate, series,
persistent, scoringScheme, id, courseArea, buoyZoneRadiusInHullLengths, useStartTimeInference,
controlTrackingFromStartAndFinishTimes, rankingMetricConstructor);
this.mongoObjectFactory = mongoObjectFactory;
mongoObjectFactory.storeRegatta(this); // make sure the canBoatsOfCompetitorsChangePerRace flag makes it into the DB
}
public synchronized void migrateCanBoatsOfCompetitorsChangePerRace() {
// It should not be possible to call this after races have been already added to this regatta.
assert !canBoatsOfCompetitorsChangePerRace() && Util.size(getAllRaces()) == 0;
super.setCanBoatsOfCompetitorsChangePerRace(true);
if (mongoObjectFactory != null) {
logger.log(Level.INFO, "Bug2822 DB-Migration: Store migrated regatta '" + getName() + "' having now canBoatsOfCompetitorsChangePerRace=true.");
mongoObjectFactory.storeRegatta(this);
}
}
}
@@ -55,16 +55,21 @@ import com.sap.sailing.domain.abstractlog.race.tracking.RaceLogUseCompetitorsFro
import com.sap.sailing.domain.abstractlog.regatta.RegattaLogEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogCloseOpenEndedDeviceMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDefineMarkEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceBoatMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceBoatSensorDataMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceCompetitorMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceCompetitorSensorDataMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMarkMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogRegisterBoatEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogRegisterCompetitorEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogRevokeEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogSetCompetitorTimeOnDistanceAllowancePerNauticalMileEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogSetCompetitorTimeOnTimeFactorEvent;
import com.sap.sailing.domain.anniversary.DetailedRaceInfo;
import com.sap.sailing.domain.base.Boat;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.CompetitorWithBoat;
import com.sap.sailing.domain.base.ControlPoint;
import com.sap.sailing.domain.base.ControlPointWithTwoMarks;
import com.sap.sailing.domain.base.CourseArea;
@@ -116,7 +121,9 @@ import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.TrackedRegatta;
import com.sap.sailing.domain.tracking.WindTrack;
import com.sap.sailing.server.gateway.serialization.JsonSerializer;
import com.sap.sailing.server.gateway.serialization.impl.BoatJsonSerializer;
import com.sap.sailing.server.gateway.serialization.impl.CompetitorJsonSerializer;
import com.sap.sailing.server.gateway.serialization.impl.CompetitorWithBoatRefJsonSerializer;
import com.sap.sailing.server.gateway.serialization.impl.DeviceConfigurationJsonSerializer;
import com.sap.sailing.server.gateway.serialization.impl.RegattaConfigurationJsonSerializer;
import com.sap.sse.common.Duration;
@@ -135,7 +142,9 @@ import com.sap.sse.shared.media.VideoDescriptor;
public class MongoObjectFactoryImpl implements MongoObjectFactory {
private static Logger logger = Logger.getLogger(MongoObjectFactoryImpl.class.getName());
private final DB database;
private final CompetitorWithBoatRefJsonSerializer competitorWithBoatRefSerializer = CompetitorWithBoatRefJsonSerializer.create();
private final CompetitorJsonSerializer competitorSerializer = CompetitorJsonSerializer.create();
private final BoatJsonSerializer boatSerializer = BoatJsonSerializer.create();
private final TypeBasedServiceFinder<DeviceIdentifierMongoHandler> deviceIdentifierServiceFinder;
private final TypeBasedServiceFinder<RaceTrackingConnectivityParametersHandler> raceTrackingConnectivityParamsServiceFinder;
@@ -706,6 +715,7 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
dbRegatta.put(FieldNames.REGATTA_BUOY_ZONE_RADIUS_IN_HULL_LENGTHS.name(), regatta.getBuoyZoneRadiusInHullLengths());
dbRegatta.put(FieldNames.REGATTA_USE_START_TIME_INFERENCE.name(), regatta.useStartTimeInference());
dbRegatta.put(FieldNames.REGATTA_CONTROL_TRACKING_FROM_START_AND_FINISH_TIMES.name(), regatta.isControlTrackingFromStartAndFinishTimes());
dbRegatta.put(FieldNames.REGATTA_CAN_BOATS_OF_COMPETITORS_CHANGE_PER_RACE.name(), regatta.canBoatsOfCompetitorsChangePerRace());
dbRegatta.put(FieldNames.REGATTA_RANKING_METRIC.name(), storeRankingMetric(regatta));
boolean success = false;
final int MAX_TRIES = 3;
@@ -1067,6 +1077,7 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
storeRaceLogEventProperties(event, result);
result.put(FieldNames.RACE_LOG_EVENT_CLASS.name(), RaceLogRegisterCompetitorEvent.class.getSimpleName());
result.put(FieldNames.RACE_LOG_COMPETITOR_ID.name(), event.getCompetitor().getId());
result.put(FieldNames.RACE_LOG_BOAT_ID.name(), event.getBoat().getId());
return result;
}
@@ -1126,7 +1137,7 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
storeTimePoint(event.getCreatedAt(), result, FieldNames.RACE_LOG_EVENT_CREATED_AT);
result.put(FieldNames.RACE_LOG_EVENT_ID.name(), event.getId());
result.put(FieldNames.RACE_LOG_EVENT_PASS_ID.name(), event.getPassId());
result.put(FieldNames.RACE_LOG_EVENT_INVOLVED_BOATS.name(), storeInvolvedBoatsForRaceLogEvent(event.getInvolvedBoats()));
result.put(FieldNames.RACE_LOG_EVENT_INVOLVED_BOATS.name(), storeInvolvedBoatsForRaceLogEvent(event.getInvolvedCompetitors()));
storeRaceLogEventAuthor(result, event.getAuthor());
}
@@ -1290,8 +1301,17 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
}
return passing;
}
@Override
public void storeCompetitor(Competitor competitor) {
if (competitor.hasBoat()) {
storeCompetitorWithBoat((CompetitorWithBoat) competitor);
} else {
storeCompetitorWithoutBoat(competitor);
}
}
private void storeCompetitorWithoutBoat(Competitor competitor) {
DBCollection collection = database.getCollection(CollectionNames.COMPETITORS.name());
JSONObject json = competitorSerializer.serialize(competitor);
DBObject query = (DBObject) JSON.parse(CompetitorJsonSerializer.getCompetitorIdQuery(competitor).toString());
@@ -1299,9 +1319,33 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
collection.update(query, entry, /* upsrt */true, /* multi */false, WriteConcern.SAFE);
}
private void storeCompetitorWithBoat(CompetitorWithBoat competitor) {
DBCollection collection = database.getCollection(CollectionNames.COMPETITORS.name());
JSONObject json = competitorWithBoatRefSerializer.serialize(competitor);
DBObject query = (DBObject) JSON.parse(CompetitorJsonSerializer.getCompetitorIdQuery(competitor).toString());
DBObject entry = (DBObject) JSON.parse(json.toString());
collection.update(query, entry, /* upsrt */true, /* multi */false, WriteConcern.SAFE);
}
@Override
public void storeCompetitors(Iterable<Competitor> competitors) {
public void storeCompetitors(Iterable<? extends Competitor> competitors) {
if (competitors != null && !Util.isEmpty(competitors)) {
List<Competitor> competitorsWithoutBoat = new ArrayList<>();
List<CompetitorWithBoat> competitorsWithBoat = new ArrayList<>();
for (Competitor competitor : competitors) {
if (competitor.hasBoat()) {
competitorsWithBoat.add((CompetitorWithBoat) competitor);
} else {
competitorsWithoutBoat.add(competitor);
}
}
storeCompetitorsWithBoat(competitorsWithBoat);
storeCompetitorsWithoutBoat(competitorsWithoutBoat);
}
}
private void storeCompetitorsWithoutBoat(Iterable<Competitor> competitors) {
if (!Util.isEmpty(competitors)) {
DBCollection collection = database.getCollection(CollectionNames.COMPETITORS.name());
List<DBObject> competitorsDB = new ArrayList<>();
for (Competitor competitor : competitors) {
@@ -1313,9 +1357,22 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
}
}
private void storeCompetitorsWithBoat(Iterable<CompetitorWithBoat> competitors) {
if (!Util.isEmpty(competitors)) {
DBCollection collection = database.getCollection(CollectionNames.COMPETITORS.name());
List<DBObject> competitorsDB = new ArrayList<>();
for (CompetitorWithBoat competitor : competitors) {
JSONObject json = competitorWithBoatRefSerializer.serialize(competitor);
DBObject entry = (DBObject) JSON.parse(json.toString());
competitorsDB.add(entry);
}
collection.insert(competitorsDB);
}
}
@Override
public void removeAllCompetitors() {
logger.info("Removing all persistent competitor info");
logger.info("Removing all persistent competitors");
DBCollection collection = database.getCollection(CollectionNames.COMPETITORS.name());
collection.drop();
}
@@ -1327,7 +1384,45 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
DBObject query = (DBObject) JSON.parse(CompetitorJsonSerializer.getCompetitorIdQuery(competitor).toString());
collection.remove(query, WriteConcern.SAFE);
}
@Override
public void storeBoat(Boat boat) {
DBCollection collection = database.getCollection(CollectionNames.BOATS.name());
JSONObject json = boatSerializer.serialize(boat);
DBObject query = (DBObject) JSON.parse(BoatJsonSerializer.getBoatIdQuery(boat).toString());
DBObject entry = (DBObject) JSON.parse(json.toString());
collection.update(query, entry, /* upsrt */true, /* multi */false, WriteConcern.SAFE);
}
@Override
public void storeBoats(Iterable<Boat> boats) {
if (boats != null && !Util.isEmpty(boats)) {
DBCollection collection = database.getCollection(CollectionNames.BOATS.name());
List<DBObject> boatsDB = new ArrayList<>();
for (Boat boat : boats) {
JSONObject json = boatSerializer.serialize(boat);
DBObject entry = (DBObject) JSON.parse(json.toString());
boatsDB.add(entry);
}
collection.insert(boatsDB);
}
}
@Override
public void removeAllBoats() {
logger.info("Removing all persistent boats");
DBCollection collection = database.getCollection(CollectionNames.BOATS.name());
collection.drop();
}
@Override
public void removeBoat(Boat boat) {
logger.info("Removing persistent boat "+boat.getName()+" with ID "+boat.getId());
DBCollection collection = database.getCollection(CollectionNames.BOATS.name());
DBObject query = (DBObject) JSON.parse(BoatJsonSerializer.getBoatIdQuery(boat).toString());
collection.remove(query, WriteConcern.SAFE);
}
@Override
public void storeDeviceConfiguration(DeviceConfigurationMatcher matcher, DeviceConfiguration configuration) {
DBCollection configurationsCollections = database.getCollection(CollectionNames.CONFIGURATIONS.name());
@@ -1463,6 +1558,14 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
result.put(FieldNames.COMPETITOR_ID.name(), event.getMappedTo().getId());
storeRegattaLogEvent(regattaLikeId, result);
}
public void storeRegattaLogEvent(RegattaLikeIdentifier regattaLikeId, RegattaLogDeviceBoatMappingEvent event) {
DBObject result = createBasicRegattaLogEventDBObject(event);
result.put(FieldNames.REGATTA_LOG_EVENT_CLASS.name(), RegattaLogDeviceBoatMappingEvent.class.getSimpleName());
storeDeviceMappingEvent(event, result, FieldNames.REGATTA_LOG_FROM, FieldNames.REGATTA_LOG_TO);
result.put(FieldNames.RACE_LOG_BOAT_ID.name(), event.getMappedTo().getId());
storeRegattaLogEvent(regattaLikeId, result);
}
public void storeRegattaLogEvent(RegattaLikeIdentifier regattaLikeId,
RegattaLogDeviceCompetitorSensorDataMappingEvent event) {
@@ -1472,6 +1575,15 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
result.put(FieldNames.COMPETITOR_ID.name(), event.getMappedTo().getId());
storeRegattaLogEvent(regattaLikeId, result);
}
public void storeRegattaLogEvent(RegattaLikeIdentifier regattaLikeId,
RegattaLogDeviceBoatSensorDataMappingEvent event) {
DBObject result = createBasicRegattaLogEventDBObject(event);
result.put(FieldNames.REGATTA_LOG_EVENT_CLASS.name(), event.getClass().getSimpleName());
storeDeviceMappingEvent(event, result, FieldNames.REGATTA_LOG_FROM, FieldNames.REGATTA_LOG_TO);
result.put(FieldNames.RACE_LOG_BOAT_ID.name(), event.getMappedTo().getId());
storeRegattaLogEvent(regattaLikeId, result);
}
public void storeRegattaLogEvent(RegattaLikeIdentifier regattaLikeId, RegattaLogDeviceMarkMappingEvent event) {
DBObject result = createBasicRegattaLogEventDBObject(event);
@@ -1491,6 +1603,13 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
storeRegattaLogEvent(regattaLikeId, result);
}
public void storeRegattaLogEvent(RegattaLikeIdentifier regattaLikeId, RegattaLogRegisterBoatEvent event) {
DBObject result = createBasicRegattaLogEventDBObject(event);
result.put(FieldNames.REGATTA_LOG_EVENT_CLASS.name(), RegattaLogRegisterBoatEvent.class.getSimpleName());
result.put(FieldNames.REGATTA_LOG_BOAT_ID.name(), event.getBoat().getId());
storeRegattaLogEvent(regattaLikeId, result);
}
public void storeRegattaLogEvent(RegattaLikeIdentifier regattaLikeId, RegattaLogRegisterCompetitorEvent event) {
DBObject result = createBasicRegattaLogEventDBObject(event);
result.put(FieldNames.REGATTA_LOG_EVENT_CLASS.name(), RegattaLogRegisterCompetitorEvent.class.getSimpleName());
@@ -5,9 +5,12 @@ import java.util.logging.Logger;
import com.sap.sailing.domain.abstractlog.regatta.RegattaLogEventVisitor;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogCloseOpenEndedDeviceMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDefineMarkEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceBoatMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceBoatSensorDataMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceCompetitorMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceCompetitorSensorDataMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMarkMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogRegisterBoatEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogRegisterCompetitorEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogRevokeEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogSetCompetitorTimeOnDistanceAllowancePerNauticalMileEvent;
@@ -43,15 +46,31 @@ public class MongoRegattaLogStoreVisitor implements RegattaLogEventVisitor {
mongoObjectFactory.storeRegattaLogEvent(regattaLikeIdentifier, event);
}
@Override
public void visit(RegattaLogDeviceBoatMappingEvent event) {
mongoObjectFactory.storeRegattaLogEvent(regattaLikeIdentifier, event);
}
@Override
public void visit(RegattaLogDeviceCompetitorSensorDataMappingEvent event) {
mongoObjectFactory.storeRegattaLogEvent(regattaLikeIdentifier, event);
}
@Override
public void visit(RegattaLogDeviceBoatSensorDataMappingEvent event) {
mongoObjectFactory.storeRegattaLogEvent(regattaLikeIdentifier, event);
}
@Override
public void visit(RegattaLogCloseOpenEndedDeviceMappingEvent event) {
mongoObjectFactory.storeRegattaLogEvent(regattaLikeIdentifier, event);
}
@Override
public void visit(RegattaLogRegisterBoatEvent event) {
mongoObjectFactory.storeRegattaLogEvent(regattaLikeIdentifier, event);
}
@Override
public void visit(RegattaLogRegisterCompetitorEvent event) {
mongoObjectFactory.storeRegattaLogEvent(regattaLikeIdentifier, event);
@@ -14,6 +14,7 @@ Require-Bundle: org.junit;bundle-version="4.8.2",
org.mongodb.mongo-java-driver;bundle-version="2.13.0",
com.sap.sse.common
Import-Package: com.sap.sailing.domain.abstractlog.regatta.events,
com.sap.sailing.domain.base,
com.sap.sailing.domain.persistence.impl,
com.sap.sailing.domain.persistence.racelog.tracking.impl,
com.sap.sailing.domain.tractracadapter,
@@ -17,6 +17,7 @@ import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogDefineMa
import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogDeviceCompetitorMappingEventImpl;
import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogDeviceMarkMappingEventImpl;
import com.sap.sailing.domain.abstractlog.regatta.impl.RegattaLogImpl;
import com.sap.sailing.domain.base.Boat;
import com.sap.sailing.domain.base.BoatClass;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.DomainFactory;
@@ -59,7 +60,9 @@ public class AbstractGPSFixStoreTest extends RaceLogTrackingTestHelper {
protected final DeviceIdentifier device = new SmartphoneImeiIdentifier("a");
protected RegattaLog regattaLog;
protected SensorFixStore store;
protected final BoatClass boatClass = DomainFactory.INSTANCE.getOrCreateBoatClass("49er");
protected final Competitor comp = DomainFactory.INSTANCE.getOrCreateCompetitor("comp", "comp", null, null, null, null, null, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowanceInSecondsPerNauticalMile */ null, null);
protected final Boat boat = DomainFactory.INSTANCE.getOrCreateBoat("boat", "boat", boatClass, "GER 234", null);
protected final Mark mark = DomainFactory.INSTANCE.getOrCreateMark("mark");
protected GPSFixMoving createFix(long millis, double lat, double lng, double knots, double degrees) {
@@ -121,7 +124,7 @@ public class AbstractGPSFixStoreTest extends RaceLogTrackingTestHelper {
protected DynamicTrackedRaceImpl createDynamicTrackedRace(BoatClass boatClass, RaceDefinition raceDefinition) {
DynamicTrackedRegatta regatta = new DynamicTrackedRegattaImpl(new RegattaImpl(EmptyRaceLogStore.INSTANCE,
EmptyRegattaLogStore.INSTANCE, RegattaImpl.getDefaultName("regatta", boatClass.getName()), boatClass,
/* startDate */ null, /* endDate */null, null, null, "a", null));
/* canBoatsOfCompetitorsChangePerRace */ true, /* startDate */ null, /* endDate */null, null, null, "a", null));
return new DynamicTrackedRaceImpl(regatta, raceDefinition, Collections.<Sideline> emptyList(),
EmptyWindStore.INSTANCE, 0, 0, 0, /* useMarkPassingCalculator */ false, OneDesignRankingMetric::new,
mock(RaceLogResolver.class));
@@ -28,12 +28,18 @@ import com.sap.sailing.domain.abstractlog.race.tracking.impl.RaceLogUseCompetito
import com.sap.sailing.domain.abstractlog.regatta.RegattaLog;
import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogDeviceCompetitorMappingEventImpl;
import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogRegisterCompetitorEventImpl;
import com.sap.sailing.domain.base.Boat;
import com.sap.sailing.domain.base.BoatClass;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.CompetitorWithBoat;
import com.sap.sailing.domain.base.DomainFactory;
import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.RaceColumn;
import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.base.Series;
import com.sap.sailing.domain.base.impl.BoatClassImpl;
import com.sap.sailing.domain.base.impl.BoatImpl;
import com.sap.sailing.domain.base.impl.DynamicBoat;
import com.sap.sailing.domain.base.impl.FleetImpl;
import com.sap.sailing.domain.base.impl.RegattaImpl;
import com.sap.sailing.domain.base.impl.SeriesImpl;
@@ -67,7 +73,7 @@ import com.sap.sse.common.impl.MillisecondsTimePoint;
public class CreateAndTrackWithRaceLogTest extends RaceLogTrackingTestHelper {
private RacingEventService service;
private final static BoatClass boatClass = new BoatClassImpl("505", /* typicallyStartsUpwind */ true);
private final Fleet fleet = new FleetImpl("fleet");
private final String columnName = "column";
private RegattaLeaderboard leaderboard;
@@ -89,7 +95,8 @@ public class CreateAndTrackWithRaceLogTest extends RaceLogTrackingTestHelper {
Series series = new SeriesImpl("series", /* isMedal */ false, /* isFleetsCanRunInParallel */ true, Collections.singletonList(fleet), Collections.emptySet(),
service);
regatta = service.createRegatta(RegattaImpl.getDefaultName("regatta", "Laser"), "Laser",
/* startDate */null, /* endDate */null, UUID.randomUUID(), Collections.<Series> singletonList(series), /* persistent */ true,
/* canBoatsOfCompetitorsChangePerRace */ true, /* startDate */null, /* endDate */null,
UUID.randomUUID(), Collections.<Series> singletonList(series), /* persistent */ true,
new HighPoint(), UUID.randomUUID(), /*buoyZoneRadiusInHullLengths*/2.0, /* useStartTimeInference */true, /* controlTrackingFromStartAndFinishTimes */ false, OneDesignRankingMetric::new);
series.addRaceColumn(columnName, /* trackedRegattaRegistry */null);
leaderboard = service.addRegattaLeaderboard(regatta.getRegattaIdentifier(), "RegattaLeaderboard", new int[] {});
@@ -184,13 +191,14 @@ public class CreateAndTrackWithRaceLogTest extends RaceLogTrackingTestHelper {
assertFalse(raceLog.isEmpty());
// add a mapping and one fix in, one out of mapping
Competitor comp1 = DomainFactory.INSTANCE.getOrCreateCompetitor("comp1", "comp1", null, null, null, null, null,
Boat boat1 = new BoatImpl("id12345", "boat1", boatClass, /* sailID */ null);
Competitor comp1 = DomainFactory.INSTANCE.getOrCreateCompetitor("comp1", "comp1", "c", null, null, null, null,
/* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null);
DeviceIdentifier dev1 = new SmartphoneImeiIdentifier("dev1");
regattaLog.add(new RegattaLogDeviceCompetitorMappingEventImpl(t(), t(), author, 0, comp1, dev1, t(0), t(10)));
addFixes0(dev1);
raceLog.add(new RaceLogUseCompetitorsFromRaceLogEventImpl(t(), author, t(), UUID.randomUUID(), 0));
raceLog.add(new RaceLogRegisterCompetitorEventImpl(t(), author, 0, comp1));
raceLog.add(new RaceLogRegisterCompetitorEventImpl(t(), author, 0, comp1, boat1));
raceLog.add(new RaceLogStartOfTrackingEventImpl(t(0), author, /* passId */ 0));
// start tracking
trackRace(column);
@@ -281,8 +289,9 @@ public class CreateAndTrackWithRaceLogTest extends RaceLogTrackingTestHelper {
adapter.denoteRaceForRaceLogTracking(service, leaderboard, column, fleet, "race");
// add a mapping and one fix in, one out of mapping
Competitor comp1 = DomainFactory.INSTANCE.getOrCreateCompetitor("comp1", "comp1", null, null, null, null, null,
/* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null);
Boat boat1 = new BoatImpl("id12345", "boat1", boatClass, /* sailID */ null);
CompetitorWithBoat comp1 = DomainFactory.INSTANCE.getOrCreateCompetitorWithBoat("comp1", "comp1", "c1", null, null, null, null,
/* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null, (DynamicBoat) boat1);
DeviceIdentifier dev1 = new SmartphoneImeiIdentifier("dev1");
regattaLog.add(new RegattaLogDeviceCompetitorMappingEventImpl(t(), t(), author, UUID.randomUUID(), comp1, dev1,
t(0), t(10)));
@@ -6,6 +6,8 @@ import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.Collections;
import java.util.ConcurrentModificationException;
import java.util.HashMap;
import java.util.Map;
import org.junit.Before;
import org.junit.Test;
@@ -19,6 +21,7 @@ import com.sap.sailing.domain.abstractlog.race.impl.RaceLogImpl;
import com.sap.sailing.domain.abstractlog.regatta.RegattaLog;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.impl.RegattaLogImpl;
import com.sap.sailing.domain.base.Boat;
import com.sap.sailing.domain.base.BoatClass;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Course;
@@ -66,11 +69,12 @@ public class RaceLogFixTrackerManagerTest {
protected RaceLog raceLog2;
protected RegattaLog regattaLog;
protected SensorFixStore store;
protected final Competitor comp = DomainFactory.INSTANCE.getOrCreateCompetitor("comp", "comp", null, null, null,
null, null, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowanceInSecondsPerNauticalMile */ null, null);
protected final Competitor comp = DomainFactory.INSTANCE.getOrCreateCompetitor("comp", "comp", "c2", null, null, null,
null, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowanceInSecondsPerNauticalMile */ null, null);
private final BoatClass boatClass = DomainFactory.INSTANCE.getOrCreateBoatClass("49er");
protected final Boat boat = DomainFactory.INSTANCE.getOrCreateBoat("boat", "boat", boatClass, "USA 123", null);
protected final Mark mark = DomainFactory.INSTANCE.getOrCreateMark("mark");
protected final Mark mark2 = DomainFactory.INSTANCE.getOrCreateMark("mark2");
private final BoatClass boatClass = DomainFactory.INSTANCE.getOrCreateBoatClass("49er");
protected final AbstractLogEventAuthor author = new LogEventAuthorImpl("author", 0);
private DynamicTrackedRace trackedRace;
@@ -92,10 +96,12 @@ public class RaceLogFixTrackerManagerTest {
Course course = new CourseImpl("course",
Arrays.asList(new Waypoint[] { new WaypointImpl(mark), new WaypointImpl(mark2) }));
RaceDefinition race = new RaceDefinitionImpl("race", course, boatClass, Arrays.asList(comp));
Map<Competitor, Boat> competitorsAndBoats = new HashMap<>();
competitorsAndBoats.put(comp, boat);
RaceDefinition race = new RaceDefinitionImpl("race", course, boatClass, competitorsAndBoats);
DynamicTrackedRegatta regatta = new DynamicTrackedRegattaImpl(new RegattaImpl(EmptyRaceLogStore.INSTANCE,
EmptyRegattaLogStore.INSTANCE, RegattaImpl.getDefaultName("regatta", boatClass.getName()), boatClass,
/* startDate */ null, /* endDate */null, null, null, "a", null));
/* canBoatsOfCompetitorsChangePerRace */ true, /* startDate */ null, /* endDate */null, null, null, "a", null));
trackedRace = new DynamicTrackedRaceImpl(regatta, race, Collections.<Sideline> emptyList(),
EmptyWindStore.INSTANCE, 0, 0, 0, /* useMarkPassingCalculator */ false, OneDesignRankingMetric::new,
mock(RaceLogResolver.class));
@@ -12,8 +12,10 @@ import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.CyclicBarrier;
@@ -42,6 +44,7 @@ import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogDefineMa
import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogDeviceCompetitorBravoMappingEventImpl;
import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogRevokeEventImpl;
import com.sap.sailing.domain.abstractlog.regatta.impl.RegattaLogImpl;
import com.sap.sailing.domain.base.Boat;
import com.sap.sailing.domain.base.BoatClass;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Course;
@@ -120,15 +123,17 @@ public class SensorFixStoreAndLoadTest {
protected RaceLog raceLog;
protected RegattaLog regattaLog;
protected SensorFixStore store;
protected final Competitor comp = DomainFactory.INSTANCE.getOrCreateCompetitor("comp", "comp", null, null, null,
null, null, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowanceInSecondsPerNauticalMile */ null, null);
protected final Competitor comp2 = DomainFactory.INSTANCE.getOrCreateCompetitor("comp2", "comp2", null, null, null,
null, null, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowanceInSecondsPerNauticalMile */ null, null);
private final Competitor compNotPartOfRace = DomainFactory.INSTANCE.getOrCreateCompetitor("comp3", "comp3", null, null, null,
null, null, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null);
protected final Competitor comp = DomainFactory.INSTANCE.getOrCreateCompetitor("comp", "comp", null, null, null, null,
null, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowanceInSecondsPerNauticalMile */ null, null);
protected final Competitor comp2 = DomainFactory.INSTANCE.getOrCreateCompetitor("comp2", "comp2", null, null, null, null,
null, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowanceInSecondsPerNauticalMile */ null, null);
private final BoatClass boatClass = DomainFactory.INSTANCE.getOrCreateBoatClass("49er");
protected final Boat boat1 = DomainFactory.INSTANCE.getOrCreateBoat("Boat1", "Boat1", boatClass, "GER 1", null);
protected final Boat boat2 = DomainFactory.INSTANCE.getOrCreateBoat("Boat2", "Boat2", boatClass, "GER 2", null);
private final Competitor compNotPartOfRace = DomainFactory.INSTANCE.getOrCreateCompetitor("comp3", "comp3", null, null, null, null,
null, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null);
protected final Mark mark = DomainFactory.INSTANCE.getOrCreateMark("mark");
protected final Mark mark2 = DomainFactory.INSTANCE.getOrCreateMark("mark2");
private final BoatClass boatClass = DomainFactory.INSTANCE.getOrCreateBoatClass("49er");
protected final AbstractLogEventAuthor author = new LogEventAuthorImpl("author", 0);
private DynamicTrackedRace trackedRace;
@@ -156,10 +161,13 @@ public class SensorFixStoreAndLoadTest {
new MillisecondsTimePoint(1), 0, mark2));
Course course = new CourseImpl("course",
Arrays.asList(new Waypoint[] { new WaypointImpl(mark), new WaypointImpl(mark2) }));
RaceDefinition race = new RaceDefinitionImpl("race", course, boatClass, Arrays.asList(comp, comp2));
Map<Competitor, Boat> competitorsAndBoats = new HashMap<>();
competitorsAndBoats.put(comp, boat1);
competitorsAndBoats.put(comp2, boat2);
RaceDefinition race = new RaceDefinitionImpl("race", course, boatClass, competitorsAndBoats);
DynamicTrackedRegatta regatta = new DynamicTrackedRegattaImpl(new RegattaImpl(EmptyRaceLogStore.INSTANCE,
EmptyRegattaLogStore.INSTANCE, RegattaImpl.getDefaultName("regatta", boatClass.getName()), boatClass,
/* startDate */ null, /* endDate */null, null, null, "a", null));
/* canBoatsOfCompetitorsChangePerRace */ true, /* startDate */ null, /* endDate */null, null, null, "a", null));
trackedRace = new DynamicTrackedRaceImpl(regatta, race, Collections.<Sideline> emptyList(),
EmptyWindStore.INSTANCE, 0, 0, 0, /* useMarkPassingCalculator */ false, OneDesignRankingMetric::new,
mock(RaceLogResolver.class));
@@ -412,7 +420,7 @@ public class SensorFixStoreAndLoadTest {
store.storeFix(device, createBravoDoubleVectorFixWithRideHeight(FIX_TIMESTAMP2, FIX_RIDE_HEIGHT2.getMeters()));
store.storeFix(device, createBravoDoubleVectorFixWithRideHeight(FIX_TIMESTAMP3, FIX_RIDE_HEIGHT3.getMeters()));
}
private void addMoreBravoFixes() {
store.storeFix(device, createBravoDoubleVectorFixWithRideHeight(FIX_TIMESTAMP + 1, FIX_RIDE_HEIGHT.getMeters()));
store.storeFix(device, createBravoDoubleVectorFixWithRideHeight(FIX_TIMESTAMP2 + 1, FIX_RIDE_HEIGHT2.getMeters()));
@@ -428,10 +436,10 @@ public class SensorFixStoreAndLoadTest {
@Override
public <FixT extends Timed, TrackT extends DynamicTrack<FixT>> SensorFixMapper<FixT, TrackT, Competitor> createCompetitorMapper(
Class<? extends RegattaLogDeviceMappingEvent<?>> eventType) {
if (bravoDataFixMapper.isResponsibleFor(eventType)) {
if(bravoDataFixMapper.isResponsibleFor(eventType)) {
return (SensorFixMapper) bravoDataFixMapper;
}
if (testDataFixMapper.isResponsibleFor(eventType)) {
if(testDataFixMapper.isResponsibleFor(eventType)) {
return (SensorFixMapper) testDataFixMapper;
}
throw new IllegalArgumentException("Unknown event type");
@@ -442,7 +450,7 @@ public class SensorFixStoreAndLoadTest {
protected void testNumberOfRawFixes(Track<?> track, long expected) {
if (expected == 0) {
if (track != null) {
track.lockForRead();
track.lockForRead();
try {
assertTrue(size(track.getRawFixes()) == 0);
} finally {
@@ -452,10 +460,10 @@ public class SensorFixStoreAndLoadTest {
} else {
track.lockForRead();
try {
assertEquals(expected, size(track.getRawFixes()));
assertEquals(expected, size(track.getRawFixes()));
} finally {
track.unlockAfterRead();
}
track.unlockAfterRead();
}
}
}
@@ -563,6 +571,7 @@ public class SensorFixStoreAndLoadTest {
implements RegattaLogDeviceCompetitorSensorDataMappingEvent {
private static final long serialVersionUID = -14940305448048753L;
public RegattaLogDeviceCompetitorTestMappingEventImpl(TimePoint createdAt, TimePoint logicalTimePoint,
AbstractLogEventAuthor author, Serializable pId, Competitor mappedTo, DeviceIdentifier device,
TimePoint from, TimePoint to) {
@@ -618,7 +627,7 @@ public class SensorFixStoreAndLoadTest {
assertNull(trackedRace.getSensorTrack(compNotPartOfRace, BravoFixTrack.TRACK_NAME));
fixLoaderAndTracker.stop(true, /* willBeRemoved */ false);
}
@Test
/** Test for changes of bug 4044 - https://bugzilla.sapsailing.com/bugzilla/show_bug.cgi?id=4044 */
public void testThatNoSensorFixesAreLoadedAsLongAsStartOfTrackingIsNull() throws InterruptedException {
@@ -637,7 +646,7 @@ public class SensorFixStoreAndLoadTest {
testNumberOfRawFixes(trackedRace.getSensorTrack(comp, BravoFixTrack.TRACK_NAME), 3);
fixLoaderAndTracker.stop(true, /* willBeRemoved */ false);
}
@Test
/** Test for changes of bug 4044 - https://bugzilla.sapsailing.com/bugzilla/show_bug.cgi?id=4044 */
public void testThatNoSensorFixesAreRecordedAsWhenStartOfTrackingIsNull() throws InterruptedException {
@@ -794,7 +803,7 @@ public class SensorFixStoreAndLoadTest {
fixLoaderAndTracker.stop(true, /* willBeRemoved */ false);
statusTransitionListener.assertTransitions(TrackedRaceStatusEnum.PREPARED, TrackedRaceStatusEnum.TRACKING, TrackedRaceStatusEnum.LOADING, TrackedRaceStatusEnum.TRACKING, TrackedRaceStatusEnum.FINISHED);
}
@Test
/** Test for bug 4125 - https://bugzilla.sapsailing.com/bugzilla/show_bug.cgi?id=4125 */
public void testThatLoadingStateIsTriggeredWhenAddingMapping() throws InterruptedException {
@@ -810,7 +819,7 @@ public class SensorFixStoreAndLoadTest {
fixLoaderAndTracker.stop(true, /* willBeRemoved */ false);
statusTransitionListener.assertTransitions(TrackedRaceStatusEnum.PREPARED, TrackedRaceStatusEnum.TRACKING, TrackedRaceStatusEnum.LOADING, TrackedRaceStatusEnum.TRACKING, TrackedRaceStatusEnum.FINISHED);
}
@Test
/** Test for bug 4125 - https://bugzilla.sapsailing.com/bugzilla/show_bug.cgi?id=4125 */
public void testThatLoadingStateIsTriggeredWhenStartOfTrackingIsSet() throws InterruptedException {
@@ -2,19 +2,24 @@ package com.sap.sailing.domain.racelogtracking.test.impl;
import static junit.framework.Assert.assertEquals;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Consumer;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.Timeout;
import com.mongodb.MongoException;
import com.sap.sailing.domain.abstractlog.race.impl.RaceLogEndOfTrackingEventImpl;
import com.sap.sailing.domain.abstractlog.race.impl.RaceLogStartOfTrackingEventImpl;
import com.sap.sailing.domain.base.Boat;
import com.sap.sailing.domain.base.BoatClass;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Course;
@@ -42,22 +47,34 @@ import com.sap.sse.common.impl.TimeRangeImpl;
public class TrackedRaceLoadsFixesTest extends AbstractGPSFixStoreTest {
private static final Logger logger = Logger.getLogger(TrackedRaceLoadsFixesTest.class.getName());
private final BoatClass boatClass = DomainFactory.INSTANCE.getOrCreateBoatClass("49er");
private final BoatClass boatClass = DomainFactory.INSTANCE.getOrCreateBoatClass("49er");
private final Mark mark2 = DomainFactory.INSTANCE.getOrCreateMark("mark2");
private final Competitor comp2 = DomainFactory.INSTANCE.getOrCreateCompetitor("comp2", "comp2", null, null, null,
null, null, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null);
private final Competitor comp2 = DomainFactory.INSTANCE.getOrCreateCompetitor("comp2", "comp2", "c2", null, null, null,
null, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null);
private final Boat boat2 = DomainFactory.INSTANCE.getOrCreateBoat("boat2", "boat2", boatClass, "USA 123", null);
private final Course course = new CourseImpl("course", Arrays.asList(new WaypointImpl(mark), new WaypointImpl(mark2)));
private final RaceDefinition raceDefinition = new RaceDefinitionImpl("race", course, boatClass, Arrays.asList(comp, comp2));
private RaceDefinition raceDefinition;
@Before
public void setUp() throws UnknownHostException, MongoException {
Map<Competitor, Boat> competitorsAndBoats = new HashMap<>();
competitorsAndBoats.put(comp, boat);
competitorsAndBoats.put(comp2, boat2);
this.raceDefinition = new RaceDefinitionImpl("race", course, boatClass, competitorsAndBoats);
}
@Rule
public Timeout TrackedRaceLoadsFixesTestTimeout = new Timeout(3 * 60 * 1000);
@Test
public void doesRaceLoadOnlyBetweenStartAndEndOfTracking() throws TransformationException,
NoCorrespondingServiceRegisteredException, InterruptedException {
DeviceIdentifier markDevice = new SmartphoneImeiIdentifier("imei2");
defineMarksOnRegattaLog(mark, mark2);
map(comp, device, 0, 10000);
map(mark, markDevice, 0, 10000);
@@ -8,6 +8,8 @@ import static org.mockito.Mockito.mock;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import org.junit.Test;
@@ -18,6 +20,7 @@ import com.sap.sailing.domain.abstractlog.race.impl.RaceLogRaceStatusEventImpl;
import com.sap.sailing.domain.abstractlog.race.impl.RaceLogStartOfTrackingEventImpl;
import com.sap.sailing.domain.abstractlog.race.impl.RaceLogStartTimeEventImpl;
import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogDefineMarkEventImpl;
import com.sap.sailing.domain.base.Boat;
import com.sap.sailing.domain.base.BoatClass;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Course;
@@ -59,18 +62,22 @@ public class TrackedRaceStartTimeInferenceTest extends AbstractGPSFixStoreTest {
@Test
public void testStartTimeInferencePrecedenceOrder() throws TransformationException,
NoCorrespondingServiceRegisteredException, InterruptedException {
Competitor comp2 = DomainFactory.INSTANCE.getOrCreateCompetitor("comp2", "comp2", null, null, null, null, null,
Competitor comp2 = DomainFactory.INSTANCE.getOrCreateCompetitor("comp2", "comp2", "c2", null, null, null, null,
/* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null);
Boat boat2 = DomainFactory.INSTANCE.getOrCreateBoat("boat2", "boat2", boatClass, "USA 123", null);
Map<Competitor, Boat> competitorsAndBoats = new HashMap<>();
competitorsAndBoats.put(comp, boat);
competitorsAndBoats.put(comp2, boat2);
Mark mark2 = DomainFactory.INSTANCE.getOrCreateMark("mark2");
regattaLog.add(new RegattaLogDefineMarkEventImpl(new MillisecondsTimePoint(1), author, new MillisecondsTimePoint(1), 0, mark));
regattaLog.add(new RegattaLogDefineMarkEventImpl(new MillisecondsTimePoint(2), author, new MillisecondsTimePoint(1), 0, mark2));
Course course = new CourseImpl("course", Arrays.asList(new Waypoint[] { new WaypointImpl(mark),
new WaypointImpl(mark2) }));
RaceDefinition race = new RaceDefinitionImpl("race", course, boatClass, Arrays.asList(new Competitor[] { comp, comp2 }));
RaceDefinition race = new RaceDefinitionImpl("race", course, boatClass, competitorsAndBoats);
TrackedRegatta regatta = new DynamicTrackedRegattaImpl(new RegattaImpl(EmptyRaceLogStore.INSTANCE,
EmptyRegattaLogStore.INSTANCE, RegattaImpl.getDefaultName("regatta", boatClass.getName()), boatClass, /* startDate */
null, /* endDate */null, null, null, "a", null));
EmptyRegattaLogStore.INSTANCE, RegattaImpl.getDefaultName("regatta", boatClass.getName()), boatClass,
/* canBoatsOfCompetitorsChangePerRace */ true, /* startDate */ null, /* endDate */null, null, null, "a", null));
final DynamicTrackedRaceImpl trackedRace = new DynamicTrackedRaceImpl(regatta, race,
Collections.<Sideline> emptyList(), EmptyWindStore.INSTANCE, 0, 0, 0,
/* useMarkPassingCalculator */ false,
@@ -132,17 +139,21 @@ public class TrackedRaceStartTimeInferenceTest extends AbstractGPSFixStoreTest {
@Test
public void testStartTimeChangeNotificationForFirstUpdateThroughRaceLog() throws TransformationException,
NoCorrespondingServiceRegisteredException, InterruptedException {
Competitor comp2 = DomainFactory.INSTANCE.getOrCreateCompetitor("comp2", "comp2", null, null, null, null, null,
Competitor comp2 = DomainFactory.INSTANCE.getOrCreateCompetitor("comp2", "comp2", "c2", null, null, null, null,
/* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null);
Boat boat2 = DomainFactory.INSTANCE.getOrCreateBoat("boat2", "boat2", boatClass, "USA 123", null);
Map<Competitor, Boat> competitorsAndBoats = new HashMap<>();
competitorsAndBoats.put(comp, boat);
competitorsAndBoats.put(comp2, boat2);
Mark mark2 = DomainFactory.INSTANCE.getOrCreateMark("mark2");
regattaLog.add(new RegattaLogDefineMarkEventImpl(new MillisecondsTimePoint(1), author, new MillisecondsTimePoint(1), 0, mark));
regattaLog.add(new RegattaLogDefineMarkEventImpl(new MillisecondsTimePoint(2), author, new MillisecondsTimePoint(1), 0, mark2));
Course course = new CourseImpl("course", Arrays.asList(new Waypoint[] { new WaypointImpl(mark),
new WaypointImpl(mark2) }));
RaceDefinition race = new RaceDefinitionImpl("race", course, boatClass, Arrays.asList(new Competitor[] { comp, comp2 }));
RaceDefinition race = new RaceDefinitionImpl("race", course, boatClass, competitorsAndBoats);
TrackedRegatta regatta = new DynamicTrackedRegattaImpl(new RegattaImpl(EmptyRaceLogStore.INSTANCE,
EmptyRegattaLogStore.INSTANCE, RegattaImpl.getDefaultName("regatta", boatClass.getName()), boatClass, /* startDate */
null, /* endDate */null, null, null, "a", null));
EmptyRegattaLogStore.INSTANCE, RegattaImpl.getDefaultName("regatta", boatClass.getName()), boatClass,
/* canBoatsOfCompetitorsChangePerRace */ true, /* startDate */ null, /* endDate */null, null, null, "a", null));
final DynamicTrackedRaceImpl trackedRace = new DynamicTrackedRaceImpl(regatta, race,
Collections.<Sideline> emptyList(), EmptyWindStore.INSTANCE, 0, 0, 0,
/* useMarkPassingCalculator */ false,
@@ -170,17 +181,21 @@ public class TrackedRaceStartTimeInferenceTest extends AbstractGPSFixStoreTest {
@Test
public void testStartTimeChangeNotificationForFirstUpdateThroughStartMarkPassing() throws TransformationException,
NoCorrespondingServiceRegisteredException, InterruptedException {
Competitor comp2 = DomainFactory.INSTANCE.getOrCreateCompetitor("comp2", "comp2", null, null, null, null, null,
Competitor comp2 = DomainFactory.INSTANCE.getOrCreateCompetitor("comp2", "comp2", "c2", null, null, null, null,
/* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null);
Boat boat2 = DomainFactory.INSTANCE.getOrCreateBoat("boat2", "boat2", boatClass, "USA 123", null);
Map<Competitor, Boat> competitorsAndBoats = new HashMap<>();
competitorsAndBoats.put(comp, boat);
competitorsAndBoats.put(comp2, boat2);
Mark mark2 = DomainFactory.INSTANCE.getOrCreateMark("mark2");
regattaLog.add(new RegattaLogDefineMarkEventImpl(new MillisecondsTimePoint(1), author, new MillisecondsTimePoint(1), 0, mark));
regattaLog.add(new RegattaLogDefineMarkEventImpl(new MillisecondsTimePoint(2), author, new MillisecondsTimePoint(1), 0, mark2));
Course course = new CourseImpl("course", Arrays.asList(new Waypoint[] { new WaypointImpl(mark),
new WaypointImpl(mark2) }));
RaceDefinition race = new RaceDefinitionImpl("race", course, boatClass, Arrays.asList(new Competitor[] { comp, comp2 }));
RaceDefinition race = new RaceDefinitionImpl("race", course, boatClass, competitorsAndBoats);
TrackedRegatta regatta = new DynamicTrackedRegattaImpl(new RegattaImpl(EmptyRaceLogStore.INSTANCE,
EmptyRegattaLogStore.INSTANCE, RegattaImpl.getDefaultName("regatta", boatClass.getName()), boatClass, /* startDate */
null, /* endDate */null, null, null, "a", null));
EmptyRegattaLogStore.INSTANCE, RegattaImpl.getDefaultName("regatta", boatClass.getName()), boatClass,
/* canBoatsOfCompetitorsChangePerRace */ true, /* startDate */ null, /* endDate */null, null, null, "a", null));
final DynamicTrackedRaceImpl trackedRace = new DynamicTrackedRaceImpl(regatta, race,
Collections.<Sideline> emptyList(), EmptyWindStore.INSTANCE, 0, 0, 0,
/* useMarkPassingCalculator */ false,
@@ -211,17 +226,21 @@ public class TrackedRaceStartTimeInferenceTest extends AbstractGPSFixStoreTest {
@Test
public void testStartAndEndTrackingTimeInferencePrecedenceOrderTriggersListener() throws TransformationException,
NoCorrespondingServiceRegisteredException, InterruptedException {
Competitor comp2 = DomainFactory.INSTANCE.getOrCreateCompetitor("comp2", "comp2", null, null, null, null, null,
Competitor comp2 = DomainFactory.INSTANCE.getOrCreateCompetitor("comp2", "comp2", "c2", null, null, null, null,
/* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null);
Boat boat2 = DomainFactory.INSTANCE.getOrCreateBoat("boat2", "boat2", boatClass, "USA 123", null);
Map<Competitor, Boat> competitorsAndBoats = new HashMap<>();
competitorsAndBoats.put(comp, boat);
competitorsAndBoats.put(comp2, boat2);
Mark mark2 = DomainFactory.INSTANCE.getOrCreateMark("mark2");
regattaLog.add(new RegattaLogDefineMarkEventImpl(new MillisecondsTimePoint(1), author, new MillisecondsTimePoint(1), 0, mark));
regattaLog.add(new RegattaLogDefineMarkEventImpl(new MillisecondsTimePoint(2), author, new MillisecondsTimePoint(1), 0, mark2));
Course course = new CourseImpl("course", Arrays.asList(new Waypoint[] { new WaypointImpl(mark),
new WaypointImpl(mark2) }));
RaceDefinition race = new RaceDefinitionImpl("race", course, boatClass, Arrays.asList(new Competitor[] { comp, comp2 }));
RaceDefinition race = new RaceDefinitionImpl("race", course, boatClass, competitorsAndBoats);
TrackedRegatta regatta = new DynamicTrackedRegattaImpl(new RegattaImpl(EmptyRaceLogStore.INSTANCE,
EmptyRegattaLogStore.INSTANCE, RegattaImpl.getDefaultName("regatta", boatClass.getName()), boatClass, /* startDate */
null, /* endDate */null, null, null, "a", null));
EmptyRegattaLogStore.INSTANCE, RegattaImpl.getDefaultName("regatta", boatClass.getName()), boatClass,
/* canBoatsOfCompetitorsChangePerRace */ true, /* startDate */ null, /* endDate */null, null, null, "a", null));
assertTrue(regatta.getRegatta().useStartTimeInference());
final DynamicTrackedRaceImpl trackedRace = new DynamicTrackedRaceImpl(regatta, race,
Collections.<Sideline> emptyList(), EmptyWindStore.INSTANCE, 0, 0, 0,
@@ -321,17 +340,21 @@ public class TrackedRaceStartTimeInferenceTest extends AbstractGPSFixStoreTest {
@Test
public void testStartAndEndOfTrackingTimeChangeNotificationForFirstUpdateThroughRaceLog() throws TransformationException,
NoCorrespondingServiceRegisteredException, InterruptedException {
Competitor comp2 = DomainFactory.INSTANCE.getOrCreateCompetitor("comp2", "comp2", null, null, null, null, null,
Competitor comp2 = DomainFactory.INSTANCE.getOrCreateCompetitor("comp2", "comp2", "c2", null, null, null, null,
/* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null);
Boat boat2 = DomainFactory.INSTANCE.getOrCreateBoat("boat2", "boat2", boatClass, "USA 123", null);
Map<Competitor, Boat> competitorsAndBoats = new HashMap<>();
competitorsAndBoats.put(comp, boat);
competitorsAndBoats.put(comp2, boat2);
Mark mark2 = DomainFactory.INSTANCE.getOrCreateMark("mark2");
regattaLog.add(new RegattaLogDefineMarkEventImpl(new MillisecondsTimePoint(1), author, new MillisecondsTimePoint(1), 0, mark));
regattaLog.add(new RegattaLogDefineMarkEventImpl(new MillisecondsTimePoint(2), author, new MillisecondsTimePoint(1), 0, mark2));
Course course = new CourseImpl("course", Arrays.asList(new Waypoint[] { new WaypointImpl(mark),
new WaypointImpl(mark2) }));
RaceDefinition race = new RaceDefinitionImpl("race", course, boatClass, Arrays.asList(new Competitor[] { comp, comp2 }));
RaceDefinition race = new RaceDefinitionImpl("race", course, boatClass, competitorsAndBoats);
TrackedRegatta regatta = new DynamicTrackedRegattaImpl(new RegattaImpl(EmptyRaceLogStore.INSTANCE,
EmptyRegattaLogStore.INSTANCE, RegattaImpl.getDefaultName("regatta", boatClass.getName()), boatClass, /* startDate */
null, /* endDate */null, null, null, "a", null));
EmptyRegattaLogStore.INSTANCE, RegattaImpl.getDefaultName("regatta", boatClass.getName()), boatClass,
/* canBoatsOfCompetitorsChangePerRace */ true, /* startDate */null, /* endDate */null, null, null, "a", null));
final DynamicTrackedRaceImpl trackedRace = new DynamicTrackedRaceImpl(regatta, race,
Collections.<Sideline> emptyList(), EmptyWindStore.INSTANCE, 0, 0, 0,
/* useMarkPassingCalculator */ false,
@@ -1,6 +1,7 @@
package com.sap.sailing.domain.racelogtracking.impl;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogDeviceBoatBravoMappingEventImpl;
import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogDeviceCompetitorBravoMappingEventImpl;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.common.tracking.BravoFix;
@@ -31,6 +32,7 @@ public class BravoDataFixMapper implements SensorFixMapper<BravoFix, DynamicSens
@Override
public boolean isResponsibleFor(Class<? extends RegattaLogDeviceMappingEvent<?>> eventType) {
return RegattaLogDeviceCompetitorBravoMappingEventImpl.class.isAssignableFrom(eventType);
return RegattaLogDeviceCompetitorBravoMappingEventImpl.class.isAssignableFrom(eventType)
|| RegattaLogDeviceBoatBravoMappingEventImpl.class.isAssignableFrom(eventType);
}
}
@@ -1,6 +1,7 @@
package com.sap.sailing.domain.racelogtracking.impl;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogDeviceBoatBravoExtendedMappingEventImpl;
import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogDeviceCompetitorBravoExtendedMappingEventImpl;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.common.tracking.BravoExtendedFix;
@@ -32,6 +33,7 @@ public class BravoExtendedDataFixMapper implements SensorFixMapper<BravoFix, Dyn
@Override
public boolean isResponsibleFor(Class<? extends RegattaLogDeviceMappingEvent<?>> eventType) {
return RegattaLogDeviceCompetitorBravoExtendedMappingEventImpl.class.isAssignableFrom(eventType);
return RegattaLogDeviceCompetitorBravoExtendedMappingEventImpl.class.isAssignableFrom(eventType)
|| RegattaLogDeviceBoatBravoExtendedMappingEventImpl.class.isAssignableFrom(eventType);
}
}
@@ -1,6 +1,7 @@
package com.sap.sailing.domain.racelogtracking.impl;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogDeviceBoatExpeditionExtendedMappingEventImpl;
import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogDeviceCompetitorExpeditionExtendedMappingEventImpl;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.common.tracking.BravoExtendedFix;
@@ -36,6 +37,7 @@ public class ExpeditionExtendedDataFixMapper implements SensorFixMapper<BravoFix
@Override
public boolean isResponsibleFor(Class<? extends RegattaLogDeviceMappingEvent<?>> eventType) {
return RegattaLogDeviceCompetitorExpeditionExtendedMappingEventImpl.class.isAssignableFrom(eventType);
return RegattaLogDeviceCompetitorExpeditionExtendedMappingEventImpl.class.isAssignableFrom(eventType)
|| RegattaLogDeviceBoatExpeditionExtendedMappingEventImpl.class.isAssignableFrom(eventType);
}
}
@@ -19,6 +19,7 @@ import com.sap.sailing.domain.base.impl.RegattaImpl;
import com.sap.sailing.domain.common.racelog.tracking.DoesNotHaveRegattaLogException;
import com.sap.sailing.domain.common.racelog.tracking.RaceNotCreatedException;
import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.leaderboard.LeaderboardGroupResolver;
import com.sap.sailing.domain.regattalike.HasRegattaLike;
import com.sap.sailing.domain.tracking.DynamicTrackedRegatta;
import com.sap.sailing.domain.tracking.RaceTracker;
@@ -64,13 +65,13 @@ public class RaceLogConnectivityParams extends AbstractRaceTrackingConnectivityP
@Override
public RaceTracker createRaceTracker(TrackedRegattaRegistry trackedRegattaRegistry, WindStore windStore,
RaceLogResolver raceLogResolver, long timeoutInMilliseconds) {
return createRaceTracker(regatta, trackedRegattaRegistry, windStore, raceLogResolver, timeoutInMilliseconds);
RaceLogResolver raceLogResolver, LeaderboardGroupResolver leaderboardGroupResolver, long timeoutInMilliseconds) {
return createRaceTracker(regatta, trackedRegattaRegistry, windStore, raceLogResolver, leaderboardGroupResolver, timeoutInMilliseconds);
}
@Override
public RaceTracker createRaceTracker(Regatta regatta, TrackedRegattaRegistry trackedRegattaRegistry,
WindStore windStore, RaceLogResolver raceLogResolver, long timeoutInMilliseconds) {
WindStore windStore, RaceLogResolver raceLogResolver, LeaderboardGroupResolver leaderboardGroupResolver, long timeoutInMilliseconds) {
if (regatta == null) {
BoatClass boatClass = new RaceInformationFinder(getRaceLog()).analyze().getBoatClass();
regatta = service.getOrCreateDefaultRegatta(
@@ -36,6 +36,7 @@ import com.sap.sailing.domain.abstractlog.regatta.RegattaLog;
import com.sap.sailing.domain.abstractlog.regatta.RegattaLogEventVisitor;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDefineMarkEvent;
import com.sap.sailing.domain.abstractlog.regatta.impl.BaseRegattaLogEventVisitor;
import com.sap.sailing.domain.base.Boat;
import com.sap.sailing.domain.base.BoatClass;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.ControlPoint;
@@ -296,9 +297,9 @@ public class RaceLogRaceTracker extends AbstractRaceTrackerBaseImpl {
}
throw new RaceNotCreatedException(String.format("Race for racelog (%s) has already been created", raceLog));
}
Iterable<Competitor> competitors = raceColumn.getAllCompetitors(params.getFleet());
Map<Competitor, Boat> competitorsAndTheirBoats = raceColumn.getAllCompetitorsAndTheirBoats(params.getFleet());
Serializable raceId = denoteEvent.getRaceId();
final RaceDefinition raceDef = new RaceDefinitionImpl(raceName, course, boatClass, competitors, raceId);
final RaceDefinition raceDef = new RaceDefinitionImpl(raceName, course, boatClass, competitorsAndTheirBoats, raceId);
Iterable<Sideline> sidelines = Collections.<Sideline> emptyList();
// set race definition, so race is linked to leaderboard automatically
trackedRegatta.getRegatta().addRace(raceDef);
@@ -4,6 +4,7 @@ import java.io.IOException;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.logging.Level;
@@ -27,6 +28,7 @@ import com.sap.sailing.domain.abstractlog.race.tracking.impl.RaceLogStartTrackin
import com.sap.sailing.domain.abstractlog.regatta.RegattaLog;
import com.sap.sailing.domain.abstractlog.regatta.RegattaLogEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogDeviceMarkMappingEventImpl;
import com.sap.sailing.domain.base.Boat;
import com.sap.sailing.domain.base.BoatClass;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.CourseBase;
@@ -138,13 +140,13 @@ public class RaceLogTrackingAdapterImpl implements RaceLogTrackingAdapter {
RegattaLeaderboard rLeaderboard = (RegattaLeaderboard) leaderboard;
boatClass = rLeaderboard.getRegatta().getBoatClass();
} else {
if (!Util.isEmpty(raceColumn.getAllCompetitors(fleet))) {
boatClass = findDominatingBoatClass(raceColumn.getAllCompetitors(fleet));
} else if (!Util.isEmpty(raceColumn.getAllCompetitors())) {
boatClass = findDominatingBoatClass(raceColumn.getAllCompetitors());
if (!Util.isEmpty(raceColumn.getAllCompetitorsAndTheirBoats(fleet).values())) {
boatClass = findDominatingBoatClass(raceColumn.getAllCompetitorsAndTheirBoats(fleet).values());
} else if (!Util.isEmpty(raceColumn.getAllCompetitorsAndTheirBoats().values())) {
boatClass = findDominatingBoatClass(raceColumn.getAllCompetitorsAndTheirBoats().values());
} else if (!Util.isEmpty(leaderboard.getAllCompetitors())) {
boatClass = findDominatingBoatClass(leaderboard.getAllCompetitors());
} else {
boatClass = leaderboard.getBoatClass();
} else {
throw new NotDenotableForRaceLogTrackingException("Couldn't infer boat class, no competitors on race and leaderboard");
}
}
@@ -167,8 +169,8 @@ public class RaceLogTrackingAdapterImpl implements RaceLogTrackingAdapter {
return result;
}
private BoatClass findDominatingBoatClass(Iterable<Competitor> allCompetitors) {
return Util.getDominantObject(()->StreamSupport.stream(allCompetitors.spliterator(), /* parallel */ false).map(c->c.getBoat().getBoatClass()).iterator());
private BoatClass findDominatingBoatClass(Iterable<Boat> allBoats) {
return Util.getDominantObject(()->StreamSupport.stream(allBoats.spliterator(), /* parallel */ false).map(b->b.getBoatClass()).iterator());
}
@Override
@@ -228,16 +230,16 @@ public class RaceLogTrackingAdapterImpl implements RaceLogTrackingAdapter {
@Override
public void copyCompetitors(final RaceColumn fromRaceColumn, final Fleet fromFleet, final Iterable<Pair<RaceColumn, Fleet>> toRaces) {
Iterable<Competitor> competitorsToCopy = fromRaceColumn.getAllCompetitors(fromFleet);
Map<Competitor, Boat> competitorsAndBoatsToCopy = fromRaceColumn.getAllCompetitorsAndTheirBoats(fromFleet);
for (Pair<RaceColumn, Fleet> toRace : toRaces) {
final RaceColumn toRaceColumn = toRace.getA();
final Fleet toFleet = toRace.getB();
try {
if (toRaceColumn.isCompetitorRegistrationInRacelogEnabled(toFleet)) {
toRaceColumn.registerCompetitors(competitorsToCopy, toFleet);
toRaceColumn.registerCompetitors(competitorsAndBoatsToCopy, toFleet);
} else {
toRaceColumn.enableCompetitorRegistrationOnRaceLog(toFleet);
toRaceColumn.registerCompetitors(competitorsToCopy, toFleet);
toRaceColumn.registerCompetitors(competitorsAndBoatsToCopy, toFleet);
}
} catch (CompetitorRegistrationOnRaceLogDisabledException e1) {
// cannot happen as we explicitly checked successfully before, or enabled it when the check failed; still produce a log documenting this strangeness:
@@ -16,10 +16,13 @@ import java.util.logging.Logger;
import com.sap.sailing.domain.abstractlog.regatta.MappingEventVisitor;
import com.sap.sailing.domain.abstractlog.regatta.RegattaLog;
import com.sap.sailing.domain.abstractlog.regatta.RegattaLogEventVisitor;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceBoatMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceBoatSensorDataMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceCompetitorMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceCompetitorSensorDataMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMarkMappingEvent;
import com.sap.sailing.domain.base.Boat;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Mark;
import com.sap.sailing.domain.base.Regatta;
@@ -170,102 +173,139 @@ public class FixLoaderAndTracker implements TrackingDataLoader {
public void fixReceived(DeviceIdentifier device, Timed fix) {
if (!preemptiveStopRequested.get() && trackedRace.getStartOfTracking() != null) {
final TimePoint timePoint = fix.getTimePoint();
deviceMappings.forEachMappingOfDeviceIncludingTimePoint(device, fix.getTimePoint(), (mapping) -> {
mapping.getRegattaLogEvent().accept(new MappingEventVisitor() {
@Override
public void visit(RegattaLogDeviceCompetitorSensorDataMappingEvent event) {
if (preemptiveStopRequested.get()) {
return;
deviceMappings.forEachMappingOfDeviceIncludingTimePoint(device, fix.getTimePoint(), new Consumer<DeviceMappingWithRegattaLogEvent<WithID>>() {
@Override
public void accept(DeviceMappingWithRegattaLogEvent<WithID> mapping) {
mapping.getRegattaLogEvent().accept(new MappingEventVisitor() {
@Override
public void visit(RegattaLogDeviceCompetitorSensorDataMappingEvent event) {
recordSensorFixForCompetitor(event.getMappedTo(), event);
}
SensorFixMapper<SensorFix, DynamicSensorFixTrack<Competitor, SensorFix>, Competitor> mapper = sensorFixMapperFactory
.createCompetitorMapper(event.getClass());
DynamicSensorFixTrack<Competitor, SensorFix> track = mapper.getTrack(trackedRace,
event.getMappedTo());
if (track != null && trackedRace.isWithinStartAndEndOfTracking(fix.getTimePoint())) {
mapper.addFix(track, (DoubleVectorFix) fix);
@Override
public void visit(RegattaLogDeviceBoatSensorDataMappingEvent event) {
final Boat boat = event.getMappedTo();
final Competitor competitor = trackedRace.getCompetitorOfBoat(boat);
if (competitor != null) {
recordSensorFixForCompetitor(competitor, event);
} else {
logger.log(Level.WARNING,
"Could not record fix for boat because no competitor could be determined. Boat: "
+ boat);
}
}
}
@Override
public void visit(RegattaLogDeviceCompetitorMappingEvent event) {
if (preemptiveStopRequested.get()) {
return;
private void recordSensorFixForCompetitor(Competitor competitor, RegattaLogDeviceMappingEvent<?> event) {
if (preemptiveStopRequested.get()) {
return;
}
@SuppressWarnings("unchecked")
SensorFixMapper<SensorFix, DynamicSensorFixTrack<Competitor, SensorFix>, Competitor> mapper = sensorFixMapperFactory
.createCompetitorMapper((Class<? extends RegattaLogDeviceMappingEvent<?>>) event.getClass());
DynamicSensorFixTrack<Competitor, SensorFix> track = mapper.getTrack(trackedRace,
competitor);
if (track != null && trackedRace.isWithinStartAndEndOfTracking(fix.getTimePoint())) {
mapper.addFix(track, (DoubleVectorFix) fix);
}
}
Competitor comp = event.getMappedTo();
if (fix instanceof GPSFixMoving) {
trackedRace.recordFix(comp, (GPSFixMoving) fix);
} else {
logger.log(Level.WARNING,
String.format(
"Could not add fix for competitor (%s) in race (%s), as it"
+ " is no GPSFixMoving, meaning it is missing COG/SOG values",
comp, trackedRace.getRace().getName()));
@Override
public void visit(RegattaLogDeviceCompetitorMappingEvent event) {
recordForCompetitor(event.getMappedTo());
}
}
@Override
public void visit(RegattaLogDeviceMarkMappingEvent event) {
if (preemptiveStopRequested.get()) {
return;
@Override
public void visit(RegattaLogDeviceBoatMappingEvent event) {
final Boat boat = event.getMappedTo();
final Competitor comp = trackedRace.getCompetitorOfBoat(boat);
if (comp != null) {
recordForCompetitor(comp);
} else {
logger.log(Level.WARNING,
"Could not record fix for boat because no competitor could be determined. Boat: "
+ boat);
}
}
Mark mark = event.getMappedTo();
final DynamicGPSFixTrack<Mark, GPSFix> markTrack = trackedRace.getOrCreateTrack(mark);
final GPSFix firstFixAtOrAfter;
final boolean forceFix;
if (trackedRace.isWithinStartAndEndOfTracking(fix.getTimePoint())) {
forceFix = false;
} else {
markTrack.lockForRead();
try {
if (Util.isEmpty(markTrack.getRawFixes())
|| (firstFixAtOrAfter = markTrack.getFirstFixAtOrAfter(timePoint)) != null
&& firstFixAtOrAfter.getTimePoint().equals(timePoint)) {
// either the first fix or overwriting an existing one
forceFix = true;
} else {
// checking if the given fix is "better" than an existing one
TimePoint startOfTracking = trackedRace.getStartOfTracking();
TimePoint endOfTracking = trackedRace.getStartOfTracking();
if (startOfTracking != null) {
GPSFix fixAfterStartOfTracking = markTrack
.getFirstFixAtOrAfter(startOfTracking);
if (fixAfterStartOfTracking == null
|| !trackedRace.isWithinStartAndEndOfTracking(
fixAfterStartOfTracking.getTimePoint())) {
// There is no fix in the tracking interval, so this fix could be "better"
// than ones already available in the track
// Better means closer before/after the beginning/end of the tracking
// interval
if (timePoint.before(startOfTracking)) {
// check if it is closer to the beginning of the tracking interval
GPSFix fixBeforeStartOfTracking = markTrack
.getLastFixAtOrBefore(startOfTracking);
forceFix = (fixBeforeStartOfTracking == null
|| fixBeforeStartOfTracking.getTimePoint().before(timePoint));
} else if (endOfTracking != null && timePoint.after(endOfTracking)) {
// check if it is closer to the end of the tracking interval
GPSFix fixAfterEndOfTracking = markTrack
.getFirstFixAtOrAfter(endOfTracking);
forceFix = (fixAfterEndOfTracking == null
|| fixAfterEndOfTracking.getTimePoint().after(timePoint));
public void recordForCompetitor(Competitor comp) {
if (preemptiveStopRequested.get()) {
return;
}
if (fix instanceof GPSFixMoving) {
trackedRace.recordFix(comp, (GPSFixMoving) fix);
} else {
logger.log(Level.WARNING,
String.format(
"Could not add fix for competitor (%s) in race (%s), as it"
+ " is no GPSFixMoving, meaning it is missing COG/SOG values",
comp, trackedRace.getRace().getName()));
}
}
@Override
public void visit(RegattaLogDeviceMarkMappingEvent event) {
if (preemptiveStopRequested.get()) {
return;
}
Mark mark = event.getMappedTo();
final DynamicGPSFixTrack<Mark, GPSFix> markTrack = trackedRace.getOrCreateTrack(mark);
final GPSFix firstFixAtOrAfter;
final boolean forceFix;
if (trackedRace.isWithinStartAndEndOfTracking(fix.getTimePoint())) {
forceFix = false;
} else {
markTrack.lockForRead();
try {
if (Util.isEmpty(markTrack.getRawFixes())
|| (firstFixAtOrAfter = markTrack.getFirstFixAtOrAfter(timePoint)) != null
&& firstFixAtOrAfter.getTimePoint().equals(timePoint)) {
// either the first fix or overwriting an existing one
forceFix = true;
} else {
// checking if the given fix is "better" than an existing one
TimePoint startOfTracking = trackedRace.getStartOfTracking();
TimePoint endOfTracking = trackedRace.getStartOfTracking();
if (startOfTracking != null) {
GPSFix fixAfterStartOfTracking = markTrack
.getFirstFixAtOrAfter(startOfTracking);
if (fixAfterStartOfTracking == null
|| !trackedRace.isWithinStartAndEndOfTracking(
fixAfterStartOfTracking.getTimePoint())) {
// There is no fix in the tracking interval, so this fix could be "better"
// than ones already available in the track
// Better means closer before/after the beginning/end of the tracking
// interval
if (timePoint.before(startOfTracking)) {
// check if it is closer to the beginning of the tracking interval
GPSFix fixBeforeStartOfTracking = markTrack
.getLastFixAtOrBefore(startOfTracking);
forceFix = (fixBeforeStartOfTracking == null
|| fixBeforeStartOfTracking.getTimePoint().before(timePoint));
} else if (endOfTracking != null && timePoint.after(endOfTracking)) {
// check if it is closer to the end of the tracking interval
GPSFix fixAfterEndOfTracking = markTrack
.getFirstFixAtOrAfter(endOfTracking);
forceFix = (fixAfterEndOfTracking == null
|| fixAfterEndOfTracking.getTimePoint().after(timePoint));
} else {
forceFix = false;
}
} else {
// there is already a fix in the tracking interval
forceFix = false;
}
} else {
// there is already a fix in the tracking interval
forceFix = false;
}
} else {
forceFix = false;
}
} finally {
markTrack.unlockAfterRead();
}
} finally {
markTrack.unlockAfterRead();
}
trackedRace.recordFix(mark, (GPSFix) fix, /* only when in tracking interval */ !forceFix);
}
trackedRace.recordFix(mark, (GPSFix) fix, /* only when in tracking interval */ !forceFix);
}
});
});
}
});
}
}
@@ -378,9 +418,30 @@ public class FixLoaderAndTracker implements TrackingDataLoader {
mappingEvent.accept(new MappingEventVisitor() {
@Override
public void visit(RegattaLogDeviceCompetitorSensorDataMappingEvent event) {
final Competitor competitor = event.getMappedTo();
loadSensorFixesForCompetitor(timeRangeToLoad, progressConsumer, stopCallback, event, competitor);
}
@Override
public void visit(RegattaLogDeviceBoatSensorDataMappingEvent event) {
final Boat boat = event.getMappedTo();
final Competitor competitor = trackedRace.getCompetitorOfBoat(boat);
if (competitor != null) {
loadSensorFixesForCompetitor(timeRangeToLoad, progressConsumer, stopCallback, event, competitor);
} else {
logger.log(Level.WARNING,
"Could not load fixes for boat because no competitor could be determined. Boat: "
+ boat);
}
}
private void loadSensorFixesForCompetitor(TimeRange timeRangeToLoad, final Consumer<Double> progressConsumer,
final BooleanSupplier stopCallback, RegattaLogDeviceMappingEvent<?> event,
final Competitor competitor) {
@SuppressWarnings("unchecked")
SensorFixMapper<Timed, DynamicTrack<Timed>, Competitor> mapper = sensorFixMapperFactory
.createCompetitorMapper(event.getClass());
DynamicTrack<Timed> track = mapper.getTrack(trackedRace, event.getMappedTo());
.createCompetitorMapper((Class<? extends RegattaLogDeviceMappingEvent<?>>) event.getClass());
DynamicTrack<Timed> track = mapper.getTrack(trackedRace, competitor);
if (track != null) {
// for split-fleet racing, device mappings coming from the regatta log may not be relevant
// for the trackedRace because the competitors may not compete in it; in this case, the
@@ -398,7 +459,24 @@ public class FixLoaderAndTracker implements TrackingDataLoader {
@Override
public void visit(RegattaLogDeviceCompetitorMappingEvent event) {
DynamicGPSFixTrack<Competitor, GPSFixMoving> track = trackedRace.getTrack(event.getMappedTo());
loadForCompetitor(event.getMappedTo(), event);
}
@Override
public void visit(RegattaLogDeviceBoatMappingEvent event) {
final Boat boat = event.getMappedTo();
final Competitor comp = trackedRace.getCompetitorOfBoat(boat);
if (comp != null) {
loadForCompetitor(comp, event);
} else {
logger.log(Level.WARNING,
"Could not load fixes for boat because no competitor could be determined. Boat: "
+ boat);
}
}
private void loadForCompetitor(Competitor competitor, RegattaLogDeviceMappingEvent<?> event) {
DynamicGPSFixTrack<Competitor, GPSFixMoving> track = trackedRace.getTrack(competitor);
if (track != null) {
// for split-fleet racing, device mappings coming from the regatta log may not be relevant
// for the trackedRace because the competitors may not compete in it; in this case, the
@@ -408,7 +486,7 @@ public class FixLoaderAndTracker implements TrackingDataLoader {
timeRangeToLoad.from(), timeRangeToLoad.to(), /* toIsInclusive */ false,
stopCallback, progressConsumer);
} catch (TransformationException | NoCorrespondingServiceRegisteredException e) {
logger.log(Level.WARNING, "Could not load competitor track " + event.getMappedTo() + "; device "
logger.log(Level.WARNING, "Could not load competitor track " + competitor + "; device "
+ event.getDevice());
}
}
@@ -441,11 +519,21 @@ public class FixLoaderAndTracker implements TrackingDataLoader {
throw new UnsupportedOperationException();
}
@Override
public void visit(RegattaLogDeviceBoatSensorDataMappingEvent event) {
throw new UnsupportedOperationException();
}
@Override
public void visit(RegattaLogDeviceCompetitorMappingEvent event) {
throw new UnsupportedOperationException();
}
@Override
public void visit(RegattaLogDeviceBoatMappingEvent event) {
throw new UnsupportedOperationException();
}
@Override
public void visit(RegattaLogDeviceMarkMappingEvent event) {
DynamicGPSFixTrack<Mark, GPSFix> track = trackedRace.getOrCreateTrack(event.getMappedTo());
@@ -15,6 +15,8 @@ import java.util.logging.Logger;
import com.sap.sailing.domain.abstractlog.regatta.RegattaLog;
import com.sap.sailing.domain.abstractlog.regatta.RegattaLogEventVisitor;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogCloseOpenEndedDeviceMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceBoatMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceBoatSensorDataMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceCompetitorMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceCompetitorSensorDataMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMappingEvent;
@@ -70,7 +72,12 @@ public abstract class RegattaLogDeviceMappings<ItemT extends WithID> {
private final RegattaLogEventVisitor regattaLogEventVisitor = new BaseRegattaLogEventVisitor() {
@Override
public void visit(RegattaLogDeviceCompetitorSensorDataMappingEvent event) {
logger.log(Level.FINE, "New mapping for: " + event.getMappedTo() + "; device: " + event.getDevice());
logger.log(Level.FINE, "New CompetitorSensorDataMapping for: " + event.getMappedTo() + "; device: " + event.getDevice());
updateMappings();
}
@Override
public void visit(RegattaLogDeviceBoatSensorDataMappingEvent event) {
logger.log(Level.FINE, "New BoatSensorDataMapping for: " + event.getMappedTo() + "; device: " + event.getDevice());
updateMappings();
}
@@ -87,6 +94,12 @@ public abstract class RegattaLogDeviceMappings<ItemT extends WithID> {
"New DeviceCompetitorMapping for: " + event.getMappedTo() + "; device: " + event.getDevice());
updateMappings();
}
public void visit(RegattaLogDeviceBoatMappingEvent event) {
logger.log(Level.FINE,
"New DeviceBoatMappingEvent for: " + event.getMappedTo() + "; device: " + event.getDevice());
updateMappings();
}
@Override
public void visit(RegattaLogCloseOpenEndedDeviceMappingEvent event) {
@@ -65,7 +65,7 @@ public class CurrentRaceFilterImplTest {
_505RaceRows.add(_505RaceRow);
_505Series.add(new SeriesWithRowsImpl("Default", /* isMedal */ false, /* isFleetsCanRunInParallel */ false, _505RaceRows));
final RaceGroup _505 = new RaceGroupImpl("505", /* displayName */ null,
new BoatClassImpl(BoatClassMasterdata._5O5.getDisplayName(), BoatClassMasterdata._5O5),
new BoatClassImpl(BoatClassMasterdata._5O5.getDisplayName(), BoatClassMasterdata._5O5), /* canBoatsOfCompetitorsChangePerRace */ false,
/* courseArea */ null, _505Series, /* regattaConfiguration */ null);
return _505;
}
@@ -75,7 +75,7 @@ public class CurrentRaceFilterImplTest {
final List<RaceRow> yesRaceRows = Arrays.asList(createRaceRow(1, 9, "R", "Yellow"), createRaceRow(1, 9, "R", "Blue"));
yesSeries.add(new SeriesWithRowsImpl("Default", /* isMedal */ false, /* isFleetsCanRunInParallel */ true, yesRaceRows));
final RaceGroup yes = new RaceGroupImpl("YES", /* displayName */ null,
new BoatClassImpl(BoatClassMasterdata._470.getDisplayName(), BoatClassMasterdata._470),
new BoatClassImpl(BoatClassMasterdata._470.getDisplayName(), BoatClassMasterdata._470), /* canBoatsOfCompetitorsChangePerRace */ false,
/* courseArea */ null, yesSeries, /* regattaConfiguration */ null);
return yes;
}
@@ -89,7 +89,7 @@ public class CurrentRaceFilterImplTest {
final List<RaceRow> isafMedalRaceRows = Arrays.asList(createRaceRow(1, 1, "M", "Medal"));
isafSeries.add(new SeriesWithRowsImpl("Medal", /* isMedal */ true, /* isFleetsCanRunInParallel */ true, isafMedalRaceRows));
final RaceGroup isaf = new RaceGroupImpl("ISAF", /* displayName */ null,
new BoatClassImpl(BoatClassMasterdata._470.getDisplayName(), BoatClassMasterdata._470),
new BoatClassImpl(BoatClassMasterdata._470.getDisplayName(), BoatClassMasterdata._470), /* canBoatsOfCompetitorsChangePerRace */ false,
/* courseArea */ null, isafSeries, /* regattaConfiguration */ null);
return isaf;
}
@@ -109,7 +109,7 @@ public class CurrentRaceFilterImplTest {
final List<RaceRow> essDay4RaceRows = Arrays.asList(createRaceRow(16, 5, "R", "Default"));
essSeries.add(new SeriesWithRowsImpl("Day 4", /* isMedal */ false, /* isFleetsCanRunInParallel */ true, essDay4RaceRows));
final RaceGroup ess = new RaceGroupImpl("ESS", /* displayName */ null,
new BoatClassImpl(BoatClassMasterdata.EXTREME_40.getDisplayName(), BoatClassMasterdata.EXTREME_40),
new BoatClassImpl(BoatClassMasterdata.EXTREME_40.getDisplayName(), BoatClassMasterdata.EXTREME_40), /* canBoatsOfCompetitorsChangePerRace */ false,
/* courseArea */ null, essSeries, /* regattaConfiguration */ null);
return ess;
}
@@ -119,7 +119,7 @@ public class CurrentRaceFilterImplTest {
final List<RaceRow> leagueRaceRows = Arrays.asList(createRaceRow(1, 15, "F", "Red"), createRaceRow(1, 15, "F", "Green"), createRaceRow(1, 15, "F", "Blue"));
leagueSeries.add(new SeriesWithRowsImpl("Default", /* isMedal */ false, /* isFleetsCanRunInParallel */ false, leagueRaceRows));
final RaceGroup league = new RaceGroupImpl("League", /* displayName */ null,
new BoatClassImpl(BoatClassMasterdata.J70.getDisplayName(), BoatClassMasterdata.J70),
new BoatClassImpl(BoatClassMasterdata.J70.getDisplayName(), BoatClassMasterdata.J70), /* canBoatsOfCompetitorsChangePerRace */ false,
/* courseArea */ null, leagueSeries, /* regattaConfiguration */ null);
return league;
}
@@ -14,13 +14,21 @@ import com.sap.sailing.domain.abstractlog.regatta.RegattaLogEventVisitor;
import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogRegisterCompetitorEventImpl;
import com.sap.sailing.domain.abstractlog.shared.analyzing.CompetitorDeregistrator;
import com.sap.sailing.domain.abstractlog.shared.analyzing.CompetitorsInLogAnalyzer;
import com.sap.sailing.domain.base.BoatClass;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.impl.CompetitorImpl;
import com.sap.sailing.domain.base.CompetitorWithBoat;
import com.sap.sailing.domain.base.impl.BoatClassImpl;
import com.sap.sailing.domain.base.impl.BoatImpl;
import com.sap.sailing.domain.base.impl.CompetitorWithBoatImpl;
import com.sap.sailing.domain.base.impl.DynamicBoat;
import com.sap.sse.common.impl.MillisecondsTimePoint;
public class CompetitorDeregistrationTest extends AbstractRegattaLogTrackingTest {
private final Competitor competitor = new CompetitorImpl("comp", "Comp", null, null, null, null, null, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null);
private final Competitor competitor2 = new CompetitorImpl("comp2", "Comp2", null, null, null, null, null, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null);
private final static BoatClass boatClass = new BoatClassImpl("505", /* typicallyStartsUpwind */ true);
private final static DynamicBoat boat1 = new BoatImpl("id12345", "boat1", boatClass, /* sailID */ null);
private final static DynamicBoat boat2 = new BoatImpl("id12345", "boat1", boatClass, /* sailID */ null);
private final CompetitorWithBoat competitor = new CompetitorWithBoatImpl("comp", "Comp", "KYC", null, null, null, null, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null, boat1);
private final CompetitorWithBoat competitor2 = new CompetitorWithBoatImpl("comp2", "Comp2", "KYC", null, null, null, null, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null, boat2);
@Test
public void testCompetitorDeregistration() {
@@ -27,8 +27,8 @@ import com.sap.sse.common.impl.MillisecondsTimePoint;
import junit.framework.Assert;
public class DeviceMappingFinderTest extends AbstractRegattaLogTrackingTest {
private final Competitor competitor = new CompetitorImpl("comp", "Comp", null, null, null, null, null, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null);
private final Competitor competitor2 = new CompetitorImpl("comp2", "Comp2", null, null, null, null, null, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null);
private final Competitor competitor = new CompetitorImpl("comp", "Comp", "KYC", null, null, null, null, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null);
private final Competitor competitor2 = new CompetitorImpl("comp2", "Comp2", "KYC", null, null, null, null, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null);
private final DeviceIdentifier device = new SmartphoneImeiIdentifier("imei");
private int time = 0;
@@ -23,5 +23,5 @@ public interface RaceLogEventData extends Serializable {
* A {@link RaceLogEventData} might be associated with a list of competitors, which are somehow relevant for this
* kind of event. An example is a list of competitors who are marked for an individual recall.
*/
List<Competitor> getInvolvedBoats();
List<Competitor> getInvolvedCompetitors();
}
@@ -37,7 +37,7 @@ public interface RaceLogEventVisitor {
public void visit(RaceLogDenoteForTrackingEvent event);
public void visit(RaceLogStartTrackingEvent event);
public void visit(RaceLogRegisterCompetitorEvent event);
public void visit(RaceLogAdditionalScoringInformationEvent additionalScoringInformation);
@@ -23,11 +23,11 @@ public class MarkPassingDataFinder extends RaceLogAnalyzer<Set<Triple<Competitor
for (RaceLogEvent event : getLog().getUnrevokedEvents()) {
if (event instanceof RaceLogFixedMarkPassingEvent) {
RaceLogFixedMarkPassingEvent castedEvent = (RaceLogFixedMarkPassingEvent) event;
result.add(new Triple<Competitor, Integer, TimePoint>(castedEvent.getInvolvedBoats().get(0), castedEvent
result.add(new Triple<Competitor, Integer, TimePoint>(castedEvent.getInvolvedCompetitors().get(0), castedEvent
.getZeroBasedIndexOfPassedWaypoint(), castedEvent.getTimePointOfFixedPassing()));
} else if (event instanceof RaceLogSuppressedMarkPassingsEvent){
RaceLogSuppressedMarkPassingsEvent castedEvent = (RaceLogSuppressedMarkPassingsEvent) event;
result.add(new Triple<Competitor, Integer, TimePoint>(castedEvent.getInvolvedBoats().get(0), castedEvent.getZeroBasedIndexOfFirstSuppressedWaypoint(), null));
result.add(new Triple<Competitor, Integer, TimePoint>(castedEvent.getInvolvedCompetitors().get(0), castedEvent.getZeroBasedIndexOfFirstSuppressedWaypoint(), null));
}
}
return result;
@@ -0,0 +1,31 @@
package com.sap.sailing.domain.abstractlog.race.analyzing.impl;
import java.util.Collections;
import java.util.Map;
import com.sap.sailing.domain.abstractlog.race.RaceLog;
import com.sap.sailing.domain.abstractlog.race.tracking.analyzing.impl.RaceLogUsesOwnCompetitorsAnalyzer;
import com.sap.sailing.domain.abstractlog.shared.analyzing.CompetitorsAndBoatsInLogAnalyzer;
import com.sap.sailing.domain.base.Boat;
import com.sap.sailing.domain.base.Competitor;
public class RaceLogRegisteredCompetitorsAndBoatsAnalyzer extends RaceLogAnalyzer<Map<Competitor, Boat>> {
public RaceLogRegisteredCompetitorsAndBoatsAnalyzer(RaceLog raceLog) {
super(raceLog);
}
@Override
protected Map<Competitor, Boat> performAnalysis() {
final Map<Competitor, Boat> result;
if (new RaceLogUsesOwnCompetitorsAnalyzer(getLog()).analyze()){
// get Events from RaceLog
result = new CompetitorsAndBoatsInLogAnalyzer<>(getLog()).analyze();
} else {
// as we're explicitly only trying to find those registrations in the RaceLog, we won't
// return anything from the regatta log.
result = Collections.emptyMap();
}
return result;
}
}
@@ -103,7 +103,7 @@ public abstract class AbstractRaceLogChangedVisitor implements RaceLogEventVisit
public void visit(RaceLogRegisterCompetitorEvent event) {
notifyListenerAboutEventAdded(event);
}
@Override
public void visit(RaceLogAdditionalScoringInformationEvent additionalScoringInformation) {
notifyListenerAboutEventAdded(additionalScoringInformation);
@@ -109,7 +109,7 @@ public abstract class BaseRaceLogEventVisitor implements RaceLogEventVisitor {
public void visit(RaceLogRegisterCompetitorEvent event) {
}
@Override
public void visit(RaceLogAdditionalScoringInformationEvent event) {
@@ -144,4 +144,5 @@ public abstract class BaseRaceLogEventVisitor implements RaceLogEventVisitor {
public void visit(RaceLogUseCompetitorsFromRaceLogEvent event) {
}
}
@@ -21,7 +21,7 @@ public class RaceLogEventDataImpl implements RaceLogEventData {
}
@Override
public List<Competitor> getInvolvedBoats() {
public List<Competitor> getInvolvedCompetitors() {
return involvedBoats;
}
@@ -32,6 +32,6 @@ public class RaceLogEventDataImpl implements RaceLogEventData {
@Override
public String toString() {
return super.toString() + ", involvedBoats: " + getInvolvedBoats() + ", passId: " + getPassId();
return super.toString() + ", involvedBoats: " + getInvolvedCompetitors() + ", passId: " + getPassId();
}
}
@@ -36,8 +36,8 @@ public abstract class RaceLogEventImpl extends AbstractLogEventImpl<RaceLogEvent
}
@Override
public List<Competitor> getInvolvedBoats() {
return raceLogEventData.getInvolvedBoats();
public List<Competitor> getInvolvedCompetitors() {
return raceLogEventData.getInvolvedCompetitors();
}
@Override
@@ -49,8 +49,8 @@ public class RaceLogFixedMarkPassingEventImpl extends RaceLogEventImpl implement
@Override
public String getShortInfo() {
return (getInvolvedBoats() == null || getInvolvedBoats().get(0) == null ? "Unknown" :
getInvolvedBoats().get(0).getName()) + " at mark " + getZeroBasedIndexOfPassedWaypoint() + " at "
return (getInvolvedCompetitors() == null || getInvolvedCompetitors().get(0) == null ? "Unknown" :
getInvolvedCompetitors().get(0).getName()) + " at mark " + getZeroBasedIndexOfPassedWaypoint() + " at "
+ getTimePointOfFixedPassing();
}
}
@@ -35,8 +35,8 @@ public class RaceLogRevokeEventImpl extends RevokeEventImpl<RaceLogEventVisitor>
}
@Override
public List<Competitor> getInvolvedBoats() {
return raceLogEventData.getInvolvedBoats();
public List<Competitor> getInvolvedCompetitors() {
return raceLogEventData.getInvolvedCompetitors();
}
@Override
@@ -42,6 +42,6 @@ public class RaceLogSuppressedMarkPassingsEventImpl extends RaceLogEventImpl imp
@Override
public String getShortInfo() {
return getInvolvedBoats().get(0).getName() + " at mark " + getZeroBasedIndexOfFirstSuppressedWaypoint();
return getInvolvedCompetitors().get(0).getName() + " at mark " + getZeroBasedIndexOfFirstSuppressedWaypoint();
}
}
@@ -3,8 +3,16 @@ package com.sap.sailing.domain.abstractlog.race.tracking;
import com.sap.sailing.domain.abstractlog.race.RaceLogEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogEventVisitor;
import com.sap.sailing.domain.abstractlog.shared.events.RegisterCompetitorEvent;
import com.sap.sailing.domain.base.Boat;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.CompetitorWithBoat;
public interface RaceLogRegisterCompetitorEvent extends RaceLogEvent,
RegisterCompetitorEvent<RaceLogEventVisitor> {
/**
* The event registers a {@link CompetitorWithBoat} or a pair of {@link Competitor} and {@link Boat} on a race.
* The competitor type is determined by the {@link Regatta} attribute 'canBoatsOfCompetitorsChangePerRace'
* If 'canBoatsOfCompetitorsChangePerRace' is true the type must be {@link Competitor}
* If 'canBoatsOfCompetitorsChangePerRace' is false the type must be {@link CompetitorWithBoat}
*/
public interface RaceLogRegisterCompetitorEvent extends RaceLogEvent, RegisterCompetitorEvent<RaceLogEventVisitor> {
Boat getBoat();
}
@@ -0,0 +1,44 @@
package com.sap.sailing.domain.abstractlog.race.tracking.analyzing.impl;
import java.util.Map;
import com.sap.sailing.domain.abstractlog.race.RaceLog;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogAnalyzer;
import com.sap.sailing.domain.abstractlog.regatta.RegattaLog;
import com.sap.sailing.domain.abstractlog.shared.analyzing.CompetitorsAndBoatsInLogAnalyzer;
import com.sap.sailing.domain.base.Boat;
import com.sap.sailing.domain.base.Competitor;
/**
* Used to find competitors (including their boats) of a race based on {@link RaceLog} and {@link RegattaLog} contents. Checks whether the
* competitors for the given race are registered on the RaceLog via {@link RaceLogUsesOwnCompetitorsAnalyzer} or on the
* RegattaLog and fetches the competitors appropriately.
*
* Should not be used by clients directly. Instead, get the competitor set from the {@code RaceColumn}, using either one
* of {@code getAllCompetitorsAndTheirBoats()} and {@code getAllCompetitorsAndTheirBoats(Fleet)}. Those methods automatically check whether a
* tracked race is present, which takes precedence over the Race/RegattaLog.
*
* @author Jan Bross (D056848)
*
*/
public class RegisteredCompetitorsAndBoatsAnalyzer extends RaceLogAnalyzer<Map<Competitor, Boat>> {
private RegattaLog regattaLog;
public RegisteredCompetitorsAndBoatsAnalyzer(RaceLog raceLog, RegattaLog regattaLog) {
super(raceLog);
this.regattaLog = regattaLog;
}
@Override
protected Map<Competitor, Boat> performAnalysis() {
final Map<Competitor, Boat> result;
if (new RaceLogUsesOwnCompetitorsAnalyzer(getLog()).analyze()) {
// get Events from RaceLog
result = new CompetitorsAndBoatsInLogAnalyzer<>(getLog()).analyze();
} else {
// get Events from RegattaLog
result = new CompetitorsAndBoatsInLogAnalyzer<>(regattaLog).analyze();
}
return result;
}
}
@@ -10,43 +10,83 @@ import com.sap.sailing.domain.abstractlog.race.RaceLogEventVisitor;
import com.sap.sailing.domain.abstractlog.race.impl.RaceLogEventDataImpl;
import com.sap.sailing.domain.abstractlog.race.tracking.RaceLogRegisterCompetitorEvent;
import com.sap.sailing.domain.abstractlog.shared.events.impl.BaseRegisterCompetitorEventImpl;
import com.sap.sailing.domain.base.Boat;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.CompetitorWithBoat;
import com.sap.sse.common.TimePoint;
/**
* Links a competitor to the boat it sails with in the race. The {@link #boat} field refers to the
* {@link CompetitorWithBoat#getBoat() competitor's boat} if the competitor has a fixed boat attached.
*
* @author Axel Uhl (d043530)
*
*/
public class RaceLogRegisterCompetitorEventImpl extends BaseRegisterCompetitorEventImpl<RaceLogEventVisitor> implements
RaceLogRegisterCompetitorEvent {
private static final long serialVersionUID = -5114645637316367845L;
private final RaceLogEventData raceLogEventData;
/**
* The boat the competitor sails with in the race to which the race log containing this event belongs.
* Never {@code null}.
*/
private final Boat boat;
/**
* @throws IllegalArgumentException
* if {@code competitor} is null
*/
public RaceLogRegisterCompetitorEventImpl(TimePoint createdAt, TimePoint logicalTimePoint,
AbstractLogEventAuthor author, Serializable id, int passId, Competitor competitor)
AbstractLogEventAuthor author, Serializable id, int passId, CompetitorWithBoat competitor)
throws IllegalArgumentException {
super(createdAt, logicalTimePoint, author, id, competitor);
this.raceLogEventData = new RaceLogEventDataImpl(null, passId);
checkBoat(competitor.getBoat());
this.boat = competitor.getBoat();
}
/**
* @throws IllegalArgumentException
* if {@code competitor} is null
* if {@code competitor} is null or if {@code boat} is null
*/
public RaceLogRegisterCompetitorEventImpl(TimePoint createdAt, TimePoint logicalTimePoint,
AbstractLogEventAuthor author, Serializable id, int passId, Competitor competitor, Boat boat)
throws IllegalArgumentException {
super(createdAt, logicalTimePoint, author, id, competitor);
this.raceLogEventData = new RaceLogEventDataImpl(null, passId);
checkBoat(boat);
this.boat = boat;
}
/**
* @throws IllegalArgumentException
* if {@code competitor} is null or if {@code competitor.getBoat()} is null
*/
public RaceLogRegisterCompetitorEventImpl(TimePoint logicalTimePoint,
AbstractLogEventAuthor author, int passId, Competitor competitor)
AbstractLogEventAuthor author, int passId, CompetitorWithBoat competitor)
throws IllegalArgumentException {
this(now(), logicalTimePoint, author, randId(), passId, competitor);
}
/**
* @throws IllegalArgumentException
* if {@code competitor} is null or if {@code boat} is null
*/
public RaceLogRegisterCompetitorEventImpl(TimePoint logicalTimePoint,
AbstractLogEventAuthor author, int passId, Competitor competitor, Boat boat)
throws IllegalArgumentException {
this(now(), logicalTimePoint, author, randId(), passId, competitor, boat);
}
@Override
public int getPassId() {
return raceLogEventData.getPassId();
}
@Override
public List<Competitor> getInvolvedBoats() {
return Collections.singletonList(getCompetitor());
public List<Competitor> getInvolvedCompetitors() {
return Collections.singletonList((Competitor) getCompetitor());
}
@Override
@@ -58,4 +98,20 @@ public class RaceLogRegisterCompetitorEventImpl extends BaseRegisterCompetitorEv
public void accept(RaceLogEventVisitor visitor) {
visitor.visit(this);
}
@Override
public Boat getBoat() {
return boat;
}
private static void checkBoat(Boat boat) throws IllegalArgumentException {
if (boat == null) {
throw new IllegalArgumentException("Boat must not be null");
}
}
@Override
public String getShortInfo() {
return "competitor: " + getCompetitor().toString() + " with boat " + getBoat().toString();
}
}
@@ -1,5 +1,7 @@
package com.sap.sailing.domain.abstractlog.regatta;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceBoatMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceBoatSensorDataMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceCompetitorMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceCompetitorSensorDataMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMappingEvent;
@@ -13,7 +15,11 @@ public interface MappingEventVisitor {
void visit(RegattaLogDeviceMarkMappingEvent event);
void visit(RegattaLogDeviceCompetitorMappingEvent event);
void visit(RegattaLogDeviceBoatMappingEvent event);
void visit(RegattaLogDeviceCompetitorSensorDataMappingEvent event);
void visit(RegattaLogDeviceBoatSensorDataMappingEvent event);
}
@@ -2,9 +2,12 @@ package com.sap.sailing.domain.abstractlog.regatta;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogCloseOpenEndedDeviceMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDefineMarkEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceBoatMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceBoatSensorDataMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceCompetitorMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceCompetitorSensorDataMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMarkMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogRegisterBoatEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogRegisterCompetitorEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogRevokeEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogSetCompetitorTimeOnDistanceAllowancePerNauticalMileEvent;
@@ -16,13 +19,19 @@ public interface RegattaLogEventVisitor {
void visit(RegattaLogDeviceMarkMappingEvent event);
void visit(RegattaLogDeviceCompetitorMappingEvent event);
void visit(RegattaLogDeviceBoatMappingEvent event);
void visit(RegattaLogDeviceCompetitorSensorDataMappingEvent event);
void visit(RegattaLogDeviceBoatSensorDataMappingEvent event);
void visit(RegattaLogCloseOpenEndedDeviceMappingEvent event);
void visit(RegattaLogRegisterBoatEvent event);
void visit(RegattaLogRegisterCompetitorEvent event);
void visit(RegattaLogSetCompetitorTimeOnTimeFactorEvent event);
void visit(RegattaLogSetCompetitorTimeOnDistanceAllowancePerNauticalMileEvent event);
@@ -0,0 +1,7 @@
package com.sap.sailing.domain.abstractlog.regatta.events;
import com.sap.sailing.domain.base.Boat;
public interface RegattaLogDeviceBoatMappingEvent extends RegattaLogDeviceMappingEvent<Boat> {
}
@@ -0,0 +1,7 @@
package com.sap.sailing.domain.abstractlog.regatta.events;
import com.sap.sailing.domain.base.Boat;
public interface RegattaLogDeviceBoatSensorDataMappingEvent extends RegattaLogDeviceMappingEvent<Boat> {
}
@@ -13,6 +13,12 @@ public abstract class RegattaLogDeviceMappingEventImpl<ItemType extends WithID>
RegattaLogDeviceMappingEvent<ItemType> {
private static final long serialVersionUID = -8439653251231710356L;
public interface Factory<ItemType extends WithID, T extends RegattaLogDeviceMappingEvent<ItemType>> {
T create(TimePoint createdAt, TimePoint logicalTimePoint,
AbstractLogEventAuthor author, Serializable pId, ItemType mappedTo, DeviceIdentifier device,
TimePoint from, TimePoint to);
}
private final ItemType mappedTo;
private final DeviceIdentifier device;
@@ -0,0 +1,12 @@
package com.sap.sailing.domain.abstractlog.regatta.events;
import com.sap.sailing.domain.abstractlog.Revokable;
import com.sap.sailing.domain.abstractlog.regatta.RegattaLogEvent;
import com.sap.sailing.domain.base.Boat;
/**
* The event registers a standalone {@link Boat} on a regatta.
*/
public interface RegattaLogRegisterBoatEvent extends RegattaLogEvent, Revokable {
Boat getBoat();
}
@@ -3,8 +3,16 @@ package com.sap.sailing.domain.abstractlog.regatta.events;
import com.sap.sailing.domain.abstractlog.regatta.RegattaLogEvent;
import com.sap.sailing.domain.abstractlog.regatta.RegattaLogEventVisitor;
import com.sap.sailing.domain.abstractlog.shared.events.RegisterCompetitorEvent;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.CompetitorWithBoat;
public interface RegattaLogRegisterCompetitorEvent extends RegattaLogEvent,
RegisterCompetitorEvent<RegattaLogEventVisitor> {
/**
* The event registers a {@link Competitor} or {@link CompetitorWithBoat} on a regatta.
* The competitor type is determined by the {@link Regatta} attribute 'canBoatsOfCompetitorsChangePerRace'
* If 'canBoatsOfCompetitorsChangePerRace' is true the type must be {@link Competitor}
* If 'canBoatsOfCompetitorsChangePerRace' is false the type must be {@link CompetitorWithBoat}
*/
public interface RegattaLogRegisterCompetitorEvent
extends RegattaLogEvent, RegisterCompetitorEvent<RegattaLogEventVisitor> {
}
@@ -0,0 +1,41 @@
package com.sap.sailing.domain.abstractlog.regatta.events.impl;
import java.io.Serializable;
import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor;
import com.sap.sailing.domain.abstractlog.regatta.MappingEventVisitor;
import com.sap.sailing.domain.abstractlog.regatta.RegattaLogEventVisitor;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceBoatSensorDataMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMappingEventImpl;
import com.sap.sailing.domain.base.Boat;
import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sse.common.TimePoint;
/**
* Base type for mapping event for devices mapped for specific Bravo type fixes.
*/
public abstract class AbstractRegattaLogDeviceBoatSensorDataMappingEventImpl extends RegattaLogDeviceMappingEventImpl<Boat>
implements RegattaLogDeviceBoatSensorDataMappingEvent {
private static final long serialVersionUID = -1494030544804758753L;
public AbstractRegattaLogDeviceBoatSensorDataMappingEventImpl(TimePoint createdAt, TimePoint logicalTimePoint,
AbstractLogEventAuthor author, Serializable pId, Boat mappedTo, DeviceIdentifier device,
TimePoint from, TimePoint to) {
super(createdAt, logicalTimePoint, author, pId, mappedTo, device, from, to);
}
public AbstractRegattaLogDeviceBoatSensorDataMappingEventImpl(TimePoint logicalTimePoint, AbstractLogEventAuthor author,
Boat mappedTo, DeviceIdentifier device, TimePoint from, TimePoint to) {
super(logicalTimePoint, author, mappedTo, device, from, to);
}
@Override
public void accept(RegattaLogEventVisitor visitor) {
visitor.visit(this);
}
@Override
public void accept(MappingEventVisitor visitor) {
visitor.visit(this);
}
}
@@ -18,12 +18,6 @@ public abstract class AbstractRegattaLogDeviceCompetitorSensorDataMappingEventIm
implements RegattaLogDeviceCompetitorSensorDataMappingEvent {
private static final long serialVersionUID = -1494030544804758753L;
public interface Factory<T extends RegattaLogDeviceCompetitorSensorDataMappingEvent> {
T create(TimePoint createdAt, TimePoint logicalTimePoint,
AbstractLogEventAuthor author, Serializable pId, Competitor mappedTo, DeviceIdentifier device,
TimePoint from, TimePoint to);
}
public AbstractRegattaLogDeviceCompetitorSensorDataMappingEventImpl(TimePoint createdAt, TimePoint logicalTimePoint,
AbstractLogEventAuthor author, Serializable pId, Competitor mappedTo, DeviceIdentifier device,
TimePoint from, TimePoint to) {
@@ -0,0 +1,27 @@
package com.sap.sailing.domain.abstractlog.regatta.events.impl;
import java.io.Serializable;
import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor;
import com.sap.sailing.domain.base.Boat;
import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sse.common.TimePoint;
/**
* Mapping event for devices mapped for Bravo extended type fixes.
*/
public class RegattaLogDeviceBoatBravoExtendedMappingEventImpl extends AbstractRegattaLogDeviceBoatSensorDataMappingEventImpl {
private static final long serialVersionUID = -1494030544804758753L;
public RegattaLogDeviceBoatBravoExtendedMappingEventImpl(TimePoint createdAt, TimePoint logicalTimePoint,
AbstractLogEventAuthor author, Serializable pId, Boat mappedTo, DeviceIdentifier device,
TimePoint from, TimePoint to) {
super(createdAt, logicalTimePoint, author, pId, mappedTo, device, from, to);
}
public RegattaLogDeviceBoatBravoExtendedMappingEventImpl(TimePoint logicalTimePoint, AbstractLogEventAuthor author,
Boat mappedTo, DeviceIdentifier device, TimePoint from, TimePoint to) {
super(logicalTimePoint, author, mappedTo, device, from, to);
}
}
@@ -0,0 +1,26 @@
package com.sap.sailing.domain.abstractlog.regatta.events.impl;
import java.io.Serializable;
import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor;
import com.sap.sailing.domain.base.Boat;
import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sse.common.TimePoint;
/**
* Mapping event for devices mapped for Bravo type fixes.
*/
public class RegattaLogDeviceBoatBravoMappingEventImpl extends AbstractRegattaLogDeviceBoatSensorDataMappingEventImpl {
private static final long serialVersionUID = -1494030544804758753L;
public RegattaLogDeviceBoatBravoMappingEventImpl(TimePoint createdAt, TimePoint logicalTimePoint,
AbstractLogEventAuthor author, Serializable pId, Boat mappedTo, DeviceIdentifier device,
TimePoint from, TimePoint to) {
super(createdAt, logicalTimePoint, author, pId, mappedTo, device, from, to);
}
public RegattaLogDeviceBoatBravoMappingEventImpl(TimePoint logicalTimePoint, AbstractLogEventAuthor author,
Boat mappedTo, DeviceIdentifier device, TimePoint from, TimePoint to) {
super(logicalTimePoint, author, mappedTo, device, from, to);
}
}
@@ -0,0 +1,28 @@
package com.sap.sailing.domain.abstractlog.regatta.events.impl;
import java.io.Serializable;
import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor;
import com.sap.sailing.domain.base.Boat;
import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sse.common.TimePoint;
/**
* Mapping event for devices mapped for "Expedition" extended type fixes, coming from the
* "Expedition" software that was originally used in the context of this solution only for
* handling wind data but now is also used to forward sensor data into competitor tracks.
*/
public class RegattaLogDeviceBoatExpeditionExtendedMappingEventImpl extends AbstractRegattaLogDeviceBoatSensorDataMappingEventImpl {
private static final long serialVersionUID = -7865925670153838864L;
public RegattaLogDeviceBoatExpeditionExtendedMappingEventImpl(TimePoint createdAt, TimePoint logicalTimePoint,
AbstractLogEventAuthor author, Serializable pId, Boat mappedTo, DeviceIdentifier device,
TimePoint from, TimePoint to) {
super(createdAt, logicalTimePoint, author, pId, mappedTo, device, from, to);
}
public RegattaLogDeviceBoatExpeditionExtendedMappingEventImpl(TimePoint logicalTimePoint, AbstractLogEventAuthor author,
Boat mappedTo, DeviceIdentifier device, TimePoint from, TimePoint to) {
super(logicalTimePoint, author, mappedTo, device, from, to);
}
}
@@ -0,0 +1,38 @@
package com.sap.sailing.domain.abstractlog.regatta.events.impl;
import java.io.Serializable;
import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor;
import com.sap.sailing.domain.abstractlog.regatta.MappingEventVisitor;
import com.sap.sailing.domain.abstractlog.regatta.RegattaLogEventVisitor;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceBoatMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMappingEventImpl;
import com.sap.sailing.domain.base.Boat;
import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sse.common.TimePoint;
public class RegattaLogDeviceBoatMappingEventImpl extends RegattaLogDeviceMappingEventImpl<Boat>
implements RegattaLogDeviceBoatMappingEvent {
public RegattaLogDeviceBoatMappingEventImpl(TimePoint createdAt, TimePoint logicalTimePoint,
AbstractLogEventAuthor author, Serializable pId, Boat mappedTo, DeviceIdentifier device,
TimePoint from, TimePoint toInclusive) {
super(createdAt, logicalTimePoint, author, pId, mappedTo, device, from, toInclusive);
}
public RegattaLogDeviceBoatMappingEventImpl(TimePoint logicalTimePoint, AbstractLogEventAuthor author,
Boat mappedTo, DeviceIdentifier device, TimePoint from, TimePoint toInclusive) {
super(logicalTimePoint, author, mappedTo, device, from, toInclusive);
}
private static final long serialVersionUID = -1494030544804758753L;
@Override
public void accept(RegattaLogEventVisitor visitor) {
visitor.visit(this);
}
@Override
public void accept(MappingEventVisitor visitor) {
visitor.visit(this);
}
}
@@ -13,7 +13,6 @@ import com.sap.sse.common.TimePoint;
public class RegattaLogDeviceCompetitorBravoMappingEventImpl extends AbstractRegattaLogDeviceCompetitorSensorDataMappingEventImpl {
private static final long serialVersionUID = -1494030544804758753L;
public RegattaLogDeviceCompetitorBravoMappingEventImpl(TimePoint createdAt, TimePoint logicalTimePoint,
AbstractLogEventAuthor author, Serializable pId, Competitor mappedTo, DeviceIdentifier device,
TimePoint from, TimePoint to) {
@@ -0,0 +1,57 @@
package com.sap.sailing.domain.abstractlog.regatta.events.impl;
import java.io.Serializable;
import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor;
import com.sap.sailing.domain.abstractlog.impl.AbstractLogEventImpl;
import com.sap.sailing.domain.abstractlog.regatta.RegattaLogEventVisitor;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogRegisterBoatEvent;
import com.sap.sailing.domain.base.Boat;
import com.sap.sse.common.TimePoint;
public class RegattaLogRegisterBoatEventImpl extends AbstractLogEventImpl<RegattaLogEventVisitor>
implements RegattaLogRegisterBoatEvent {
private static final long serialVersionUID = -4531928509653259811L;
private final Boat boat;
/**
* @throws IllegalArgumentException
* if {@code boat} is null
*/
public RegattaLogRegisterBoatEventImpl(TimePoint createdAt, TimePoint logicalTimePoint,
AbstractLogEventAuthor author, Serializable id, Boat boat) throws IllegalArgumentException {
super(createdAt, logicalTimePoint, author, id);
checkBoat(boat);
this.boat = boat;
}
/**
* @throws IllegalArgumentException
* if {@code boat} is null
*/
public RegattaLogRegisterBoatEventImpl(TimePoint logicalTimePoint,
AbstractLogEventAuthor author, Boat boat) throws IllegalArgumentException {
this(now(), logicalTimePoint, author, randId(), boat);
}
@Override
public void accept(RegattaLogEventVisitor visitor) {
visitor.visit(this);
}
private static void checkBoat(Boat boat) throws IllegalArgumentException {
if (boat == null) {
throw new IllegalArgumentException("Boat may not be null");
}
}
@Override
public Boat getBoat() {
return boat;
}
@Override
public String getShortInfo() {
return "boat: " + getBoat().toString();
}
}
@@ -7,8 +7,16 @@ import com.sap.sailing.domain.abstractlog.regatta.RegattaLogEventVisitor;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogRegisterCompetitorEvent;
import com.sap.sailing.domain.abstractlog.shared.events.impl.BaseRegisterCompetitorEventImpl;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.CompetitorWithBoat;
import com.sap.sse.common.TimePoint;
/**
* This event can be used for 2 kinds of competitor registrations.
* If 'canBoatsOfCompetitorsChangePerRace' is true for a regatta the event registers a standalone {@link Competitor} without a boat.
* If 'canBoatsOfCompetitorsChangePerRace' is false the event registers a {@link CompetitorWithBoat}.
* @author Frank Mittag
*
*/
public class RegattaLogRegisterCompetitorEventImpl extends BaseRegisterCompetitorEventImpl<RegattaLogEventVisitor>
implements RegattaLogRegisterCompetitorEvent {
private static final long serialVersionUID = -5114645637316367845L;
@@ -3,9 +3,12 @@ package com.sap.sailing.domain.abstractlog.regatta.impl;
import com.sap.sailing.domain.abstractlog.regatta.RegattaLogEventVisitor;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogCloseOpenEndedDeviceMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDefineMarkEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceBoatMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceBoatSensorDataMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceCompetitorMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceCompetitorSensorDataMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMarkMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogRegisterBoatEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogRegisterCompetitorEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogRevokeEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogSetCompetitorTimeOnDistanceAllowancePerNauticalMileEvent;
@@ -24,15 +27,27 @@ public class BaseRegattaLogEventVisitor implements RegattaLogEventVisitor {
@Override
public void visit(RegattaLogDeviceCompetitorMappingEvent event) {
}
@Override
public void visit(RegattaLogDeviceBoatMappingEvent event) {
}
@Override
public void visit(RegattaLogDeviceCompetitorSensorDataMappingEvent event) {
}
@Override
public void visit(RegattaLogDeviceBoatSensorDataMappingEvent event) {
}
@Override
public void visit(RegattaLogCloseOpenEndedDeviceMappingEvent event) {
}
@Override
public void visit(RegattaLogRegisterBoatEvent event) {
}
@Override
public void visit(RegattaLogRegisterCompetitorEvent event) {
}
@@ -4,9 +4,12 @@ import com.sap.sailing.domain.abstractlog.regatta.RegattaLogEvent;
import com.sap.sailing.domain.abstractlog.regatta.RegattaLogEventVisitor;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogCloseOpenEndedDeviceMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDefineMarkEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceBoatMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceBoatSensorDataMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceCompetitorMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceCompetitorSensorDataMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMarkMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogRegisterBoatEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogRegisterCompetitorEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogRevokeEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogSetCompetitorTimeOnDistanceAllowancePerNauticalMileEvent;
@@ -29,12 +32,22 @@ public abstract class RegattaLogEventListener implements RegattaLogEventVisitor
public void visit(RegattaLogDeviceCompetitorMappingEvent event) {
eventAdded(event);
}
@Override
public void visit(RegattaLogDeviceBoatMappingEvent event) {
eventAdded(event);
}
@Override
public void visit(RegattaLogCloseOpenEndedDeviceMappingEvent event) {
eventAdded(event);
}
@Override
public void visit(RegattaLogRegisterBoatEvent event) {
eventAdded(event);
}
@Override
public void visit(RegattaLogRegisterCompetitorEvent event) {
eventAdded(event);
@@ -59,4 +72,9 @@ public abstract class RegattaLogEventListener implements RegattaLogEventVisitor
public void visit(RegattaLogDeviceCompetitorSensorDataMappingEvent event) {
eventAdded(event);
}
@Override
public void visit(RegattaLogDeviceBoatSensorDataMappingEvent event) {
eventAdded(event);
}
}
@@ -0,0 +1,70 @@
package com.sap.sailing.domain.abstractlog.regatta.tracking.analyzing.impl;
import java.util.HashSet;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.sap.sailing.domain.abstractlog.AbstractLog;
import com.sap.sailing.domain.abstractlog.AbstractLogEvent;
import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor;
import com.sap.sailing.domain.abstractlog.BaseLogAnalyzer;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogRegisterBoatEvent;
import com.sap.sailing.domain.base.Boat;
import com.sap.sailing.domain.common.abstractlog.NotRevokableException;
import com.sap.sse.common.Util;
/**
* Finds the boat registration events for a given set of {@link Boat boats} that must be
* {@link AbstractLog#revokeEvent(AbstractLogEventAuthor, AbstractLogEvent) revoked} in order to deregister the
* boats. Note that the de-registration cannot be performed during analysis because
* {@link BaseLogAnalyzer#analyze()} obtains a read lock on the log whereas
* {@link AbstractLog#revokeEvent(AbstractLogEventAuthor, AbstractLogEvent)} requires a write lock, and upgrading from a
* read to a write lock is not possible.<p>
*
* Clients must call {@link #deregister} with the result of {@link #analyze()} in order to actually perform the
* deregistration.
*
* @author Frank Mittag
*
*/
public class RegattaLogBoatDeregistrator<LogT extends AbstractLog<EventT, VisitorT>, EventT extends AbstractLogEvent<VisitorT>, VisitorT>
extends BaseLogAnalyzer<LogT, EventT, VisitorT, Set<EventT>> {
private static final Logger logger = Logger.getLogger(RegattaLogBoatDeregistrator.class.getName());
protected final Iterable<Boat> boatsToDeregister;
private AbstractLogEventAuthor eventAuthor;
public RegattaLogBoatDeregistrator(LogT log, Iterable<Boat> boatsToDeregister, AbstractLogEventAuthor eventAuthor) {
super(log);
this.boatsToDeregister = boatsToDeregister;
this.eventAuthor = eventAuthor;
}
@Override
protected Set<EventT> performAnalysis() {
final Set<EventT> result = new HashSet<>();
final HashSet<Boat> boatSet = new HashSet<Boat>();
Util.addAll(boatsToDeregister, boatSet);
for (EventT event : log.getUnrevokedEventsDescending()) {
if (event instanceof RegattaLogRegisterBoatEvent) {
RegattaLogRegisterBoatEvent registerEvent = (RegattaLogRegisterBoatEvent) event;
if (boatSet.contains(registerEvent.getBoat())) {
result.add(event);
}
}
}
return result;
}
public void deregister(final Set<EventT> boatRegistrationEvents) {
for (final EventT event : boatRegistrationEvents) {
try {
log.revokeEvent(eventAuthor, event,
"unregistering boat because no longer selected for registration");
} catch (NotRevokableException e) {
logger.log(Level.WARNING, "could not unregister boat by adding RevokeEvent", e);
}
}
}
}
@@ -0,0 +1,36 @@
package com.sap.sailing.domain.abstractlog.regatta.tracking.analyzing.impl;
import java.util.HashSet;
import java.util.Set;
import com.sap.sailing.domain.abstractlog.AbstractLog;
import com.sap.sailing.domain.abstractlog.AbstractLogEvent;
import com.sap.sailing.domain.abstractlog.BaseLogAnalyzer;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogRegisterBoatEvent;
import com.sap.sailing.domain.base.Boat;
/**
* This class searches for RegisterBoat events in the given log.
*
* Boats alone in a regatta log are not enough to be used in a real race.
* Instead a competitor and a boat together must be registered through a {@link RegisterCompetitorEvent} event.
*
*/
public class RegattaLogBoatsInLogAnalyzer<LogT extends AbstractLog<EventT, VisitorT>, EventT extends AbstractLogEvent<VisitorT>, VisitorT>
extends BaseLogAnalyzer<LogT, EventT, VisitorT, Set<Boat>> {
public RegattaLogBoatsInLogAnalyzer(LogT log) {
super(log);
}
@Override
protected Set<Boat> performAnalysis() {
Set<Boat> result = new HashSet<Boat>();
for (EventT event : getLog().getUnrevokedEvents()) {
if (event instanceof RegattaLogRegisterBoatEvent) {
result.add(((RegattaLogRegisterBoatEvent) event).getBoat());
}
}
return result;
}
}
@@ -0,0 +1,55 @@
package com.sap.sailing.domain.abstractlog.shared.analyzing;
import java.util.HashMap;
import java.util.Map;
import com.sap.sailing.domain.abstractlog.AbstractLog;
import com.sap.sailing.domain.abstractlog.AbstractLogEvent;
import com.sap.sailing.domain.abstractlog.BaseLogAnalyzer;
import com.sap.sailing.domain.abstractlog.race.RaceLog;
import com.sap.sailing.domain.abstractlog.race.tracking.RaceLogRegisterCompetitorEvent;
import com.sap.sailing.domain.abstractlog.regatta.RegattaLog;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogRegisterCompetitorEvent;
import com.sap.sailing.domain.base.Boat;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.CompetitorWithBoat;
/**
* This class searches for {@link RaceLogRegisterCompetitorEvent}s or {@link RegattaLogRegisterCompetitorEvent}s in the given log
* in order to determine the competitor/boat mappings for both kind of events
*
*/
public class CompetitorsAndBoatsInLogAnalyzer<LogT extends AbstractLog<EventT, VisitorT>, EventT extends AbstractLogEvent<VisitorT>, VisitorT>
extends BaseLogAnalyzer<LogT, EventT, VisitorT, Map<Competitor, Boat>> {
public CompetitorsAndBoatsInLogAnalyzer(LogT log) {
super(log);
}
@Override
protected Map<Competitor, Boat> performAnalysis() {
Map<Competitor, Boat> result = new HashMap<>();
LogT log = getLog();
if (log instanceof RegattaLog) {
for (EventT event : getLog().getUnrevokedEvents()) {
if (event instanceof RegattaLogRegisterCompetitorEvent) {
RegattaLogRegisterCompetitorEvent regattaLogCompetitorEvent = (RegattaLogRegisterCompetitorEvent) event;
if (regattaLogCompetitorEvent.getCompetitor().hasBoat()) {
CompetitorWithBoat competitorWithBoat = (CompetitorWithBoat) regattaLogCompetitorEvent.getCompetitor();
result.put(competitorWithBoat, competitorWithBoat.getBoat());
}
}
}
} else if (log instanceof RaceLog) {
for (EventT event : getLog().getUnrevokedEvents()) {
if (event instanceof RaceLogRegisterCompetitorEvent) {
RaceLogRegisterCompetitorEvent raceLogCompetitorEvent = (RaceLogRegisterCompetitorEvent) event;
result.put(raceLogCompetitorEvent.getCompetitor(), raceLogCompetitorEvent.getBoat());
}
}
}
return result;
}
}
@@ -35,7 +35,7 @@ public class CompetitorsInLogAnalyzer<LogT extends AbstractLog<EventT, VisitorT>
@Override
protected Set<Competitor> performAnalysis() {
Set<Competitor> result = new HashSet<Competitor>();
Set<Competitor> result = new HashSet<>();
for (EventT event : getLog().getUnrevokedEvents()) {
if (event instanceof RegisterCompetitorEvent) {
@@ -3,11 +3,12 @@ package com.sap.sailing.domain.abstractlog.shared.events;
import com.sap.sailing.domain.abstractlog.AbstractLogEvent;
import com.sap.sailing.domain.abstractlog.Revokable;
import com.sap.sailing.domain.abstractlog.race.RaceLog;
import com.sap.sailing.domain.abstractlog.regatta.RegattaLog;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sse.common.IsManagedByCache;
/**
* Register a competitor for that race using the {@link RaceLog} in racelog-tracked races.
* Register a competitor for {@link RaceLog} and {@link RegattaLog} tracked races and regattas.
*
* A dummy {@link Competitor} implementation with only an {@link Competitor#getId() id} may be used,
* if the competitor is known to already exist on the server, as it is
@@ -1,14 +1,33 @@
package com.sap.sailing.domain.base;
import com.sap.sse.common.Color;
import com.sap.sse.common.Named;
import com.sap.sse.common.IsManagedByCache;
import com.sap.sse.common.NamedWithID;
import com.sap.sse.datamining.annotations.Dimension;
public interface Boat extends Named {
BoatClass getBoatClass();
/**
* The boat being sailed by a competitor.
*
*/
public interface Boat extends NamedWithID, IsManagedByCache<SharedDomainFactory> {
BoatClass getBoatClass();
@Dimension(messageKey="SailID")
String getSailID();
Color getColor();
/**
* Adds a listener to this boat.
* Adding a listener that is already part of this boat's listeners set remains without effect.
*/
void addBoatChangeListener(BoatChangeListener listener);
/**
* Removes a listener from this boat.
* Trying to remove a listener that is not currently in the set of this boats's listener remains
* without effect.
*/
void removeBoatChangeListener(BoatChangeListener listener);
}
@@ -1,5 +1,11 @@
package com.sap.sailing.domain.base;
import com.sap.sse.common.Color;
public interface BoatChangeListener {
void nameChanged(String oldName, String newName);
void colorChanged(Color oldColor, Color newColor);
void sailIdChanged(String oldSailId, String newSailId);
}
@@ -0,0 +1,27 @@
package com.sap.sailing.domain.base;
import java.io.Serializable;
import com.sap.sse.common.Color;
/**
* Base interface for classes managing a set of {@link Boat} objects.
*/
public interface BoatFactory {
/**
* If a valid boat is returned and the caller has information available that could be used to update the boat,
* the caller must check the result of {@link #isBoatToUpdateDuringGetOrCreate(Boat)}, and if <code>true</code>,
* must call {@link #getOrCreateBoat()} to cause an update of the boat's values.
*/
Boat getExistingBoatById(Serializable boatId);
/**
* Checks if the <code>boat</code> shall be updated from the default provided by, e.g., a tracking infrastructure.
* Callers of {@link #getExistingBoatById(Serializable)} or {@link #getExistingBoatByIdAsString(String)}
* must call this method in case they retrieve a valid boat by ID and have data available that can be used to update
* the .
*/
boolean isBoatToUpdateDuringGetOrCreate(Boat result);
Boat getOrCreateBoat(Serializable id, String name, BoatClass boatClass, String sailId, Color color);
}
@@ -19,15 +19,28 @@ public interface Competitor extends NamedWithID, IsManagedByCache<SharedDomainFa
@Connector(messageKey="Nationality", ordinal=9)
Nationality getNationality();
@Connector(messageKey="Boat", ordinal=10)
Boat getBoat();
Color getColor();
String getEmail();
boolean hasEmail();
String getShortName();
/**
* Returns a derived short information about a competitor depending on the information available
* If we have a short name set on the competitor this name will be returned.
* If no short name exist but a boat the either the sailId or the boat name will returned.
* If all these attributes have no value null is returned.
*/
String getShortInfo();
/**
* A helper to know if the competitor has a boat attached.
* See {@link CompetitorWithBoat}
*/
boolean hasBoat();
@Dimension(messageKey="SearchTag", ordinal=11)
String getSearchTag();
@@ -0,0 +1,13 @@
package com.sap.sailing.domain.base;
import java.io.Serializable;
/**
* A helper interface to combine a competitor and a boat
* @author fmittag
*
*/
public interface CompetitorAndBoat extends Serializable {
Competitor getCompetitor();
Boat getBoat();
}
@@ -5,11 +5,13 @@ import java.net.URI;
import com.sap.sse.common.Color;
import com.sap.sse.common.Duration;
public interface CompetitorChangeListener extends BoatChangeListener, NationalityChangeListener {
public interface CompetitorChangeListener extends NationalityChangeListener {
void colorChanged(Color oldColor, Color newColor);
void nameChanged(String oldName, String newName);
void shortNameChanged(String oldShortName, String newShortName);
void emailChanged(String oldEmail, String newEmail);
void searchTagChanged(String oldSearchTag, String newSearchTag);
@@ -15,11 +15,13 @@ public interface CompetitorFactory {
/**
* If a valid competitor is returned and the caller has information available that could be used to update the competitor,
* the caller must check the result of {@link #isCompetitorToUpdateDuringGetOrCreate(Competitor)}, and if <code>true</code>,
* must call {@link #getOrCreateCompetitor(Serializable, String, DynamicTeam, DynamicBoat)} to cause an update of the
* must call {@link #getOrCreateCompetitor(Serializable, String, DynamicTeam...)} to cause an update of the
* competitor's values.
*/
Competitor getExistingCompetitorById(Serializable competitorId);
CompetitorWithBoat getExistingCompetitorWithBoatById(Serializable competitorId);
/**
* Checks if the <code>competitor</code> shall be updated from the default provided by, e.g., a tracking infrastructure.
* Callers of {@link #getExistingCompetitorById(Serializable)} or {@link #getExistingCompetitorByIdAsString(String)}
@@ -28,6 +30,10 @@ public interface CompetitorFactory {
*/
boolean isCompetitorToUpdateDuringGetOrCreate(Competitor result);
Competitor getOrCreateCompetitor(Serializable competitorId, String name, Color displayColor, String email,
URI flagImageURI, DynamicTeam team, DynamicBoat boat, Double timeOnTimeFactor, Duration timeOnDistanceAllowancePerNauticalMile, String searchTag);
Competitor getOrCreateCompetitor(Serializable competitorId, String name, String shortName, Color displayColor, String email,
URI flagImageURI, DynamicTeam team, Double timeOnTimeFactor, Duration timeOnDistanceAllowancePerNauticalMile, String searchTag);
CompetitorWithBoat getOrCreateCompetitorWithBoat(Serializable competitorId, String name, String shortName, Color displayColor, String email,
URI flagImageURI, DynamicTeam team, Double timeOnTimeFactor, Duration timeOnDistanceAllowancePerNauticalMile, String searchTag, DynamicBoat boat);
}
@@ -2,60 +2,87 @@ package com.sap.sailing.domain.base;
import java.io.Serializable;
import java.net.URI;
import java.util.Map;
import com.sap.sailing.domain.base.impl.DynamicBoat;
import com.sap.sailing.domain.base.impl.DynamicTeam;
import com.sap.sailing.domain.common.dto.BoatDTO;
import com.sap.sailing.domain.common.dto.CompetitorDTO;
import com.sap.sailing.domain.common.dto.CompetitorWithoutBoatDTO;
import com.sap.sse.common.Color;
import com.sap.sse.common.Duration;
/**
* Manages a set of {@link Competitor} objects. There may be a transient implementation based on a simple cache,
* Manages a set of {@link Competitor}, {@link CompetitorWithBoat} and {@link Boat} objects. There may be a transient implementation based on a simple cache,
* and there may be persistent implementations.
*
* @author Axel Uhl (d043530)
*
*/
public interface CompetitorStore extends CompetitorFactory {
public interface CompetitorStore extends CompetitorFactory, BoatFactory {
public interface CompetitorUpdateListener {
void competitorUpdated(Competitor competitor);
void competitorCreated(Competitor competitor);
}
public interface BoatUpdateListener {
void boatUpdated(Boat boat);
void boatCreated(Boat boat);
}
/**
* If a valid competitor is returned and the caller has information available that could be used to update the competitor,
* the caller must check the result of {@link #isCompetitorToUpdateDuringGetOrCreate(Competitor)}, and if <code>true</code>,
* must call {@link #getOrCreateCompetitor(Serializable, String, DynamicTeam, DynamicBoat)} to cause an update of the
* must call {@link #getOrCreateCompetitor(Serializable, String, DynamicTeam...)} to cause an update of the
* competitor's values.
*/
Competitor getExistingCompetitorByIdAsString(String idAsString);
/**
* When a competitor is queried using {@link #getOrCreateCompetitor(Serializable, String, DynamicTeam, DynamicBoat)}
* If a valid competitor is returned and the caller has information available that could be used to update the competitor,
* the caller must check the result of {@link #isCompetitorToUpdateDuringGetOrCreate(Competitor)}, and if <code>true</code>,
* must call {@link #getOrCreateCompetitorWithBoat(Serializable, String, DynamicTeam, DynamicBoat)} to cause an update of the
* competitor's values.
*/
CompetitorWithBoat getExistingCompetitorWithBoatByIdAsString(String idAsString);
/**
* When a competitor is queried using {@link #getOrCreateCompetitor()}
* , and the competitor object for that ID already exists, it is generally returned unchanged, and the name, team
* and boat parameters are not evaluated. This makes the data in this competitor store generally "write-once." This
* method can be used to reset a competitor object to what a tracking provider or an external system supplies to
* {@link #getOrCreateCompetitor(Serializable, String, DynamicTeam, DynamicBoat)}. After calling this method, the
* next call to {@link #getOrCreateCompetitor(Serializable, String, DynamicTeam, DynamicBoat)} with
* {@link #getOrCreateCompetitor()}. After calling this method, the
* next call to {@link #getOrCreateCompetitor()} with
* <code>competitor</code>'s {@link Competitor#getId() ID} will
* {@link #updateCompetitor(String, String, String, Nationality)} the competitor in its updatable properties such as
* {@link #updateCompetitor()} the competitor in its updatable properties such as
* the name, the sail ID and the nationality.
*/
void allowCompetitorResetToDefaults(Competitor competitor);
int size();
/** Removes all competitors and boats from the store */
void clear();
int getCompetitorsCount();
/**
* Removes all competitors from this store. Use with due care.
*/
void clear();
void clearCompetitors();
/**
* Obtains a non-live snapshot of the list of competitors managed by this store.
* Obtains a non-live snapshot of the list of all (with and without boat) competitors managed by this store.
*/
Iterable<? extends Competitor> getCompetitors();
void removeCompetitor(Competitor competitor);
Iterable<? extends Competitor> getAllCompetitors();
/**
* Obtains a non-live snapshot of the list of competitors with an assigned boat.
*/
Iterable<CompetitorWithBoat> getCompetitorsWithBoat();
/**
* Obtains a non-live snapshot of the list of competitors without an assigned boat.
*/
Iterable<Competitor> getCompetitorsWithoutBoat();
/**
* Updates the competitor with {@link Competitor#getId() ID} <code>id</code> by setting the name, sail ID and nationality to
@@ -65,13 +92,21 @@ public interface CompetitorStore extends CompetitorFactory {
*
* If no competitor with the ID requested is found, the call is a no-op, doing nothing, not even throwing an exception.
*/
Competitor updateCompetitor(String idAsString, String newName, Color newDisplayColor, String newEmail,
String newSailId, Nationality newNationality, URI newTeamImageUri, URI newFlagImageUri,
Competitor updateCompetitor(String idAsString, String newName, String newShortName, Color newDisplayColor, String newEmail,
Nationality newNationality, URI newTeamImageUri, URI newFlagImageUri,
Double timeOnTimeFactor, Duration timeOnDistanceAllowancePerNauticalMile, String searchTag);
void addNewCompetitors(Iterable<Competitor> competitors);
CompetitorDTO convertToCompetitorDTO(Competitor c);
void addNewCompetitorsWithBoat(Iterable<CompetitorWithBoat> competitorsWithBoat);
CompetitorWithoutBoatDTO convertToCompetitorDTO(Competitor competitor);
CompetitorDTO convertToCompetitorWithBoatDTO(Competitor c, Boat b);
CompetitorDTO convertToCompetitorWithOptionalBoatDTO(Competitor c);
Map<CompetitorDTO, BoatDTO> convertToCompetitorAndBoatDTOs(Map<Competitor, Boat> competitorsAndBoats);
/**
* Listeners added here are notified whenever {@link #updateCompetitor(String, String, Color, String, Nationality)} is called
@@ -80,4 +115,84 @@ public interface CompetitorStore extends CompetitorFactory {
void addCompetitorUpdateListener(CompetitorUpdateListener listener);
void removeCompetitorUpdateListener(CompetitorUpdateListener listener);
/**
* Updates the competitor with {@link Competitor#getId() ID} <code>id</code> by setting the name, sail ID and nationality to
* the values provided. Doing so will not fire any events nor will it replicate this change from a master to any replicas.
* The calling client has to make sure that the changes applied will reach replicas and all other interested clients. It will
* be sufficient to ensure that subsequent DTOs produced from the competitor modified will reflect the changes.<p>
*
* If no competitor with the ID requested is found, the call is a no-op, doing nothing, not even throwing an exception.
*/
CompetitorWithBoat updateCompetitorWithBoat(String idAsString, String newName, String newShortName, Color newDisplayColor, String newEmail,
Nationality newNationality, URI newTeamImageUri, URI newFlagImageUri,
Double timeOnTimeFactor, Duration timeOnDistanceAllowancePerNauticalMile, String searchTag, DynamicBoat boat);
/**
* If a valid boat is returned and the caller has information available that could be used to update the boat,
* the caller must check the result of {@link #isBoatToUpdateDuringGetOrCreate(Competitor)}, and if <code>true</code>,
* must call {@link #getOrCreateBoat(..)} to cause an update of the boat's values.
*/
Boat getExistingBoatByIdAsString(String idAsString);
/**
* When a boat is queried using {@link #getOrCreateBoat(...)},
* and the boat object for that ID already exists, it is generally returned unchanged, and the properties
* are not evaluated. This makes the data in this store generally "write-once." This
* method can be used to reset a boat object to what a tracking provider or an external system supplies to
* {@link #getOrCreateBoat(...)}. After calling this method, the
* next call to {@link #getOrCreateBoat(...)} with <code>boat</code>'s {@link Boat#getId() ID} will
* {@link #updateBoat(...)} the boat in its updatable properties such as sail ID and the color.
*/
void allowBoatResetToDefaults(Boat boat);
int getBoatsCount();
/**
* Removes all boats from this store. Use with due care.
*/
void clearBoats();
/**
* Obtains a non-live snapshot of the list of boats managed by this store.
*/
Iterable<? extends Boat> getBoats();
/**
* Obtains a non-live snapshot of the list of all boats which are NOT embedded into a competitor
*/
Iterable<? extends Boat> getStandaloneBoats();
/**
* Migrates an existing competitor with contained boat to a competitor without a boat. The contained boat will be
* removed from the store.
*
* @param competitorWithBoat
* the existing competitor with a contained boat
* @return the same {@code competitorWithBoat} with the {@link CompetitorWithBoat#getBoat() boat} reference set to
* {@code null}
*/
Competitor migrateToCompetitorWithoutBoat(CompetitorWithBoat competitorWithBoat);
/**
* Updates the boat with {@link Boat#getId() ID} <code>id</code> by setting the name, sail ID, etc. to
* the values provided. Doing so will not fire any events nor will it replicate this change from a master to any replicas.
* The calling client has to make sure that the changes applied will reach replicas and all other interested clients. It will
* be sufficient to ensure that subsequent DTOs produced from the competitor modified will reflect the changes.<p>
*
* If no boat with the ID requested is found, the call is a no-op, doing nothing, not even throwing an exception.
*/
Boat updateBoat(String idAsString, String newName, Color newColor, String newSailId);
void addNewBoats(Iterable<Boat> boats);
BoatDTO convertToBoatDTO(Boat boat);
/**
* Listeners added here are notified whenever {@link #updateBoat(...)} is called
* for any boat in this store.
*/
void addBoatUpdateListener(BoatUpdateListener listener);
void removeBoatUpdateListener(BoatUpdateListener listener);
}
@@ -0,0 +1,9 @@
package com.sap.sailing.domain.base;
/**
* A competitor having an assigned boat
* This makes sense e.g. in the context of a race where a competitor needs a boat in order to compete
* @author fmittag
*/
public interface CompetitorWithBoat extends Competitor, WithBoat {
}
@@ -10,7 +10,7 @@ import com.sap.sailing.domain.common.MarkType;
import com.sap.sailing.domain.common.PassingInstruction;
import com.sap.sse.common.Color;
public interface SharedDomainFactory extends CompetitorFactory {
public interface SharedDomainFactory extends CompetitorFactory, BoatFactory {
/**
* Looks up or, if not found, creates a {@link Nationality} object and re-uses <code>threeLetterIOCCode</code> also as the
@@ -0,0 +1,11 @@
package com.sap.sailing.domain.base;
import com.sap.sse.datamining.annotations.Connector;
/**
* Something with a boat.
*/
public interface WithBoat {
@Connector(messageKey = "Boat", ordinal = 10)
Boat getBoat();
}
@@ -2,39 +2,49 @@ package com.sap.sailing.domain.base.impl;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;
import com.sap.sailing.domain.base.Boat;
import com.sap.sailing.domain.base.BoatChangeListener;
import com.sap.sailing.domain.base.BoatClass;
import com.sap.sailing.domain.base.SharedDomainFactory;
import com.sap.sse.common.Color;
import com.sap.sse.common.Util;
import com.sap.sse.common.impl.NamedImpl;
import com.sap.sse.common.impl.RenamableImpl;
public class BoatImpl extends NamedImpl implements DynamicBoat {
public class BoatImpl extends RenamableImpl implements DynamicBoat {
private static final long serialVersionUID = 3489730487528955788L;
private final BoatClass boatClass;
private final Serializable id;
private String sailID;
private final Color color;
private Color color;
private transient Set<BoatChangeListener> listeners;
public BoatImpl(String name, BoatClass boatClass, String sailID) {
this(name, boatClass, sailID, null);
public BoatImpl(Serializable id, String name, BoatClass boatClass, String sailId) {
this(id, name, boatClass, sailId, null);
}
public BoatImpl(String name, BoatClass boatClass, String sailID, Color color) {
public BoatImpl(Serializable id, String name, BoatClass boatClass, String sailID, Color color) {
super(name);
this.id = id;
this.boatClass = boatClass;
this.sailID = sailID;
this.color = color;
this.listeners = new HashSet<BoatChangeListener>();
}
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
ois.defaultReadObject();
listeners = new HashSet<BoatChangeListener>();
}
@Override
public Serializable getId() {
return id;
}
@Override
public BoatClass getBoatClass() {
return boatClass;
@@ -49,7 +59,18 @@ public class BoatImpl extends NamedImpl implements DynamicBoat {
public Color getColor() {
return color;
}
@Override
public void setName(String newName) {
final String oldName = getName();
if (!Util.equalsWithNull(oldName, newName)) {
super.setName(newName);
for (BoatChangeListener listener : getListeners()) {
listener.nameChanged(oldName, newName);
}
}
}
@Override
public void setSailId(String newSailId) {
final String oldSailId = this.sailID;
@@ -61,6 +82,22 @@ public class BoatImpl extends NamedImpl implements DynamicBoat {
}
}
@Override
public void setColor(Color newColor) {
final Color oldColor = this.color;
if (!Util.equalsWithNull(oldColor, newColor)) {
this.color = newColor;
for (BoatChangeListener listener : getListeners()) {
listener.colorChanged(oldColor, newColor);
}
}
}
@Override
public Boat resolve(SharedDomainFactory domainFactory) {
return domainFactory.getOrCreateBoat(getId(), getName(), getBoatClass(), getSailID(), getColor());
}
@Override
public void addBoatChangeListener(BoatChangeListener listener) {
synchronized (listeners) {
@@ -69,7 +106,7 @@ public class BoatImpl extends NamedImpl implements DynamicBoat {
}
@Override
public void removeCompetitorChangeListener(BoatChangeListener listener) {
public void removeBoatChangeListener(BoatChangeListener listener) {
synchronized (listeners) {
listeners.remove(listener);
}
@@ -80,4 +117,9 @@ public class BoatImpl extends NamedImpl implements DynamicBoat {
return new HashSet<BoatChangeListener>(listeners);
}
}
@Override
public String toString() {
return getName()==null?getSailID():getName();
}
}
@@ -0,0 +1,31 @@
package com.sap.sailing.domain.base.impl;
import com.sap.sailing.domain.base.Boat;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.CompetitorAndBoat;
/**
* A utility class to hold a combination of a competitor and a boat
* @author Frank Mittag
*
*/
public class CompetitorAndBoatImpl implements CompetitorAndBoat {
private static final long serialVersionUID = 6057339511208358777L;
private final Competitor competitor;
private final Boat boat;
public CompetitorAndBoatImpl(Competitor competitor, Boat boat) {
this.competitor = competitor;
this.boat = boat;
}
@Override
public Competitor getCompetitor() {
return competitor;
}
@Override
public Boat getBoat() {
return boat;
}
}
@@ -18,9 +18,9 @@ import com.sap.sse.common.Util;
public class CompetitorImpl implements DynamicCompetitor {
private static final long serialVersionUID = 294603681016643157L;
private final DynamicTeam team;
private final DynamicBoat boat;
private final Serializable id;
private String name;
private String shortName;
private String searchTag;
private Color color;
private transient Set<CompetitorChangeListener> listeners;
@@ -29,11 +29,11 @@ public class CompetitorImpl implements DynamicCompetitor {
private Double timeOnTimeFactor;
private Duration timeOnDistanceAllowancePerNauticalMile;
public CompetitorImpl(Serializable id, String name, Color color, String email, URI flagImage, DynamicTeam team, DynamicBoat boat, Double timeOnTimeFactor, Duration timeOnDistanceAllowancePerNauticalMile, String searchTag) {
public CompetitorImpl(Serializable id, String name, String shortName, Color color, String email, URI flagImage, DynamicTeam team, Double timeOnTimeFactor, Duration timeOnDistanceAllowancePerNauticalMile, String searchTag) {
this.id = id;
this.name = name;
this.shortName = shortName;
this.team = team;
this.boat = boat;
this.color = color;
this.email = email;
this.flagImage = flagImage;
@@ -52,7 +52,28 @@ public class CompetitorImpl implements DynamicCompetitor {
public String getName() {
return name;
}
@Override
public String getShortName() {
return shortName;
}
@Override
public String getShortInfo() {
final String result;
if (getShortName() != null) {
result = getShortName();
} else {
result = null;
}
return result;
}
@Override
public boolean hasBoat() {
return false;
}
@Override
public String toString() {
return getName();
@@ -68,7 +89,19 @@ public class CompetitorImpl implements DynamicCompetitor {
}
}
}
@Override
public void setShortName(String newShortName) {
final String oldShortName = this.shortName;
if (!Util.equalsWithNull(oldShortName, newShortName)) {
this.shortName = newShortName;
for (CompetitorChangeListener listener : getListeners()) {
listener.shortNameChanged(oldShortName, newShortName);
}
}
}
@Override
public Serializable getId() {
return id;
@@ -79,21 +112,14 @@ public class CompetitorImpl implements DynamicCompetitor {
return team;
}
@Override
public Nationality getNationality() {
return getTeam() == null ? null : getTeam().getNationality();
}
@Override
public DynamicBoat getBoat() {
return boat;
}
@Override
public Competitor resolve(SharedDomainFactory domainFactory) {
Competitor result = domainFactory
.getOrCreateCompetitor(getId(), getName(), getColor(), getEmail(), getFlagImage(), getTeam(),
getBoat(), getTimeOnTimeFactor(), getTimeOnDistanceAllowancePerNauticalMile(), searchTag);
.getOrCreateCompetitor(getId(), getName(), getShortName(), getColor(), getEmail(), getFlagImage(), getTeam(),
getTimeOnTimeFactor(), getTimeOnDistanceAllowancePerNauticalMile(), searchTag);
return result;
}
@@ -117,7 +143,6 @@ public class CompetitorImpl implements DynamicCompetitor {
synchronized (listeners) {
listeners.add(listener);
}
getBoat().addBoatChangeListener(listener);
getTeam().addNationalityChangeListener(listener);
}
@@ -126,7 +151,6 @@ public class CompetitorImpl implements DynamicCompetitor {
synchronized (listeners) {
listeners.remove(listener);
}
getBoat().removeCompetitorChangeListener(listener);
getTeam().removeNationalityChangeListener(listener);
}
@@ -0,0 +1,78 @@
package com.sap.sailing.domain.base.impl;
import java.io.Serializable;
import java.net.URI;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.SharedDomainFactory;
import com.sap.sse.common.Color;
import com.sap.sse.common.Duration;
public class CompetitorWithBoatImpl extends CompetitorImpl implements DynamicCompetitorWithBoat {
private static final long serialVersionUID = 22679449208503264L;
private DynamicBoat boat;
public CompetitorWithBoatImpl(Serializable id, String name, String shortName, Color color, String email,
URI flagImage, DynamicTeam team, Double timeOnTimeFactor, Duration timeOnDistanceAllowancePerNauticalMile,
String searchTag, DynamicBoat boat) {
super(id, name, shortName, color, email, flagImage, team, timeOnTimeFactor, timeOnDistanceAllowancePerNauticalMile,
searchTag);
this.boat = boat;
}
public CompetitorWithBoatImpl(Competitor competitor, DynamicBoat boat) {
this(competitor.getId(), competitor.getName(), competitor.getShortName(), competitor.getColor(), competitor.getEmail(),
competitor.getFlagImage(), (DynamicTeam) competitor.getTeam(), competitor.getTimeOnTimeFactor(),
competitor.getTimeOnDistanceAllowancePerNauticalMile(), competitor.getSearchTag(), boat);
}
public Competitor resolve(SharedDomainFactory domainFactory) {
final Competitor result;
if (!hasBoat()) {
// bug2822: this is a migrated competitor that had its default boat removed because it occurs in a boats-can-change regatta.
// De-serialize as a CompetitorImpl, not as a CompetitorWithBoatImpl; here we can, other than during the migration itself
// because there references to the CompetitorWithBoatImpl object may already exist that cannot easily be found and replaced.
// Here, we at least must make sure not to force a CompetitorWithBoatImpl and allow for a CompetitorImpl.
result = super.resolve(domainFactory);
} else {
result = domainFactory.getOrCreateCompetitorWithBoat(getId(), getName(), getShortName(), getColor(),
getEmail(), getFlagImage(), getTeam(), getTimeOnTimeFactor(),
getTimeOnDistanceAllowancePerNauticalMile(), getSearchTag(), getBoat());
}
return result;
}
@Override
public DynamicBoat getBoat() {
return boat;
}
@Override
public void clearBoat() {
this.boat = null;
}
@Override
public boolean hasBoat() {
return boat != null;
}
@Override
public String getShortInfo() {
final String superResult = super.getShortInfo();
final String result;
if (superResult != null) {
result = superResult;
} else if (getBoat() != null) {
result = getBoat().getSailID() != null ? getBoat().getSailID() : getBoat().getName();
} else {
result = null;
}
return result;
}
@Override
public String toString() {
return super.toString()+" with boat "+getBoat();
}
}
@@ -1,13 +1,13 @@
package com.sap.sailing.domain.base.impl;
import com.sap.sailing.domain.base.Boat;
import com.sap.sailing.domain.base.BoatChangeListener;
import com.sap.sse.common.Color;
import com.sap.sse.common.Renamable;
public interface DynamicBoat extends Boat {
void setSailId(String newSailId);
public interface DynamicBoat extends Boat, Renamable {
void setSailId(String sailId);
void addBoatChangeListener(BoatChangeListener listener);
void removeCompetitorChangeListener(BoatChangeListener listener);
void setName(String name);
void setColor(Color color);
}
Loaded 100 of 521 files, more files were not shown because too many files have changed in this diff. Show more