diff --git a/java/com.sap.sailing.dashboards.gwt/src/main/java/com/sap/sailing/dashboards/gwt/client/actions/GetCompetitorInLeaderboardAction.java b/java/com.sap.sailing.dashboards.gwt/src/main/java/com/sap/sailing/dashboards/gwt/client/actions/GetCompetitorInLeaderboardAction.java index a5cfd3fd5d0..00b89b38845 100644 --- a/java/com.sap.sailing.dashboards.gwt/src/main/java/com/sap/sailing/dashboards/gwt/client/actions/GetCompetitorInLeaderboardAction.java +++ b/java/com.sap.sailing.dashboards.gwt/src/main/java/com/sap/sailing/dashboards/gwt/client/actions/GetCompetitorInLeaderboardAction.java @@ -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 competitorsFromBestToWorst = lb.getCompetitorsFromBestToWorst(MillisecondsTimePoint.now()); + List competitorDTOs = dashboardDispatchContext.getRacingEventService().getBaseDomainFactory().getCompetitorDTOList(competitorsFromBestToWorst); + result.setCompetitors(competitorDTOs); return result; } } diff --git a/java/com.sap.sailing.dashboards.gwt/src/main/java/com/sap/sailing/dashboards/gwt/client/widgets/startanalysis/card/StartAnalysisCard.java b/java/com.sap.sailing.dashboards.gwt/src/main/java/com/sap/sailing/dashboards/gwt/client/widgets/startanalysis/card/StartAnalysisCard.java index 4243ec2946d..e8aa94cfe14 100644 --- a/java/com.sap.sailing.dashboards.gwt/src/main/java/com/sap/sailing/dashboards/gwt/client/widgets/startanalysis/card/StartAnalysisCard.java +++ b/java/com.sap.sailing.dashboards.gwt/src/main/java/com/sap/sailing/dashboards/gwt/client/widgets/startanalysis/card/StartAnalysisCard.java @@ -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)); diff --git a/java/com.sap.sailing.dashboards.gwt/src/main/java/com/sap/sailing/dashboards/gwt/server/util/actions/startanalysis/StartAnalysisDTOFactory.java b/java/com.sap.sailing.dashboards.gwt/src/main/java/com/sap/sailing/dashboards/gwt/server/util/actions/startanalysis/StartAnalysisDTOFactory.java index b5d6940aac2..87a97a68d40 100644 --- a/java/com.sap.sailing.dashboards.gwt/src/main/java/com/sap/sailing/dashboards/gwt/server/util/actions/startanalysis/StartAnalysisDTOFactory.java +++ b/java/com.sap.sailing.dashboards.gwt/src/main/java/com/sap/sailing/dashboards/gwt/server/util/actions/startanalysis/StartAnalysisDTOFactory.java @@ -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; } diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/CompetitorDescriptor.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/CompetitorDescriptor.java index 07b0faf6443..9e52edd54dd 100644 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/CompetitorDescriptor.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/CompetitorDescriptor.java @@ -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 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 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=" diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/AbstractLeaderboardDTO.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/AbstractLeaderboardDTO.java index 2952cb1e629..657dc991b86 100755 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/AbstractLeaderboardDTO.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/AbstractLeaderboardDTO.java @@ -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; diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/BoatDTO.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/BoatDTO.java old mode 100644 new mode 100755 index 6d92a20e23b..8ce4fd1d58b --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/BoatDTO.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/BoatDTO.java @@ -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(); + } } diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/CompetitorDTO.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/CompetitorDTO.java index cc14be28357..83118be02bf 100755 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/CompetitorDTO.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/CompetitorDTO.java @@ -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(); - } diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/CompetitorDTOImpl.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/CompetitorDTOImpl.java index cca61c67c3f..54592fb6494 100755 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/CompetitorDTOImpl.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/CompetitorDTOImpl.java @@ -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; - } + } } diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/CompetitorWithoutBoatDTO.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/CompetitorWithoutBoatDTO.java new file mode 100644 index 00000000000..b1f3adfc5d9 --- /dev/null +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/CompetitorWithoutBoatDTO.java @@ -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(); + +} diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/CompetitorWithoutBoatDTOImpl.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/CompetitorWithoutBoatDTOImpl.java new file mode 100644 index 00000000000..601b4631d76 --- /dev/null +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/CompetitorWithoutBoatDTOImpl.java @@ -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; + } +} diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/LeaderboardDTO.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/LeaderboardDTO.java index 1bf5f6e4902..ef18169ceb9 100644 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/LeaderboardDTO.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/LeaderboardDTO.java @@ -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 orderedCompetitors) { competitorOrderingPerRaceColumnName.put(raceColumnName, orderedCompetitors); } diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/LeaderboardEntryDTO.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/LeaderboardEntryDTO.java index 4435b336e9f..e1a17dc135a 100755 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/LeaderboardEntryDTO.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/LeaderboardEntryDTO.java @@ -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 null 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) { diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/LeaderboardRowDTO.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/LeaderboardRowDTO.java index 28354bfc27b..e9f22db5586 100755 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/LeaderboardRowDTO.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/LeaderboardRowDTO.java @@ -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 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; diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/PairingListDTO.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/PairingListDTO.java index 8ec76da73fb..46f82a6aa2b 100644 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/PairingListDTO.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/PairingListDTO.java @@ -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>>> pairingList; + private List>>> pairingList; private List raceColumnNames; public PairingListDTO() { } - public PairingListDTO(List>>> result) { + public PairingListDTO(List>>> result) { this(result, null); } - public PairingListDTO(List>>> result, List raceColumnNames) { + public PairingListDTO(List>>> result, List raceColumnNames) { this.pairingList = result; this.raceColumnNames = raceColumnNames; } - public List>>> getPairingList() { + public List>>> getPairingList() { return pairingList; } public List getBoats() { List boats = new ArrayList<>(); - for (List> fleet : this.pairingList.get(0)) { - for (Pair competitorAndBoatPair : fleet) { + for (List> fleet : this.pairingList.get(0)) { + for (Pair competitorAndBoatPair : fleet) { if (!boats.contains(competitorAndBoatPair.getB())) { boats.add(competitorAndBoatPair.getB()); } diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/PairingListTemplateDTO.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/PairingListTemplateDTO.java index fda722cedce..974f2df6414 100644 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/PairingListTemplateDTO.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/PairingListTemplateDTO.java @@ -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 selectedFlightNames) { + public PairingListTemplateDTO(int flightCount, int groupCount, int competitorCount, int flightMultiplier, + int[][] pairingListTemplate, double quality, Iterable 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; } diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/PreviousCompetitorDTOImpl.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/PreviousCompetitorDTOImpl.java index 187f605a32d..a220d771cf6 100755 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/PreviousCompetitorDTOImpl.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/PreviousCompetitorDTOImpl.java @@ -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()+ diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/racelog/tracking/DeviceMappingConstants.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/racelog/tracking/DeviceMappingConstants.java index 8095055b859..4bad8de8796 100644 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/racelog/tracking/DeviceMappingConstants.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/racelog/tracking/DeviceMappingConstants.java @@ -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"; diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/security/Permission.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/security/Permission.java index e8cc4b42e5b..d1cfc884106 100755 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/security/Permission.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/security/Permission.java @@ -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, diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/security/SailingPermissionsForRoleProvider.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/security/SailingPermissionsForRoleProvider.java index 7c386a881d2..bcce8d546bd 100755 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/security/SailingPermissionsForRoleProvider.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/security/SailingPermissionsForRoleProvider.java @@ -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, diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/impl/BoatJsonConstants.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/impl/BoatJsonConstants.java new file mode 100644 index 00000000000..31afcbd48f2 --- /dev/null +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/impl/BoatJsonConstants.java @@ -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"; +} diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/impl/CompetitorJsonConstants.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/impl/CompetitorJsonConstants.java index 2d083a0ad1c..58478fd34ef 100644 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/impl/CompetitorJsonConstants.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/impl/CompetitorJsonConstants.java @@ -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"; diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/DomainObjectFactory.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/DomainObjectFactory.java index f92b84e6a8a..76ed6618127 100755 --- a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/DomainObjectFactory.java +++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/DomainObjectFactory.java @@ -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 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 loadAllCompetitors(); + /** + * Loads all boats and resolves them via the domain factory. + */ + Collection loadAllBoats(); + DomainFactory getBaseDomainFactory(); Iterable> loadAllDeviceConfigurations(); diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/MongoObjectFactory.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/MongoObjectFactory.java index dadb01a5ba2..c3e958ea953 100755 --- a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/MongoObjectFactory.java +++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/MongoObjectFactory.java @@ -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 competitors); + void storeCompetitors(Iterable 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 boats); + + void removeAllBoats(); + + void removeBoat(Boat boat); DB getDatabase(); diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/CollectionNames.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/CollectionNames.java index 29f55619367..19329ceb66f 100755 --- a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/CollectionNames.java +++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/CollectionNames.java @@ -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, diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/DomainObjectFactoryImpl.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/DomainObjectFactoryImpl.java index 817a62fd46e..bb14eda60d2 100644 --- a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/DomainObjectFactoryImpl.java +++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/DomainObjectFactoryImpl.java @@ -12,6 +12,7 @@ import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Map.Entry; +import java.util.Optional; import java.util.Set; import java.util.UUID; import java.util.concurrent.ExecutionException; @@ -19,6 +20,7 @@ import java.util.concurrent.FutureTask; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; +import java.util.function.Supplier; import java.util.logging.Level; import java.util.logging.Logger; @@ -99,29 +101,40 @@ import com.sap.sailing.domain.abstractlog.regatta.RegattaLog; 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.RegattaLogDeviceMappingEventImpl; 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.abstractlog.regatta.events.impl.AbstractRegattaLogDeviceCompetitorSensorDataMappingEventImpl; import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogCloseOpenEndedDeviceMappingEventImpl; import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogDefineMarkEventImpl; +import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogDeviceBoatBravoExtendedMappingEventImpl; +import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogDeviceBoatBravoMappingEventImpl; +import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogDeviceBoatExpeditionExtendedMappingEventImpl; +import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogDeviceBoatMappingEventImpl; import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogDeviceCompetitorBravoExtendedMappingEventImpl; import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogDeviceCompetitorBravoMappingEventImpl; import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogDeviceCompetitorExpeditionExtendedMappingEventImpl; 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.events.impl.RegattaLogRegisterBoatEventImpl; import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogRegisterCompetitorEventImpl; import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogRevokeEventImpl; import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogSetCompetitorTimeOnDistanceAllowancePerNauticalMileEventImpl; import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogSetCompetitorTimeOnTimeFactorEventImpl; import com.sap.sailing.domain.abstractlog.regatta.impl.RegattaLogImpl; import com.sap.sailing.domain.anniversary.DetailedRaceInfo; +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.ControlPoint; import com.sap.sailing.domain.base.ControlPointWithTwoMarks; import com.sap.sailing.domain.base.CourseArea; @@ -180,6 +193,7 @@ import com.sap.sailing.domain.common.racelog.Flags; import com.sap.sailing.domain.common.racelog.RaceLogRaceStatus; import com.sap.sailing.domain.common.racelog.RacingProcedureType; import com.sap.sailing.domain.common.racelog.tracking.TransformationException; +import com.sap.sailing.domain.common.tracking.impl.CompetitorJsonConstants; import com.sap.sailing.domain.leaderboard.DelayedLeaderboardCorrections; import com.sap.sailing.domain.leaderboard.EventResolver; import com.sap.sailing.domain.leaderboard.FlexibleLeaderboard; @@ -219,9 +233,11 @@ import com.sap.sailing.domain.tracking.WindTrack; import com.sap.sailing.domain.tracking.impl.WindTrackImpl; import com.sap.sailing.server.gateway.deserialization.JsonDeserializationException; import com.sap.sailing.server.gateway.deserialization.JsonDeserializer; -import com.sap.sailing.server.gateway.deserialization.impl.CompetitorJsonDeserializer; +import com.sap.sailing.server.gateway.deserialization.impl.BoatJsonDeserializer; +import com.sap.sailing.server.gateway.deserialization.impl.CompetitorWithBoatRefJsonDeserializer; import com.sap.sailing.server.gateway.deserialization.impl.DeviceConfigurationJsonDeserializer; import com.sap.sailing.server.gateway.deserialization.impl.Helpers; +import com.sap.sailing.server.gateway.deserialization.impl.LegacyCompetitorWithContainedBoatJsonDeserializer; import com.sap.sailing.server.gateway.deserialization.impl.RegattaConfigurationJsonDeserializer; import com.sap.sse.common.Color; import com.sap.sse.common.Duration; @@ -233,6 +249,7 @@ import com.sap.sse.common.TypeBasedServiceFinderFactory; import com.sap.sse.common.Util; import com.sap.sse.common.Util.Pair; import com.sap.sse.common.Util.Triple; +import com.sap.sse.common.WithID; import com.sap.sse.common.impl.AbstractColor; import com.sap.sse.common.impl.MillisecondsDurationImpl; import com.sap.sse.common.impl.MillisecondsTimePoint; @@ -247,39 +264,46 @@ import com.sap.sse.util.ThreadPoolUtil; public class DomainObjectFactoryImpl implements DomainObjectFactory { private static final Logger logger = Logger.getLogger(DomainObjectFactoryImpl.class.getName()); - private final CompetitorJsonDeserializer competitorDeserializer; + private final LegacyCompetitorWithContainedBoatJsonDeserializer legacyCompetitorWithBoatDeserializer; + private final CompetitorWithBoatRefJsonDeserializer competitorWithBoatRefDeserializer; + private final BoatJsonDeserializer boatDeserializer; private final DB database; - + private final DomainFactory baseDomainFactory; private final TypeBasedServiceFinderFactory serviceFinderFactory; private final TypeBasedServiceFinder deviceIdentifierServiceFinder; private final TypeBasedServiceFinder raceTrackingConnectivityParamsServiceFinder; - + /** - * Uses null as the {@link TypeBasedServiceFinder}, meaning that no {@link DeviceIdentifier}s can be loaded - * using this instance of a {@link DomainObjectFactory}. + * Uses null as the {@link TypeBasedServiceFinder}, meaning that no {@link DeviceIdentifier}s can be + * loaded using this instance of a {@link DomainObjectFactory}. */ public DomainObjectFactoryImpl(DB db, DomainFactory baseDomainFactory) { this(db, baseDomainFactory, /* deviceTypeServiceFinder */ null); } - - public DomainObjectFactoryImpl(DB db, DomainFactory baseDomainFactory, TypeBasedServiceFinderFactory serviceFinderFactory) { + + public DomainObjectFactoryImpl(DB db, DomainFactory baseDomainFactory, + TypeBasedServiceFinderFactory serviceFinderFactory) { super(); this.serviceFinderFactory = serviceFinderFactory; if (serviceFinderFactory != null) { - this.deviceIdentifierServiceFinder = serviceFinderFactory.createServiceFinder(DeviceIdentifierMongoHandler.class); + this.deviceIdentifierServiceFinder = serviceFinderFactory + .createServiceFinder(DeviceIdentifierMongoHandler.class); this.deviceIdentifierServiceFinder.setFallbackService(new PlaceHolderDeviceIdentifierMongoHandler()); - this.raceTrackingConnectivityParamsServiceFinder = serviceFinderFactory.createServiceFinder(RaceTrackingConnectivityParametersHandler.class); + this.raceTrackingConnectivityParamsServiceFinder = serviceFinderFactory + .createServiceFinder(RaceTrackingConnectivityParametersHandler.class); } else { this.deviceIdentifierServiceFinder = null; this.raceTrackingConnectivityParamsServiceFinder = null; } this.baseDomainFactory = baseDomainFactory; - this.competitorDeserializer = CompetitorJsonDeserializer.create(baseDomainFactory); + this.legacyCompetitorWithBoatDeserializer = LegacyCompetitorWithContainedBoatJsonDeserializer.create(baseDomainFactory); + this.competitorWithBoatRefDeserializer = CompetitorWithBoatRefJsonDeserializer.create(baseDomainFactory); + this.boatDeserializer = BoatJsonDeserializer.create(baseDomainFactory); this.database = db; } - + @Override public DomainFactory getBaseDomainFactory() { return baseDomainFactory; @@ -300,7 +324,7 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { return null; } } - + public static TimePoint loadTimePoint(DBObject object, String fieldName) { TimePoint result = null; Number timePointAsNumber = (Number) object.get(fieldName); @@ -313,7 +337,7 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { public static TimePoint loadTimePoint(DBObject object, FieldNames field) { return loadTimePoint(object, field.name()); } - + public static TimeRange loadTimeRange(DBObject object, FieldNames field) { DBObject timeRangeObj = (DBObject) object.get(field.name()); if (timeRangeObj == null) { @@ -323,6 +347,7 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { TimePoint to = loadTimePoint(timeRangeObj, FieldNames.TO_MILLIS); return new TimeRangeImpl(from, to); } + /** * Loads a {@link TimePoint} on the given object at {@link FieldNames#TIME_AS_MILLIS}. */ @@ -365,8 +390,8 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { "Setting the unique index on the %s collection failed because you have too many duplicates. " + "This leads to the mongo error code %s and the following message: %s \nTo fix this follow " + "the steps provided on the wiki page: http://wiki.sapsailing.com/wiki/howto/misc/cook-book#Remove-" - + "duplicates-from-WIND_TRACK-collection", CollectionNames.WIND_TRACKS.name(), - exception.getCode(), exception.getMessage())); + + "duplicates-from-WIND_TRACK-collection", + CollectionNames.WIND_TRACKS.name(), exception.getCode(), exception.getMessage())); } else { logger.severe(String.format( "Setting the unique index on the %s collection failed with error code %s and message: %s", @@ -376,7 +401,8 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { } @Override - public Leaderboard loadLeaderboard(String name, RegattaRegistry regattaRegistry, LeaderboardRegistry leaderboardRegistry) { + public Leaderboard loadLeaderboard(String name, RegattaRegistry regattaRegistry, + LeaderboardRegistry leaderboardRegistry) { DBCollection leaderboardCollection = database.getCollection(CollectionNames.LEADERBOARDS.name()); Leaderboard result = null; try { @@ -387,7 +413,7 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { } } catch (Exception e) { // something went wrong during DB access; report, then use empty new wind track - logger.log(Level.SEVERE, "Error connecting to MongoDB, unable to load leaderboard "+name+"."); + logger.log(Level.SEVERE, "Error connecting to MongoDB, unable to load leaderboard " + name + "."); logger.log(Level.SEVERE, "loadLeaderboard", e); } return result; @@ -398,12 +424,14 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { String leaderboardName, String wrappedRegattaLeaderboardName, LeaderboardRegistry leaderboardRegistry) { final RegattaLeaderboardWithEliminations result; BasicDBList eliminatedCompetitorIds = (BasicDBList) dbLeaderboard.get(FieldNames.ELMINATED_COMPETITORS.name()); - result = new DelegatingRegattaLeaderboardWithCompetitorElimination(()-> - (RegattaLeaderboard) leaderboardRegistry.getLeaderboardByName(wrappedRegattaLeaderboardName), leaderboardName); + result = new DelegatingRegattaLeaderboardWithCompetitorElimination( + () -> (RegattaLeaderboard) leaderboardRegistry.getLeaderboardByName(wrappedRegattaLeaderboardName), + leaderboardName); for (Object eliminatedCompetitorId : eliminatedCompetitorIds) { - Competitor eliminatedCompetitor = baseDomainFactory.getCompetitorStore().getExistingCompetitorById((Serializable) eliminatedCompetitorId); + Competitor eliminatedCompetitor = baseDomainFactory.getCompetitorStore() + .getExistingCompetitorById((Serializable) eliminatedCompetitorId); if (eliminatedCompetitor == null) { - logger.warning("Couldn't find eliminated competitor with ID "+eliminatedCompetitorId); + logger.warning("Couldn't find eliminated competitor with ID " + eliminatedCompetitorId); } else { result.setEliminated(eliminatedCompetitor, true); } @@ -431,25 +459,29 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { * {@link RegattaLeaderboard} cannot be found; the leaderboard loaded or found in * leaderboardRegistry, otherwise */ - private Leaderboard loadLeaderboard(DBObject dbLeaderboard, RegattaRegistry regattaRegistry, LeaderboardRegistry leaderboardRegistry, - LeaderboardGroup groupForMetaLeaderboard) { + private Leaderboard loadLeaderboard(DBObject dbLeaderboard, RegattaRegistry regattaRegistry, + LeaderboardRegistry leaderboardRegistry, LeaderboardGroup groupForMetaLeaderboard) { Leaderboard result = null; String leaderboardName = (String) dbLeaderboard.get(FieldNames.LEADERBOARD_NAME.name()); if (leaderboardRegistry != null) { result = leaderboardRegistry.getLeaderboardByName(leaderboardName); } if (result == null) { - String wrappedRegattaLeaderboardName = (String) dbLeaderboard.get(FieldNames.WRAPPED_REGATTA_LEADERBOARD_NAME.name()); + String wrappedRegattaLeaderboardName = (String) dbLeaderboard + .get(FieldNames.WRAPPED_REGATTA_LEADERBOARD_NAME.name()); if (wrappedRegattaLeaderboardName != null) { - result = loadRegattaLeaderboardWithEliminations(dbLeaderboard, leaderboardName, wrappedRegattaLeaderboardName, leaderboardRegistry); + result = loadRegattaLeaderboardWithEliminations(dbLeaderboard, leaderboardName, + wrappedRegattaLeaderboardName, leaderboardRegistry); } else { if (groupForMetaLeaderboard != null) { result = new LeaderboardGroupMetaLeaderboard(groupForMetaLeaderboard, - loadScoringScheme(dbLeaderboard), loadResultDiscardingRule(dbLeaderboard, FieldNames.LEADERBOARD_DISCARDING_THRESHOLDS)); + loadScoringScheme(dbLeaderboard), + loadResultDiscardingRule(dbLeaderboard, FieldNames.LEADERBOARD_DISCARDING_THRESHOLDS)); groupForMetaLeaderboard.setOverallLeaderboard(result); } else { String regattaName = (String) dbLeaderboard.get(FieldNames.REGATTA_NAME.name()); - ThresholdBasedResultDiscardingRule resultDiscardingRule = loadResultDiscardingRule(dbLeaderboard, FieldNames.LEADERBOARD_DISCARDING_THRESHOLDS); + ThresholdBasedResultDiscardingRule resultDiscardingRule = loadResultDiscardingRule(dbLeaderboard, + FieldNames.LEADERBOARD_DISCARDING_THRESHOLDS); if (regattaName == null) { result = loadFlexibleLeaderboard(dbLeaderboard, resultDiscardingRule); } else { @@ -458,8 +490,10 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { } } if (result != null) { - DelayedLeaderboardCorrections loadedLeaderboardCorrections = new DelayedLeaderboardCorrectionsImpl(result, baseDomainFactory); - loadLeaderboardCorrections(dbLeaderboard, loadedLeaderboardCorrections, result.getScoreCorrection()); + DelayedLeaderboardCorrections loadedLeaderboardCorrections = new DelayedLeaderboardCorrectionsImpl( + result, baseDomainFactory); + loadLeaderboardCorrections(dbLeaderboard, loadedLeaderboardCorrections, + result.getScoreCorrection()); loadSuppressedCompetitors(dbLeaderboard, loadedLeaderboardCorrections); loadColumnFactors(dbLeaderboard, result); } @@ -470,7 +504,7 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { // add the leaderboard to the registry if (leaderboardRegistry != null) { leaderboardRegistry.addLeaderboard(result); - logger.info("loaded leaderboard "+result.getName()+" into "+leaderboardRegistry); + logger.info("loaded leaderboard " + result.getName() + " into " + leaderboardRegistry); } } } @@ -487,15 +521,18 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { if (raceColumn != null) { raceColumn.setFactor(factor); } else { - logger.warning("Expected to find race column named "+raceColumnName+" in leaderboard "+result.getName()+ - " to apply column factor "+factor+", but the race column wasn't found. Ignoring factor."); + logger.warning("Expected to find race column named " + raceColumnName + " in leaderboard " + + result.getName() + " to apply column factor " + factor + + ", but the race column wasn't found. Ignoring factor."); } } } } - private void loadSuppressedCompetitors(DBObject dbLeaderboard, DelayedLeaderboardCorrections loadedLeaderboardCorrections) { - BasicDBList dbSuppressedCompetitorIDs = (BasicDBList) dbLeaderboard.get(FieldNames.LEADERBOARD_SUPPRESSED_COMPETITOR_IDS.name()); + private void loadSuppressedCompetitors(DBObject dbLeaderboard, + DelayedLeaderboardCorrections loadedLeaderboardCorrections) { + BasicDBList dbSuppressedCompetitorIDs = (BasicDBList) dbLeaderboard + .get(FieldNames.LEADERBOARD_SUPPRESSED_COMPETITOR_IDS.name()); if (dbSuppressedCompetitorIDs != null) { for (Object competitorId : dbSuppressedCompetitorIDs) { loadedLeaderboardCorrections.suppressCompetitorByID((Serializable) competitorId); @@ -504,7 +541,8 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { } /** - * @param dbObject expects to find a field identified by field which holds a {@link BasicDBList} + * @param dbObject + * expects to find a field identified by field which holds a {@link BasicDBList} */ private ThresholdBasedResultDiscardingRule loadResultDiscardingRule(DBObject dbObject, FieldNames field) { BasicDBList dbDiscardIndexResultsStartingWithHowManyRaces = (BasicDBList) dbObject.get(field.name()); @@ -526,27 +564,30 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { /** * @return null if the regatta cannot be resolved; otherwise the leaderboard for the regatta specified */ - private RegattaLeaderboard loadRegattaLeaderboard(String leaderboardName, String regattaName, DBObject dbLeaderboard, - ThresholdBasedResultDiscardingRule resultDiscardingRule, RegattaRegistry regattaRegistry) { + private RegattaLeaderboard loadRegattaLeaderboard(String leaderboardName, String regattaName, + DBObject dbLeaderboard, ThresholdBasedResultDiscardingRule resultDiscardingRule, + RegattaRegistry regattaRegistry) { RegattaLeaderboard result = null; Regatta regatta = regattaRegistry.getRegatta(new RegattaName(regattaName)); if (regatta == null) { - logger.info("Couldn't find regatta "+regattaName+" for corresponding regatta leaderboard. Not loading regatta leaderboard."); + logger.info("Couldn't find regatta " + regattaName + + " for corresponding regatta leaderboard. Not loading regatta leaderboard."); } else { result = new RegattaLeaderboardImpl(regatta, resultDiscardingRule); - result.setName(leaderboardName); // this will temporarily set the display name; it will be adjusted later if a display name is found + result.setName(leaderboardName); // this will temporarily set the display name; it will be adjusted later if + // a display name is found } return result; } - + private RaceLogStore getRaceLogStore() { - return MongoRaceLogStoreFactory.INSTANCE.getMongoRaceLogStore( - new MongoObjectFactoryImpl(database, serviceFinderFactory), this); + return MongoRaceLogStoreFactory.INSTANCE + .getMongoRaceLogStore(new MongoObjectFactoryImpl(database, serviceFinderFactory), this); } - + private RegattaLogStore getRegattaLogStore() { - return MongoRegattaLogStoreFactory.INSTANCE.getMongoRegattaLogStore( - new MongoObjectFactoryImpl(database, serviceFinderFactory), this); + return MongoRegattaLogStoreFactory.INSTANCE + .getMongoRegattaLogStore(new MongoObjectFactoryImpl(database, serviceFinderFactory), this); } private FlexibleLeaderboard loadFlexibleLeaderboard(DBObject dbLeaderboard, @@ -556,21 +597,21 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { if (dbRaceColumns == null) { // this was probably an orphaned overall leaderboard logger.warning("Probably found orphan overall leaderboard named " - + dbLeaderboard.get(FieldNames.LEADERBOARD_NAME.name())+". Ignoring."); + + dbLeaderboard.get(FieldNames.LEADERBOARD_NAME.name()) + ". Ignoring."); result = null; } else { final ScoringScheme scoringScheme = loadScoringScheme(dbLeaderboard); - + Serializable courseAreaId = (Serializable) dbLeaderboard.get(FieldNames.COURSE_AREA_ID.name()); CourseArea courseArea = null; if (courseAreaId != null) { UUID courseAreaUuid = UUID.fromString(courseAreaId.toString()); courseArea = baseDomainFactory.getExistingCourseAreaById(courseAreaUuid); } - + result = new FlexibleLeaderboardImpl(getRaceLogStore(), getRegattaLogStore(), - (String) dbLeaderboard.get(FieldNames.LEADERBOARD_NAME.name()), - resultDiscardingRule, scoringScheme, courseArea); + (String) dbLeaderboard.get(FieldNames.LEADERBOARD_NAME.name()), resultDiscardingRule, scoringScheme, + courseArea); // For a FlexibleLeaderboard, there should be only the default fleet for any race column for (Object dbRaceColumnAsObject : dbRaceColumns) { BasicDBObject dbRaceColumn = (BasicDBObject) dbRaceColumnAsObject; @@ -611,11 +652,13 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { private void loadLeaderboardCorrections(DBObject dbLeaderboard, DelayedLeaderboardCorrections correctionsToUpdate, SettableScoreCorrection scoreCorrectionToUpdate) { - BasicDBList carriedPointsById = (BasicDBList) dbLeaderboard.get(FieldNames.LEADERBOARD_CARRIED_POINTS_BY_ID.name()); + BasicDBList carriedPointsById = (BasicDBList) dbLeaderboard + .get(FieldNames.LEADERBOARD_CARRIED_POINTS_BY_ID.name()); if (carriedPointsById != null) { for (Object o : carriedPointsById) { DBObject competitorIdAndCarriedPoints = (DBObject) o; - Serializable competitorId = (Serializable) competitorIdAndCarriedPoints.get(FieldNames.COMPETITOR_ID.name()); + Serializable competitorId = (Serializable) competitorIdAndCarriedPoints + .get(FieldNames.COMPETITOR_ID.name()); Double carriedPointsForCompetitor = ((Number) competitorIdAndCarriedPoints .get(FieldNames.LEADERBOARD_CARRIED_POINTS.name())).doubleValue(); if (carriedPointsForCompetitor != null) { @@ -625,20 +668,22 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { } DBObject dbScoreCorrection = (DBObject) dbLeaderboard.get(FieldNames.LEADERBOARD_SCORE_CORRECTIONS.name()); if (dbScoreCorrection.containsField(FieldNames.LEADERBOARD_SCORE_CORRECTION_TIMESTAMP.name())) { - scoreCorrectionToUpdate.setTimePointOfLastCorrectionsValidity( - new MillisecondsTimePoint((Long) dbScoreCorrection.get(FieldNames.LEADERBOARD_SCORE_CORRECTION_TIMESTAMP.name()))); + scoreCorrectionToUpdate.setTimePointOfLastCorrectionsValidity(new MillisecondsTimePoint( + (Long) dbScoreCorrection.get(FieldNames.LEADERBOARD_SCORE_CORRECTION_TIMESTAMP.name()))); dbScoreCorrection.removeField(FieldNames.LEADERBOARD_SCORE_CORRECTION_TIMESTAMP.name()); } if (dbScoreCorrection.containsField(FieldNames.LEADERBOARD_SCORE_CORRECTION_COMMENT.name())) { - scoreCorrectionToUpdate.setComment((String) dbScoreCorrection.get(FieldNames.LEADERBOARD_SCORE_CORRECTION_COMMENT.name())); + scoreCorrectionToUpdate + .setComment((String) dbScoreCorrection.get(FieldNames.LEADERBOARD_SCORE_CORRECTION_COMMENT.name())); dbScoreCorrection.removeField(FieldNames.LEADERBOARD_SCORE_CORRECTION_COMMENT.name()); } for (String escapedRaceColumnName : dbScoreCorrection.keySet()) { // deprecated style: a DBObject per race where the keys are the escaped competitor names // new style: a BasicDBList per race where each entry is a DBObject with COMPETITOR_ID and - // LEADERBOARD_SCORE_CORRECTION_MAX_POINTS_REASON and LEADERBOARD_CORRECTED_SCORE fields each + // LEADERBOARD_SCORE_CORRECTION_MAX_POINTS_REASON and LEADERBOARD_CORRECTED_SCORE fields each DBObject dbScoreCorrectionForRace = (DBObject) dbScoreCorrection.get(escapedRaceColumnName); - final RaceColumn raceColumn = correctionsToUpdate.getLeaderboard().getRaceColumnByName(MongoUtils.unescapeDollarAndDot(escapedRaceColumnName)); + final RaceColumn raceColumn = correctionsToUpdate.getLeaderboard() + .getRaceColumnByName(MongoUtils.unescapeDollarAndDot(escapedRaceColumnName)); if (raceColumn != null) { for (Object o : (BasicDBList) dbScoreCorrectionForRace) { DBObject dbScoreCorrectionForCompetitorInRace = (DBObject) o; @@ -646,12 +691,12 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { .get(FieldNames.COMPETITOR_ID.name()); if (dbScoreCorrectionForCompetitorInRace .containsField(FieldNames.LEADERBOARD_SCORE_CORRECTION_MAX_POINTS_REASON.name())) { - correctionsToUpdate.setMaxPointsReasonByID(competitorId, raceColumn, MaxPointsReason - .valueOf((String) dbScoreCorrectionForCompetitorInRace + correctionsToUpdate.setMaxPointsReasonByID(competitorId, raceColumn, + MaxPointsReason.valueOf((String) dbScoreCorrectionForCompetitorInRace .get(FieldNames.LEADERBOARD_SCORE_CORRECTION_MAX_POINTS_REASON.name()))); } - if (dbScoreCorrectionForCompetitorInRace.containsField(FieldNames.LEADERBOARD_CORRECTED_SCORE - .name())) { + if (dbScoreCorrectionForCompetitorInRace + .containsField(FieldNames.LEADERBOARD_CORRECTED_SCORE.name())) { final Double leaderboardCorrectedScore = ((Number) dbScoreCorrectionForCompetitorInRace .get(FieldNames.LEADERBOARD_CORRECTED_SCORE.name())).doubleValue(); correctionsToUpdate.correctScoreByID(competitorId, raceColumn, @@ -663,22 +708,25 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { + " in leaderboard " + correctionsToUpdate.getLeaderboard().getName()); } } - DBObject competitorDisplayNames = (DBObject) dbLeaderboard.get(FieldNames.LEADERBOARD_COMPETITOR_DISPLAY_NAMES.name()); + DBObject competitorDisplayNames = (DBObject) dbLeaderboard + .get(FieldNames.LEADERBOARD_COMPETITOR_DISPLAY_NAMES.name()); // deprecated style: a DBObject whose keys are the escaped competitor names // new style: a BasicDBList whose entries are DBObjects with COMPETITOR_ID and COMPETITOR_DISPLAY_NAME fields if (competitorDisplayNames != null) { if (competitorDisplayNames instanceof BasicDBList) { for (Object o : (BasicDBList) competitorDisplayNames) { DBObject competitorDisplayName = (DBObject) o; - final Serializable competitorId = (Serializable) competitorDisplayName.get(FieldNames.COMPETITOR_ID.name()); - final String displayName = (String) competitorDisplayName.get(FieldNames.COMPETITOR_DISPLAY_NAME.name()); + final Serializable competitorId = (Serializable) competitorDisplayName + .get(FieldNames.COMPETITOR_ID.name()); + final String displayName = (String) competitorDisplayName + .get(FieldNames.COMPETITOR_DISPLAY_NAME.name()); correctionsToUpdate.setDisplayNameByID(competitorId, displayName); } } else { - logger.severe("Deprecated, now unreadable format of the "+FieldNames.LEADERBOARD_COMPETITOR_DISPLAY_NAMES.name() - +" field for leaderboard "+dbLeaderboard.get(FieldNames.LEADERBOARD_NAME.name())+ - ". You will have to update the competitor display names manually: "+ - competitorDisplayNames); + logger.severe("Deprecated, now unreadable format of the " + + FieldNames.LEADERBOARD_COMPETITOR_DISPLAY_NAMES.name() + " field for leaderboard " + + dbLeaderboard.get(FieldNames.LEADERBOARD_NAME.name()) + + ". You will have to update the competitor display names manually: " + competitorDisplayNames); } } } @@ -712,15 +760,17 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { } @Override - public LeaderboardGroup loadLeaderboardGroup(String name, RegattaRegistry regattaRegistry, LeaderboardRegistry leaderboardRegistry) { + public LeaderboardGroup loadLeaderboardGroup(String name, RegattaRegistry regattaRegistry, + LeaderboardRegistry leaderboardRegistry) { DBCollection leaderboardGroupCollection = database.getCollection(CollectionNames.LEADERBOARD_GROUPS.name()); LeaderboardGroup leaderboardGroup = null; try { BasicDBObject query = new BasicDBObject(); query.put(FieldNames.LEADERBOARD_GROUP_NAME.name(), name); - leaderboardGroup = loadLeaderboardGroup(leaderboardGroupCollection.findOne(query), regattaRegistry, leaderboardRegistry); + leaderboardGroup = loadLeaderboardGroup(leaderboardGroupCollection.findOne(query), regattaRegistry, + leaderboardRegistry); } catch (Exception e) { - logger.log(Level.SEVERE, "Error connecting to MongoDB, unable to load leaderboard group "+name+"."); + logger.log(Level.SEVERE, "Error connecting to MongoDB, unable to load leaderboard group " + name + "."); logger.log(Level.SEVERE, "loadLeaderboardGroup", e); } @@ -728,7 +778,8 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { } @Override - public Iterable getAllLeaderboardGroups(RegattaRegistry regattaRegistry, LeaderboardRegistry leaderboardRegistry) { + public Iterable getAllLeaderboardGroups(RegattaRegistry regattaRegistry, + LeaderboardRegistry leaderboardRegistry) { DBCollection leaderboardGroupCollection = database.getCollection(CollectionNames.LEADERBOARD_GROUPS.name()); Set leaderboardGroups = new HashSet(); try { @@ -737,8 +788,10 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { final LeaderboardGroup leaderboardGroup = loadLeaderboardGroup(o, regattaRegistry, leaderboardRegistry); leaderboardGroups.add(leaderboardGroup); if (!hasUUID) { - // in an effort to migrate leaderboard groups without ID to such that have a UUID as their ID, we need - // to write a leaderboard group to the database again after it just received a UUID for the first time: + // in an effort to migrate leaderboard groups without ID to such that have a UUID as their ID, we + // need + // to write a leaderboard group to the database again after it just received a UUID for the first + // time: logger.info("Existing LeaderboardGroup " + leaderboardGroup.getName() + " received a UUID during migration; updating the leaderboard group in the database"); new MongoObjectFactoryImpl(database).storeLeaderboardGroup(leaderboardGroup); @@ -752,21 +805,22 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { return leaderboardGroups; } - private LeaderboardGroup loadLeaderboardGroup(DBObject o, RegattaRegistry regattaRegistry, LeaderboardRegistry leaderboardRegistry) { + private LeaderboardGroup loadLeaderboardGroup(DBObject o, RegattaRegistry regattaRegistry, + LeaderboardRegistry leaderboardRegistry) { DBCollection leaderboardCollection = database.getCollection(CollectionNames.LEADERBOARDS.name()); String name = (String) o.get(FieldNames.LEADERBOARD_GROUP_NAME.name()); UUID uuid = (UUID) o.get(FieldNames.LEADERBOARD_GROUP_UUID.name()); if (uuid == null) { uuid = UUID.randomUUID(); - logger.info("Leaderboard group "+name+" receives UUID "+uuid+" in a migration effort"); + logger.info("Leaderboard group " + name + " receives UUID " + uuid + " in a migration effort"); // migration: leaderboard groups that don't yet have a UUID receive a random one } String description = (String) o.get(FieldNames.LEADERBOARD_GROUP_DESCRIPTION.name()); String displayName = (String) o.get(FieldNames.LEADERBOARD_GROUP_DISPLAY_NAME.name()); - boolean displayGroupsInReverseOrder = false; // default value + boolean displayGroupsInReverseOrder = false; // default value Object displayGroupsInReverseOrderObj = o.get(FieldNames.LEADERBOARD_GROUP_DISPLAY_IN_REVERSE_ORDER.name()); if (displayGroupsInReverseOrderObj != null) { - displayGroupsInReverseOrder = (Boolean) displayGroupsInReverseOrderObj; + displayGroupsInReverseOrder = (Boolean) displayGroupsInReverseOrderObj; } ArrayList leaderboards = new ArrayList(); BasicDBList dbLeaderboardIds = (BasicDBList) o.get(FieldNames.LEADERBOARD_GROUP_LEADERBOARDS.name()); @@ -780,11 +834,13 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { leaderboards.add(loadedLeaderboard); } } else { - logger.warning("couldn't find leaderboard with ID "+dbLeaderboardId+" referenced by leaderboard group "+name); + logger.warning("couldn't find leaderboard with ID " + dbLeaderboardId + + " referenced by leaderboard group " + name); } } - logger.info("loaded leaderboard group "+name); - LeaderboardGroupImpl result = new LeaderboardGroupImpl(uuid, name, description, displayName, displayGroupsInReverseOrder, leaderboards); + logger.info("loaded leaderboard group " + name); + LeaderboardGroupImpl result = new LeaderboardGroupImpl(uuid, name, description, displayName, + displayGroupsInReverseOrder, leaderboards); Object overallLeaderboardIdOrName = o.get(FieldNames.LEADERBOARD_GROUP_OVERALL_LEADERBOARD.name()); if (overallLeaderboardIdOrName != null) { final DBObject dbOverallLeaderboard; @@ -796,40 +852,50 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { if (dbOverallLeaderboard != null) { // the loadLeaderboard call adds the overall leaderboard to the leaderboard registry and sets it as the // overall leaderboard of the leaderboard group - loadLeaderboard(dbOverallLeaderboard, regattaRegistry, leaderboardRegistry, /* groupForMetaLeaderboard */ result); + loadLeaderboard(dbOverallLeaderboard, regattaRegistry, leaderboardRegistry, + /* groupForMetaLeaderboard */ result); } } return result; } @Override - public Iterable getLeaderboardsNotInGroup(RegattaRegistry regattaRegistry, LeaderboardRegistry leaderboardRegistry) { + public Iterable getLeaderboardsNotInGroup(RegattaRegistry regattaRegistry, + LeaderboardRegistry leaderboardRegistry) { DBCollection leaderboardCollection = database.getCollection(CollectionNames.LEADERBOARDS.name()); Set result = new HashSet(); try { // For MongoDB 2.4 $where with refs to global objects no longer works // http://docs.mongodb.org/manual/reference/operator/where/#op._S_where // Also a single where leads to a table walk without using indexes. So avoid $where. - + // Don't change the query object, unless you know what you're doing. // It queries all leaderboards not referenced to be part of a leaderboard group // and in particular not being an overall leaderboard of a leaderboard group. DBCursor allLeaderboards = leaderboardCollection.find(); for (DBObject leaderboardFromDB : allLeaderboards) { DBObject inLeaderboardGroupsQuery = new BasicDBObject(); - inLeaderboardGroupsQuery.put(FieldNames.LEADERBOARD_GROUP_LEADERBOARDS.name(), ((ObjectId)leaderboardFromDB.get("_id")).toString()); - boolean inLeaderboardGroups = database.getCollection(CollectionNames.LEADERBOARD_GROUPS.name()).find(inLeaderboardGroupsQuery).size()>0; + inLeaderboardGroupsQuery.put(FieldNames.LEADERBOARD_GROUP_LEADERBOARDS.name(), + ((ObjectId) leaderboardFromDB.get("_id")).toString()); + boolean inLeaderboardGroups = database.getCollection(CollectionNames.LEADERBOARD_GROUPS.name()) + .find(inLeaderboardGroupsQuery).size() > 0; DBObject inLeaderboardGroupOverallQuery = new BasicDBObject(); - inLeaderboardGroupOverallQuery.put(FieldNames.LEADERBOARD_GROUP_OVERALL_LEADERBOARD.name(), ((ObjectId)leaderboardFromDB.get("_id")).toString()); - boolean inLeaderboardGroupOverall = database.getCollection(CollectionNames.LEADERBOARD_GROUPS.name()).find(inLeaderboardGroupOverallQuery).size()>0; - + inLeaderboardGroupOverallQuery.put(FieldNames.LEADERBOARD_GROUP_OVERALL_LEADERBOARD.name(), + ((ObjectId) leaderboardFromDB.get("_id")).toString()); + boolean inLeaderboardGroupOverall = database.getCollection(CollectionNames.LEADERBOARD_GROUPS.name()) + .find(inLeaderboardGroupOverallQuery).size() > 0; + DBObject inLeaderboardGroupOverallQueryName = new BasicDBObject(); - inLeaderboardGroupOverallQueryName.put(FieldNames.LEADERBOARD_GROUP_OVERALL_LEADERBOARD.name(), leaderboardFromDB.get(FieldNames.LEADERBOARD_NAME.name())); - boolean inLeaderboardGroupOverallName = database.getCollection(CollectionNames.LEADERBOARD_GROUPS.name()).find(inLeaderboardGroupOverallQueryName).size()>0; - + inLeaderboardGroupOverallQueryName.put(FieldNames.LEADERBOARD_GROUP_OVERALL_LEADERBOARD.name(), + leaderboardFromDB.get(FieldNames.LEADERBOARD_NAME.name())); + boolean inLeaderboardGroupOverallName = database + .getCollection(CollectionNames.LEADERBOARD_GROUPS.name()) + .find(inLeaderboardGroupOverallQueryName).size() > 0; + if (!inLeaderboardGroups && !inLeaderboardGroupOverall && !inLeaderboardGroupOverallName) { - final Leaderboard loadedLeaderboard = loadLeaderboard(leaderboardFromDB, regattaRegistry, leaderboardRegistry, /* groupForMetaLeaderboard */ null); + final Leaderboard loadedLeaderboard = loadLeaderboard(leaderboardFromDB, regattaRegistry, + leaderboardRegistry, /* groupForMetaLeaderboard */ null); if (loadedLeaderboard != null) { result.add(loadedLeaderboard); } @@ -843,16 +909,18 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { } @Override - public WindTrack loadWindTrack(String regattaName, RaceDefinition race, WindSource windSource, long millisecondsOverWhichToAverage) { + public WindTrack loadWindTrack(String regattaName, RaceDefinition race, WindSource windSource, + long millisecondsOverWhichToAverage) { final WindTrack result; - Map resultMap = loadWindTracks(regattaName, race, windSource, millisecondsOverWhichToAverage); + Map resultMap = loadWindTracks(regattaName, race, windSource, + millisecondsOverWhichToAverage); if (resultMap.containsKey(windSource)) { result = resultMap.get(windSource); } else { // create an empty wind track as result if no fixes were found in store for the wind source requested result = new WindTrackImpl(millisecondsOverWhichToAverage, windSource.getType().getBaseConfidence(), - windSource.getType().useSpeed(), - /* nameForReadWriteLock */ WindTrackImpl.class.getSimpleName()+" for source "+windSource.toString()); + windSource.getType().useSpeed(), /* nameForReadWriteLock */ WindTrackImpl.class.getSimpleName() + + " for source " + windSource.toString()); } return result; } @@ -860,7 +928,8 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { @Override public Map loadWindTracks(String regattaName, RaceDefinition race, long millisecondsOverWhichToAverageWind) { - Map result = loadWindTracks(regattaName, race, /* constrain wind source */ null, millisecondsOverWhichToAverageWind); + Map result = loadWindTracks(regattaName, race, /* constrain wind source */ null, + millisecondsOverWhichToAverageWind); return result; } @@ -884,7 +953,8 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { for (DBObject dbWind : windTracks.find(queryById)) { loadWindFix(result, dbWind, millisecondsOverWhichToAverageWind); } - // Additionally check for legacy wind fixes stored with the old EVENT_NAME key; if any are found, migrate them + // Additionally check for legacy wind fixes stored with the old EVENT_NAME key; if any are found, migrate + // them BasicDBObject queryByName = new BasicDBObject(); queryByName.put(FieldNames.EVENT_NAME.name(), regattaName); queryByName.put(FieldNames.RACE_NAME.name(), race.getName()); @@ -900,35 +970,39 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { windFixesToMigrate.add(new MongoObjectFactoryImpl(database).storeWindTrackEntry(race, regattaName, wind.getB(), wind.getA())); } - logger.info("Migrating "+windFixesFoundByName.size()+" wind fixes of regatta "+regattaName+ - " and race "+race.getName()+" to ID-based keys"); + logger.info("Migrating " + windFixesFoundByName.size() + " wind fixes of regatta " + regattaName + + " and race " + race.getName() + " to ID-based keys"); windTracks.insert(windFixesToMigrate.toArray(new DBObject[windFixesToMigrate.size()])); - logger.info("Removing "+windFixesFoundByName.size()+" wind fixes that were keyed by the names of regatta "+regattaName+ - " and race "+race.getName()); + logger.info("Removing " + windFixesFoundByName.size() + + " wind fixes that were keyed by the names of regatta " + regattaName + " and race " + + race.getName()); windTracks.remove(queryByName); } } catch (Exception e) { // something went wrong during DB access; report, then use empty new wind track - logger.log(Level.SEVERE, "Error connecting to MongoDB, unable to load recorded wind data. Check MongoDB settings."); + logger.log(Level.SEVERE, + "Error connecting to MongoDB, unable to load recorded wind data. Check MongoDB settings."); logger.log(Level.SEVERE, "loadWindTrack", e); } return result; } - private Util.Pair loadWindFix(Map result, DBObject dbWind, long millisecondsOverWhichToAverageWind) { + private Util.Pair loadWindFix(Map result, DBObject dbWind, + long millisecondsOverWhichToAverageWind) { Wind wind = loadWind((DBObject) dbWind.get(FieldNames.WIND.name())); WindSourceType windSourceType = WindSourceType.valueOf((String) dbWind.get(FieldNames.WIND_SOURCE_NAME.name())); WindSource windSource; if (dbWind.containsField(FieldNames.WIND_SOURCE_ID.name())) { - windSource = new WindSourceWithAdditionalID(windSourceType, (String) dbWind.get(FieldNames.WIND_SOURCE_ID.name())); + windSource = new WindSourceWithAdditionalID(windSourceType, + (String) dbWind.get(FieldNames.WIND_SOURCE_ID.name())); } else { windSource = new WindSourceImpl(windSourceType); } WindTrack track = result.get(windSource); if (track == null) { track = new WindTrackImpl(millisecondsOverWhichToAverageWind, windSource.getType().getBaseConfidence(), - windSource.getType().useSpeed(), - /* nameForReadWriteLock */ WindTrackImpl.class.getSimpleName()+" for source "+windSource.toString()); + windSource.getType().useSpeed(), /* nameForReadWriteLock */ WindTrackImpl.class.getSimpleName() + + " for source " + windSource.toString()); result.put(windSource, track); } track.add(wind); @@ -944,12 +1018,14 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { UUID eventId = (UUID) dbLink.get(FieldNames.EVENT_ID.name()); Event event = eventResolver.getEvent(eventId); if (event == null) { - logger.info("Found leaderboard group IDs for event with ID "+eventId+" but couldn't find that event."); + logger.info( + "Found leaderboard group IDs for event with ID " + eventId + " but couldn't find that event."); } else { @SuppressWarnings("unchecked") List leaderboardGroupIDs = (List) dbLink.get(FieldNames.LEADERBOARD_GROUP_UUID.name()); for (UUID leaderboardGroupID : leaderboardGroupIDs) { - LeaderboardGroup leaderboardGroup = leaderboardGroupResolver.getLeaderboardGroupByID(leaderboardGroupID); + LeaderboardGroup leaderboardGroup = leaderboardGroupResolver + .getLeaderboardGroupByID(leaderboardGroupID); if (leaderboardGroup != null) { event.addLeaderboardGroup(leaderboardGroup); } @@ -1011,7 +1087,7 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { boolean isStandaloneServer = (Boolean) serverDBObject.get(FieldNames.SERVER_IS_STANDALONE.name()); return new SailingServerConfigurationImpl(isStandaloneServer); } - + private RemoteSailingServerReference loadRemoteSailingSever(DBObject serverDBObject) { RemoteSailingServerReference result = null; String name = (String) serverDBObject.get(FieldNames.SERVER_NAME.name()); @@ -1043,8 +1119,8 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { } /** - * An event doesn't store its regattas; it's the regatta that stores a reference to its event; the regatta - * needs to add itself to the event when loaded or instantiated. + * An event doesn't store its regattas; it's the regatta that stores a reference to its event; the regatta needs to + * add itself to the event when loaded or instantiated. */ private Event loadEvent(DBObject eventDBObject) { String name = (String) eventDBObject.get(FieldNames.EVENT_NAME.name()); @@ -1052,7 +1128,8 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { UUID id = (UUID) eventDBObject.get(FieldNames.EVENT_ID.name()); TimePoint startDate = loadTimePoint(eventDBObject, FieldNames.EVENT_START_DATE); TimePoint endDate = loadTimePoint(eventDBObject, FieldNames.EVENT_END_DATE); - boolean isPublic = eventDBObject.get(FieldNames.EVENT_IS_PUBLIC.name()) != null ? (Boolean) eventDBObject.get(FieldNames.EVENT_IS_PUBLIC.name()) : false; + boolean isPublic = eventDBObject.get(FieldNames.EVENT_IS_PUBLIC.name()) != null + ? (Boolean) eventDBObject.get(FieldNames.EVENT_IS_PUBLIC.name()) : false; Venue venue = loadVenue((DBObject) eventDBObject.get(FieldNames.VENUE.name())); Event result = new EventImpl(name, startDate, endDate, venue, isPublic, id); result.setDescription(description); @@ -1066,7 +1143,8 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { try { result.setOfficialWebsiteURL(new URL(officialWebSiteURLAsString)); } catch (MalformedURLException e) { - logger.severe("Error parsing official website URL "+officialWebSiteURLAsString+" for event "+name+". Ignoring this URL."); + logger.severe("Error parsing official website URL " + officialWebSiteURLAsString + " for event " + name + + ". Ignoring this URL."); } } String baseURLAsString = (String) eventDBObject.get(FieldNames.EVENT_BASE_URL.name()); @@ -1074,7 +1152,8 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { try { result.setBaseURL(new URL(baseURLAsString)); } catch (MalformedURLException e) { - logger.severe("Error parsing base URL "+baseURLAsString+" for event "+name+". Ignoring this URL."); + logger.severe( + "Error parsing base URL " + baseURLAsString + " for event " + name + ". Ignoring this URL."); } } BasicDBList images = (BasicDBList) eventDBObject.get(FieldNames.EVENT_IMAGES.name()); @@ -1095,14 +1174,15 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { } } } - BasicDBList sailorsInfoWebsiteURLs = (BasicDBList) eventDBObject.get(FieldNames.EVENT_SAILORS_INFO_WEBSITES.name()); + BasicDBList sailorsInfoWebsiteURLs = (BasicDBList) eventDBObject + .get(FieldNames.EVENT_SAILORS_INFO_WEBSITES.name()); if (sailorsInfoWebsiteURLs != null) { for (Object sailorsInfoWebsiteObject : sailorsInfoWebsiteURLs) { DBObject sailorsInfoWebsiteDBObject = (DBObject) sailorsInfoWebsiteObject; URL url = loadURL(sailorsInfoWebsiteDBObject, FieldNames.SAILORS_INFO_URL); String localeRaw = (String) sailorsInfoWebsiteDBObject.get(FieldNames.SAILORS_INFO_LOCALE.name()); if (url != null) { - Locale locale = localeRaw != null ? Locale.forLanguageTag(localeRaw) : null; + Locale locale = localeRaw != null ? Locale.forLanguageTag(localeRaw) : null; result.setSailorsInfoWebsiteURL(locale, url); } } @@ -1160,7 +1240,8 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { } BoatClass boatClass = null; if (boatClassName != null) { - boolean typicallyStartsUpwind = (Boolean) dbRegatta.get(FieldNames.BOAT_CLASS_TYPICALLY_STARTS_UPWIND.name()); + boolean typicallyStartsUpwind = (Boolean) dbRegatta + .get(FieldNames.BOAT_CLASS_TYPICALLY_STARTS_UPWIND.name()); boatClass = baseDomainFactory.getOrCreateBoatClass(boatClassName, typicallyStartsUpwind); } BasicDBList dbSeries = (BasicDBList) dbRegatta.get(FieldNames.REGATTA_SERIES.name()); @@ -1174,25 +1255,52 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { RegattaConfiguration configuration = null; if (dbRegatta.containsField(FieldNames.REGATTA_REGATTA_CONFIGURATION.name())) { try { - JSONObject json = Helpers.toJSONObjectSafe(new JSONParser().parse(JSON.serialize(dbRegatta.get(FieldNames.REGATTA_REGATTA_CONFIGURATION.name())))); + JSONObject json = Helpers.toJSONObjectSafe(new JSONParser() + .parse(JSON.serialize(dbRegatta.get(FieldNames.REGATTA_REGATTA_CONFIGURATION.name())))); configuration = RegattaConfigurationJsonDeserializer.create().deserialize(json); - } catch (JsonDeserializationException|ParseException e) { + } catch (JsonDeserializationException | ParseException e) { logger.log(Level.WARNING, "Error loading racing procedure configration for regatta.", e); } } - final Double buoyZoneRadiusInHullLengths = (Double) dbRegatta.get(FieldNames.REGATTA_BUOY_ZONE_RADIUS_IN_HULL_LENGTHS.name()); - final Boolean useStartTimeInference = (Boolean) dbRegatta.get(FieldNames.REGATTA_USE_START_TIME_INFERENCE.name()); - final Boolean controlTrackingFromStartAndFinishTimes = (Boolean) dbRegatta.get(FieldNames.REGATTA_CONTROL_TRACKING_FROM_START_AND_FINISH_TIMES.name()); + final Double buoyZoneRadiusInHullLengths = (Double) dbRegatta + .get(FieldNames.REGATTA_BUOY_ZONE_RADIUS_IN_HULL_LENGTHS.name()); + final Boolean useStartTimeInference = (Boolean) dbRegatta + .get(FieldNames.REGATTA_USE_START_TIME_INFERENCE.name()); + final Boolean controlTrackingFromStartAndFinishTimes = (Boolean) dbRegatta + .get(FieldNames.REGATTA_CONTROL_TRACKING_FROM_START_AND_FINISH_TIMES.name()); + Boolean canBoatsOfCompetitorsChangePerRace = (Boolean) dbRegatta + .get(FieldNames.REGATTA_CAN_BOATS_OF_COMPETITORS_CHANGE_PER_RACE.name()); + // for backward compatibility + boolean createMigratableRegatta = false; + if (canBoatsOfCompetitorsChangePerRace == null) { + canBoatsOfCompetitorsChangePerRace = false; + createMigratableRegatta = true; + } final RankingMetricConstructor rankingMetricConstructor = loadRankingMetricConstructor(dbRegatta); - result = new RegattaImpl(getRaceLogStore(), getRegattaLogStore(), name, boatClass, startDate, endDate, series, /* persistent */true, - loadScoringScheme(dbRegatta), id, courseArea, buoyZoneRadiusInHullLengths == null ? Regatta.DEFAULT_BUOY_ZONE_RADIUS_IN_HULL_LENGTHS : buoyZoneRadiusInHullLengths, useStartTimeInference == null ? true - : useStartTimeInference, controlTrackingFromStartAndFinishTimes == null ? false : controlTrackingFromStartAndFinishTimes, - rankingMetricConstructor); + if (createMigratableRegatta) { + result = new MigratableRegattaImpl(getRaceLogStore(), getRegattaLogStore(), name, boatClass, + canBoatsOfCompetitorsChangePerRace, startDate, endDate, series, /* persistent */true, + loadScoringScheme(dbRegatta), id, courseArea, + buoyZoneRadiusInHullLengths == null ? Regatta.DEFAULT_BUOY_ZONE_RADIUS_IN_HULL_LENGTHS + : buoyZoneRadiusInHullLengths, + useStartTimeInference == null ? true : useStartTimeInference, + controlTrackingFromStartAndFinishTimes == null ? false : controlTrackingFromStartAndFinishTimes, + rankingMetricConstructor, new MongoObjectFactoryImpl(database)); + } else { + result = new RegattaImpl(getRaceLogStore(), getRegattaLogStore(), name, boatClass, + canBoatsOfCompetitorsChangePerRace, startDate, endDate, series, /* persistent */true, + loadScoringScheme(dbRegatta), id, courseArea, + buoyZoneRadiusInHullLengths == null ? Regatta.DEFAULT_BUOY_ZONE_RADIUS_IN_HULL_LENGTHS + : buoyZoneRadiusInHullLengths, + useStartTimeInference == null ? true : useStartTimeInference, + controlTrackingFromStartAndFinishTimes == null ? false : controlTrackingFromStartAndFinishTimes, + rankingMetricConstructor); + } result.setRegattaConfiguration(configuration); } return result; } - + private RankingMetricConstructor loadRankingMetricConstructor(DBObject dbRegatta) { DBObject rankingMetricJson = (DBObject) dbRegatta.get(FieldNames.REGATTA_RANKING_METRIC.name()); // default is OneDesignRankingMetric @@ -1200,7 +1308,8 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { if (rankingMetricJson == null) { result = OneDesignRankingMetric::new; } else { - final String rankingMetricTypeName = (String) rankingMetricJson.get(FieldNames.REGATTA_RANKING_METRIC_TYPE.name()); + final String rankingMetricTypeName = (String) rankingMetricJson + .get(FieldNames.REGATTA_RANKING_METRIC_TYPE.name()); result = RankingMetricsFactory.getRankingMetricConstructor(RankingMetrics.valueOf(rankingMetricTypeName)); } return result; @@ -1218,7 +1327,8 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { // can happen that the database contains a scoring scheme that // has not yet been implemented - fall back with a warning scoringSchemeType = ScoringSchemeType.LOW_POINT; - logger.warning("Could not find scoring scheme " + scoringSchemeTypeName + "! Most probably this has not yet been implemented or even been removed."); + logger.warning("Could not find scoring scheme " + scoringSchemeTypeName + + "! Most probably this has not yet been implemented or even been removed."); } } return scoringSchemeType; @@ -1243,17 +1353,22 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { if (isFleetCanRunInParallelObject != null) { isFleetsCanRunInParallel = (Boolean) dbSeries.get(FieldNames.SERIES_IS_FLEETS_CAN_RUN_IN_PARALLEL.name()); } - final Integer maximumNumberOfDiscards = (Integer) dbSeries.get(FieldNames.SERIES_MAXIMUM_NUMBER_OF_DISCARDS.name()); + final Integer maximumNumberOfDiscards = (Integer) dbSeries + .get(FieldNames.SERIES_MAXIMUM_NUMBER_OF_DISCARDS.name()); Boolean startsWithZeroScore = (Boolean) dbSeries.get(FieldNames.SERIES_STARTS_WITH_ZERO_SCORE.name()); - Boolean hasSplitFleetContiguousScoring = (Boolean) dbSeries.get(FieldNames.SERIES_HAS_SPLIT_FLEET_CONTIGUOUS_SCORING.name()); - Boolean firstColumnIsNonDiscardableCarryForward = (Boolean) dbSeries.get(FieldNames.SERIES_STARTS_WITH_NON_DISCARDABLE_CARRY_FORWARD.name()); + Boolean hasSplitFleetContiguousScoring = (Boolean) dbSeries + .get(FieldNames.SERIES_HAS_SPLIT_FLEET_CONTIGUOUS_SCORING.name()); + Boolean firstColumnIsNonDiscardableCarryForward = (Boolean) dbSeries + .get(FieldNames.SERIES_STARTS_WITH_NON_DISCARDABLE_CARRY_FORWARD.name()); final BasicDBList dbFleets = (BasicDBList) dbSeries.get(FieldNames.SERIES_FLEETS.name()); List fleets = loadFleets(dbFleets); BasicDBList dbRaceColumns = (BasicDBList) dbSeries.get(FieldNames.SERIES_RACE_COLUMNS.name()); Iterable raceColumnNames = loadRaceColumnNames(dbRaceColumns); - Series series = new SeriesImpl(name, isMedal, isFleetsCanRunInParallel, fleets, raceColumnNames, trackedRegattaRegistry); + Series series = new SeriesImpl(name, isMedal, isFleetsCanRunInParallel, fleets, raceColumnNames, + trackedRegattaRegistry); if (dbSeries.get(FieldNames.SERIES_DISCARDING_THRESHOLDS.name()) != null) { - ThresholdBasedResultDiscardingRule resultDiscardingRule = loadResultDiscardingRule(dbSeries, FieldNames.SERIES_DISCARDING_THRESHOLDS); + ThresholdBasedResultDiscardingRule resultDiscardingRule = loadResultDiscardingRule(dbSeries, + FieldNames.SERIES_DISCARDING_THRESHOLDS); series.setResultDiscardingRule(resultDiscardingRule); } if (startsWithZeroScore != null) { @@ -1316,7 +1431,7 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { Color color = null; if (colorAsInt != null) { int r = colorAsInt % 256; - int g = (colorAsInt / 256 ) % 256; + int g = (colorAsInt / 256) % 256; int b = (colorAsInt / 256 / 256) % 256; color = new RGBColor(r, g, b); } @@ -1349,7 +1464,8 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { loadRaceLogEvents(result, query); } catch (Throwable t) { // something went wrong during DB access; report, then use empty new race log - logger.log(Level.SEVERE, "Error connecting to MongoDB, unable to load recorded race log data. Check MongoDB settings."); + logger.log(Level.SEVERE, + "Error connecting to MongoDB, unable to load recorded race log data. Check MongoDB settings."); logger.log(Level.SEVERE, "loadRaceLog", t); } return result; @@ -1360,19 +1476,31 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { DBCollection raceLog = database.getCollection(CollectionNames.RACE_LOGS.name()); for (DBObject o : raceLog.find(query)) { try { - RaceLogEvent raceLogEvent = loadRaceLogEvent((DBObject) o.get(FieldNames.RACE_LOG_EVENT.name())); + Pair> raceLogEventAndOptionalUpdateInstructions = loadRaceLogEvent((DBObject) o.get(FieldNames.RACE_LOG_EVENT.name())); + final RaceLogEvent raceLogEvent = raceLogEventAndOptionalUpdateInstructions.getA(); if (raceLogEvent != null) { targetRaceLog.load(raceLogEvent); result.add(raceLogEvent); } + raceLogEventAndOptionalUpdateInstructions.getB().ifPresent(dbObjectForUpdate->{ + final DBObject q = new BasicDBObject("_id", o.get("_id")); + o.put(FieldNames.RACE_LOG_EVENT.name(), dbObjectForUpdate); + raceLog.update(q, o); + }); } catch (IllegalStateException e) { - logger.log(Level.SEVERE, "Couldn't load race log event "+o+": "+e.getMessage(), e); + logger.log(Level.SEVERE, "Couldn't load race log event " + o + ": " + e.getMessage(), e); } } return result; } - - public RaceLogEvent loadRaceLogEvent(DBObject dbObject) { + + /** + * @return the race log event read, and an optional {@link DBObject} that, if present, indicates the need to update + * the event representation in the DB, e.g., because of a migration activity. The caller is responsible for + * updating this object into the RACE_LOGS collection then because only the caller knows the key surrounding + * the event object passed to this method. + */ + public Pair> loadRaceLogEvent(DBObject dbObject) { TimePoint logicalTimePoint = loadTimePoint(dbObject); TimePoint createdAt = loadTimePoint(dbObject, FieldNames.RACE_LOG_EVENT_CREATED_AT); Serializable id = (Serializable) dbObject.get(FieldNames.RACE_LOG_EVENT_ID.name()); @@ -1389,114 +1517,193 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { } String eventClass = (String) dbObject.get(FieldNames.RACE_LOG_EVENT_CLASS.name()); + final RaceLogEvent resultEvent; + Optional dbObjectForUpdate = Optional.empty(); if (eventClass.equals(RaceLogStartTimeEvent.class.getSimpleName())) { - return loadRaceLogStartTimeEvent(createdAt, author, logicalTimePoint, id, passId, competitors, dbObject); + resultEvent = loadRaceLogStartTimeEvent(createdAt, author, logicalTimePoint, id, passId, competitors, dbObject); } else if (eventClass.equals(RaceLogStartOfTrackingEvent.class.getSimpleName())) { - return loadRaceLogStartOfTrackingEvent(createdAt, author, logicalTimePoint, id, passId, competitors, dbObject); + resultEvent = loadRaceLogStartOfTrackingEvent(createdAt, author, logicalTimePoint, id, passId, competitors, + dbObject); } else if (eventClass.equals(RaceLogEndOfTrackingEvent.class.getSimpleName())) { - return loadRaceLogEndOfTrackingEvent(createdAt, author, logicalTimePoint, id, passId, competitors, dbObject); + resultEvent = loadRaceLogEndOfTrackingEvent(createdAt, author, logicalTimePoint, id, passId, competitors, + dbObject); } else if (eventClass.equals(RaceLogDependentStartTimeEvent.class.getSimpleName())) { - return loadRaceLogDependentStartTimeEvent(createdAt, author, logicalTimePoint, id, passId, competitors, dbObject); + resultEvent = loadRaceLogDependentStartTimeEvent(createdAt, author, logicalTimePoint, id, passId, competitors, + dbObject); } else if (eventClass.equals(RaceLogRaceStatusEvent.class.getSimpleName())) { - return loadRaceLogRaceStatusEvent(createdAt, author, logicalTimePoint, id, passId, competitors, dbObject); + resultEvent = loadRaceLogRaceStatusEvent(createdAt, author, logicalTimePoint, id, passId, competitors, dbObject); } else if (eventClass.equals(RaceLogFlagEvent.class.getSimpleName())) { - return loadRaceLogFlagEvent(createdAt, author, logicalTimePoint, id, passId, competitors, dbObject); + resultEvent = loadRaceLogFlagEvent(createdAt, author, logicalTimePoint, id, passId, competitors, dbObject); } else if (eventClass.equals(RaceLogPassChangeEvent.class.getSimpleName())) { - return loadRaceLogPassChangeEvent(createdAt, author, logicalTimePoint, id, passId, competitors); + resultEvent = loadRaceLogPassChangeEvent(createdAt, author, logicalTimePoint, id, passId, competitors); } else if (eventClass.equals(RaceLogCourseDesignChangedEvent.class.getSimpleName())) { - return loadRaceLogCourseDesignChangedEvent(createdAt, author, logicalTimePoint, id, passId, competitors, dbObject); + resultEvent = loadRaceLogCourseDesignChangedEvent(createdAt, author, logicalTimePoint, id, passId, competitors, + dbObject); } else if (eventClass.equals(RaceLogFinishPositioningListChangedEvent.class.getSimpleName())) { - return loadRaceLogFinishPositioningListChangedEvent(createdAt, author, logicalTimePoint, id, passId, competitors, dbObject); + resultEvent = loadRaceLogFinishPositioningListChangedEvent(createdAt, author, logicalTimePoint, id, passId, + competitors, dbObject); } else if (eventClass.equals(RaceLogFinishPositioningConfirmedEvent.class.getSimpleName())) { - return loadRaceLogFinishPositioningConfirmedEvent(createdAt, author, logicalTimePoint, id, passId, competitors, dbObject); + resultEvent = loadRaceLogFinishPositioningConfirmedEvent(createdAt, author, logicalTimePoint, id, passId, + competitors, dbObject); } else if (eventClass.equals(RaceLogPathfinderEvent.class.getSimpleName())) { - return loadRaceLogPathfinderEvent(createdAt, author, logicalTimePoint, id, passId, competitors, dbObject); + resultEvent = loadRaceLogPathfinderEvent(createdAt, author, logicalTimePoint, id, passId, competitors, dbObject); } else if (eventClass.equals(RaceLogGateLineOpeningTimeEvent.class.getSimpleName())) { - return loadRaceLogGateLineOpeningTimeEvent(createdAt, author, logicalTimePoint, id, passId, competitors, dbObject); + resultEvent = loadRaceLogGateLineOpeningTimeEvent(createdAt, author, logicalTimePoint, id, passId, competitors, + dbObject); } else if (eventClass.equals(RaceLogStartProcedureChangedEvent.class.getSimpleName())) { - return loadRaceLogStartProcedureChangedEvent(createdAt, author, logicalTimePoint, id, passId, competitors, dbObject); + resultEvent = loadRaceLogStartProcedureChangedEvent(createdAt, author, logicalTimePoint, id, passId, competitors, + dbObject); } else if (eventClass.equals(RaceLogProtestStartTimeEvent.class.getSimpleName())) { - return loadRaceLogProtestStartTimeEvent(createdAt, author, logicalTimePoint, id, passId, competitors, dbObject); + resultEvent = loadRaceLogProtestStartTimeEvent(createdAt, author, logicalTimePoint, id, passId, competitors, + dbObject); } else if (eventClass.equals(RaceLogWindFixEvent.class.getSimpleName())) { - return loadRaceLogWindFixEvent(createdAt, author, logicalTimePoint, id, passId, competitors, dbObject); + resultEvent = loadRaceLogWindFixEvent(createdAt, author, logicalTimePoint, id, passId, competitors, dbObject); } else if (eventClass.equals(RaceLogDenoteForTrackingEvent.class.getSimpleName())) { - return loadRaceLogDenoteForTrackingEvent(createdAt, author, logicalTimePoint, id, passId, competitors, dbObject); + resultEvent = loadRaceLogDenoteForTrackingEvent(createdAt, author, logicalTimePoint, id, passId, competitors, + dbObject); } else if (eventClass.equals(RaceLogStartTrackingEvent.class.getSimpleName())) { - return loadRaceLogStartTrackingEvent(createdAt, author, logicalTimePoint, id, passId, competitors, dbObject); + resultEvent = loadRaceLogStartTrackingEvent(createdAt, author, logicalTimePoint, id, passId, competitors, + dbObject); } else if (eventClass.equals(RaceLogRevokeEvent.class.getSimpleName())) { - return loadRaceLogRevokeEvent(createdAt, author, logicalTimePoint, id, passId, competitors, dbObject); + resultEvent = loadRaceLogRevokeEvent(createdAt, author, logicalTimePoint, id, passId, competitors, dbObject); } else if (eventClass.equals(RaceLogRegisterCompetitorEvent.class.getSimpleName())) { - return loadRaceLogRegisterCompetitorEvent(createdAt, author, logicalTimePoint, id, passId, competitors, dbObject); + final Pair> resultPair = loadRaceLogRegisterCompetitorEvent(createdAt, + author, logicalTimePoint, id, passId, competitors, dbObject); + resultEvent = resultPair.getA(); + dbObjectForUpdate = resultPair.getB(); } else if (eventClass.equals(RaceLogAdditionalScoringInformationEvent.class.getSimpleName())) { - return loadRaceLogAdditionalScoringInformationEvent(createdAt, author, logicalTimePoint, id, passId, competitors, dbObject); - } else if (eventClass.equals(RaceLogFixedMarkPassingEvent.class.getSimpleName())){ - return loadRaceLogFixedMarkPassingEvent(createdAt, author, logicalTimePoint, id, passId, competitors, dbObject); - } else if (eventClass.equals(RaceLogSuppressedMarkPassingsEvent.class.getSimpleName())){ - return loadRaceLogSuppressedMarkPassingsEvent(createdAt, author, logicalTimePoint, id, passId, competitors, dbObject); - } else if (eventClass.equals(RaceLogUseCompetitorsFromRaceLogEvent.class.getSimpleName())){ - return loadRaceLogUseCompetitorsFromRaceLogEvent(createdAt, author, logicalTimePoint, id, passId, competitors, dbObject); + resultEvent = loadRaceLogAdditionalScoringInformationEvent(createdAt, author, logicalTimePoint, id, passId, + competitors, dbObject); + } else if (eventClass.equals(RaceLogFixedMarkPassingEvent.class.getSimpleName())) { + resultEvent = loadRaceLogFixedMarkPassingEvent(createdAt, author, logicalTimePoint, id, passId, competitors, + dbObject); + } else if (eventClass.equals(RaceLogSuppressedMarkPassingsEvent.class.getSimpleName())) { + resultEvent = loadRaceLogSuppressedMarkPassingsEvent(createdAt, author, logicalTimePoint, id, passId, competitors, + dbObject); + } else if (eventClass.equals(RaceLogUseCompetitorsFromRaceLogEvent.class.getSimpleName())) { + resultEvent = loadRaceLogUseCompetitorsFromRaceLogEvent(createdAt, author, logicalTimePoint, id, passId, + competitors, dbObject); + } else { + throw new IllegalStateException(String.format("Unknown RaceLogEvent type %s", eventClass)); } - - throw new IllegalStateException(String.format("Unknown RaceLogEvent type %s", eventClass)); + return new Pair<>(resultEvent, dbObjectForUpdate); } private RaceLogEvent loadRaceLogUseCompetitorsFromRaceLogEvent(TimePoint createdAt, AbstractLogEventAuthor author, - TimePoint logicalTimePoint, Serializable id, Integer passId, List competitors, DBObject dbObject) { + TimePoint logicalTimePoint, Serializable id, Integer passId, List competitors, + DBObject dbObject) { return new RaceLogUseCompetitorsFromRaceLogEventImpl(createdAt, author, logicalTimePoint, id, passId); } - private RaceLogEvent loadRaceLogWindFixEvent(TimePoint createdAt, AbstractLogEventAuthor author, TimePoint logicalTimePoint, - Serializable id, Integer passId, List competitors, DBObject dbObject) { + private RaceLogEvent loadRaceLogWindFixEvent(TimePoint createdAt, AbstractLogEventAuthor author, + TimePoint logicalTimePoint, Serializable id, Integer passId, List competitors, + DBObject dbObject) { Wind wind = loadWind((DBObject) dbObject.get(FieldNames.WIND.name())); Boolean isMagnetic = (Boolean) dbObject.get(FieldNames.IS_MAGNETIC.name()); - return new RaceLogWindFixEventImpl(createdAt, logicalTimePoint, author, id, passId, wind, isMagnetic == null ? true : isMagnetic); + return new RaceLogWindFixEventImpl(createdAt, logicalTimePoint, author, id, passId, wind, + isMagnetic == null ? true : isMagnetic); } - private RaceLogEvent loadRaceLogDenoteForTrackingEvent(TimePoint createdAt, AbstractLogEventAuthor author, TimePoint logicalTimePoint, - Serializable id, Integer passId, List competitors, DBObject dbObject) { + private RaceLogEvent loadRaceLogDenoteForTrackingEvent(TimePoint createdAt, AbstractLogEventAuthor author, + TimePoint logicalTimePoint, Serializable id, Integer passId, List competitors, + DBObject dbObject) { String raceName = (String) dbObject.get(FieldNames.RACE_NAME.name()); - BoatClass boatClass = baseDomainFactory.getOrCreateBoatClass((String) dbObject.get(FieldNames.BOAT_CLASS_NAME.name())); + BoatClass boatClass = baseDomainFactory + .getOrCreateBoatClass((String) dbObject.get(FieldNames.BOAT_CLASS_NAME.name())); Serializable raceId = (Serializable) dbObject.get(FieldNames.RACE_ID.name()); - return new RaceLogDenoteForTrackingEventImpl(createdAt, logicalTimePoint, author, id, passId, raceName, boatClass, raceId); + return new RaceLogDenoteForTrackingEventImpl(createdAt, logicalTimePoint, author, id, passId, raceName, + boatClass, raceId); } - private RaceLogEvent loadRaceLogStartTrackingEvent(TimePoint createdAt, AbstractLogEventAuthor author, TimePoint logicalTimePoint, - Serializable id, Integer passId, List competitors, DBObject dbObject) { + private RaceLogEvent loadRaceLogStartTrackingEvent(TimePoint createdAt, AbstractLogEventAuthor author, + TimePoint logicalTimePoint, Serializable id, Integer passId, List competitors, + DBObject dbObject) { return new RaceLogStartTrackingEventImpl(createdAt, logicalTimePoint, author, id, passId); } - private RaceLogEvent loadRaceLogRevokeEvent(TimePoint createdAt, AbstractLogEventAuthor author, TimePoint logicalTimePoint, - Serializable id, Integer passId, List competitors, DBObject dbObject) { - Serializable revokedEventId = Helpers.tryUuidConversion( - (Serializable) dbObject.get(FieldNames.RACE_LOG_REVOKED_EVENT_ID.name())); + private RaceLogEvent loadRaceLogRevokeEvent(TimePoint createdAt, AbstractLogEventAuthor author, + TimePoint logicalTimePoint, Serializable id, Integer passId, List competitors, + DBObject dbObject) { + Serializable revokedEventId = Helpers + .tryUuidConversion((Serializable) dbObject.get(FieldNames.RACE_LOG_REVOKED_EVENT_ID.name())); String revokedEventType = (String) dbObject.get(FieldNames.RACE_LOG_REVOKED_EVENT_TYPE.name()); String revokedEventShortInfo = (String) dbObject.get(FieldNames.RACE_LOG_REVOKED_EVENT_SHORT_INFO.name()); String reason = (String) dbObject.get(FieldNames.RACE_LOG_REVOKED_REASON.name()); - return new RaceLogRevokeEventImpl(createdAt, logicalTimePoint, author, id, passId, - revokedEventId, revokedEventType, revokedEventShortInfo, reason); + return new RaceLogRevokeEventImpl(createdAt, logicalTimePoint, author, id, passId, revokedEventId, + revokedEventType, revokedEventShortInfo, reason); } - private RaceLogEvent loadRaceLogRegisterCompetitorEvent(TimePoint createdAt, AbstractLogEventAuthor author, TimePoint logicalTimePoint, - Serializable id, Integer passId, List competitors, DBObject dbObject) { - Serializable competitorId = (Serializable) dbObject.get(FieldNames.RACE_LOG_COMPETITOR_ID.name()); - Competitor comp = baseDomainFactory.getCompetitorStore().getExistingCompetitorById(competitorId); - return new RaceLogRegisterCompetitorEventImpl(createdAt, logicalTimePoint, author, id, passId, comp); + private int secondLeagueBoatCounter = 0; + private Pair> loadRaceLogRegisterCompetitorEvent(TimePoint createdAt, AbstractLogEventAuthor author, + TimePoint logicalTimePoint, Serializable id, Integer passId, List competitors, + DBObject dbObject) { + final RaceLogRegisterCompetitorEvent result; + final Serializable competitorId = (Serializable) dbObject.get(FieldNames.RACE_LOG_COMPETITOR_ID.name()); + final Serializable boatId = (Serializable) dbObject.get(FieldNames.RACE_LOG_BOAT_ID.name()); + // legacy RaceLogRegisterCompetitorEvent's do not have a boatId, it's expected that the + // corresponding competitors have the type CompetitorWithBoat + Competitor competitor = baseDomainFactory.getCompetitorStore().getExistingCompetitorById(competitorId); + final Optional dbObjectForUpdate; + if (competitor == null) { + logger.severe("Competitor with ID "+competitorId+" not found; can't register with boat with ID "+boatId+" for race"); + result = null; // the competitor wasn't found + dbObjectForUpdate = Optional.empty(); + } else if (boatId == null) { + final Boat boat; + if (competitor.hasBoat() && (boat=((CompetitorWithBoat) competitor).getBoat()) != null) { + result = new RaceLogRegisterCompetitorEventImpl(createdAt, logicalTimePoint, author, id, passId, competitor, boat); + } else { + logger.warning("Bug2822: Competitor with ID "+competitorId+" already seems to have been migrated to one without boat."+ + " But the RaceLogRegisterCompetitorEventImpl event loaded does not specify one either. We'll try to find a boat..."); + // Now comes a hack: we assume here that we're probably migrating a regatta from the 2nd German Sailing League of 2017 + // which is the only case we know of where a few events had boat metadata provided by TracTrac, and other events + // were tracked using smartphones. We will simply look for the boats of that season that we know the IDs of + // and cycle through them, six by six, assuming that regatta loading does not happen in parallel + final String auxiliaryBoatId = "b2567e08-26d9-45c1-b5e0-8c410c8db18b#"+(secondLeagueBoatCounter++ % 6 + 1); + result = createRaceLogRegisterCompetitorEventImpl(createdAt, author, logicalTimePoint, id, passId, auxiliaryBoatId, competitor); + } + // now update the event in the DB: + dbObject.put(FieldNames.RACE_LOG_BOAT_ID.name(), result.getBoat().getId()); + dbObjectForUpdate = Optional.of(dbObject); + } else { + result = createRaceLogRegisterCompetitorEventImpl(createdAt, author, logicalTimePoint, id, passId, boatId, competitor); + dbObjectForUpdate = Optional.empty(); + } + return new Pair<>(result, dbObjectForUpdate); } - private RaceLogEvent loadRaceLogAdditionalScoringInformationEvent(TimePoint createdAt, AbstractLogEventAuthor author, TimePoint logicalTimePoint, - Serializable id, Integer passId, List competitors, DBObject dbObject) { - Object additionalScoringInformationTypeInfo = dbObject.get(FieldNames.RACE_LOG_ADDITIONAL_SCORING_INFORMATION_TYPE.name()); + private RaceLogRegisterCompetitorEvent createRaceLogRegisterCompetitorEventImpl(TimePoint createdAt, AbstractLogEventAuthor author, + TimePoint logicalTimePoint, Serializable id, Integer passId, Serializable boatId, Competitor competitor) { + final RaceLogRegisterCompetitorEvent result; + // a boat was explicitly specified; use it + Boat boat = baseDomainFactory.getCompetitorStore().getExistingBoatById(boatId); + if (boat != null) { + result = new RaceLogRegisterCompetitorEventImpl(createdAt, logicalTimePoint, author, id, passId, competitor, boat); + } else { + result = null; + } + return result; + } + + private RaceLogEvent loadRaceLogAdditionalScoringInformationEvent(TimePoint createdAt, + AbstractLogEventAuthor author, TimePoint logicalTimePoint, Serializable id, Integer passId, + List competitors, DBObject dbObject) { + Object additionalScoringInformationTypeInfo = dbObject + .get(FieldNames.RACE_LOG_ADDITIONAL_SCORING_INFORMATION_TYPE.name()); AdditionalScoringInformationType informationType = AdditionalScoringInformationType.UNKNOWN; if (additionalScoringInformationTypeInfo != null) { informationType = AdditionalScoringInformationType.valueOf(additionalScoringInformationTypeInfo.toString()); } else { - logger.warning("Could not find additional scoring information attached to db log for " + dbObject.toString()); + logger.warning( + "Could not find additional scoring information attached to db log for " + dbObject.toString()); } - return new RaceLogAdditionalScoringInformationEventImpl(createdAt, logicalTimePoint, author, id, passId, informationType); + return new RaceLogAdditionalScoringInformationEventImpl(createdAt, logicalTimePoint, author, id, passId, + informationType); } private RaceLogEvent loadRaceLogProtestStartTimeEvent(TimePoint createdAt, AbstractLogEventAuthor author, - TimePoint logicalTimePoint, Serializable id, Integer passId, List competitors, DBObject dbObject) { + TimePoint logicalTimePoint, Serializable id, Integer passId, List competitors, + DBObject dbObject) { TimePoint protestStartTime = loadTimePoint(dbObject, FieldNames.RACE_LOG_PROTEST_START_TIME); TimePoint protestEndTime = loadTimePoint(dbObject, FieldNames.RACE_LOG_PROTEST_END_TIME); if (protestEndTime == null) { @@ -1508,73 +1715,93 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { } private RaceLogEvent loadRaceLogStartProcedureChangedEvent(TimePoint createdAt, AbstractLogEventAuthor author, - TimePoint logicalTimePoint, Serializable id, Integer passId, List competitors, DBObject dbObject) { - RacingProcedureType type = RacingProcedureType.valueOf(dbObject.get(FieldNames.RACE_LOG_START_PROCEDURE_TYPE.name()).toString()); + TimePoint logicalTimePoint, Serializable id, Integer passId, List competitors, + DBObject dbObject) { + RacingProcedureType type = RacingProcedureType + .valueOf(dbObject.get(FieldNames.RACE_LOG_START_PROCEDURE_TYPE.name()).toString()); return new RaceLogStartProcedureChangedEventImpl(createdAt, logicalTimePoint, author, id, passId, type); } - private RaceLogEvent loadRaceLogGateLineOpeningTimeEvent(TimePoint createdAt, AbstractLogEventAuthor author, TimePoint logicalTimePoint, - Serializable id, Integer passId, List competitors, DBObject dbObject) { + private RaceLogEvent loadRaceLogGateLineOpeningTimeEvent(TimePoint createdAt, AbstractLogEventAuthor author, + TimePoint logicalTimePoint, Serializable id, Integer passId, List competitors, + DBObject dbObject) { Number gateLaunchStopTime = (Number) dbObject.get(FieldNames.RACE_LOG_GATE_LINE_OPENING_TIME.name()); Number golfDownTime = 0; if (dbObject.containsField(FieldNames.RACE_LOG_GOLF_DOWN_TIME.name())) { golfDownTime = (Number) dbObject.get(FieldNames.RACE_LOG_GOLF_DOWN_TIME.name()); } - return new RaceLogGateLineOpeningTimeEventImpl(createdAt, logicalTimePoint, author, id, - passId, gateLaunchStopTime == null ? null : gateLaunchStopTime.longValue(), golfDownTime.longValue()); + return new RaceLogGateLineOpeningTimeEventImpl(createdAt, logicalTimePoint, author, id, passId, + gateLaunchStopTime == null ? null : gateLaunchStopTime.longValue(), golfDownTime.longValue()); } - - private RaceLogEvent loadRaceLogPathfinderEvent(TimePoint createdAt, AbstractLogEventAuthor author, TimePoint logicalTimePoint, - Serializable id, Integer passId, List competitors, DBObject dbObject) { + + private RaceLogEvent loadRaceLogPathfinderEvent(TimePoint createdAt, AbstractLogEventAuthor author, + TimePoint logicalTimePoint, Serializable id, Integer passId, List competitors, + DBObject dbObject) { String pathfinderId = dbObject.get(FieldNames.RACE_LOG_PATHFINDER_ID.name()).toString(); return new RaceLogPathfinderEventImpl(createdAt, logicalTimePoint, author, id, passId, pathfinderId); } - + private RaceLogEvent loadRaceLogFinishPositioningConfirmedEvent(TimePoint createdAt, AbstractLogEventAuthor author, - TimePoint logicalTimePoint, Serializable id, Integer passId, List competitors, DBObject dbObject) { - BasicDBList dbPositionedCompetitorList = (BasicDBList) dbObject.get(FieldNames.RACE_LOG_POSITIONED_COMPETITORS.name()); + TimePoint logicalTimePoint, Serializable id, Integer passId, List competitors, + DBObject dbObject) { + BasicDBList dbPositionedCompetitorList = (BasicDBList) dbObject + .get(FieldNames.RACE_LOG_POSITIONED_COMPETITORS.name()); CompetitorResults positionedCompetitors = null; - //When a confirmation event is loaded that does not contain the positioned competitors (this is the case for the ESS events in - //Singapore and Quingdao) then null should be set for the positionedCompetitors, which is evaluated later on. + // When a confirmation event is loaded that does not contain the positioned competitors (this is the case for + // the ESS events in + // Singapore and Quingdao) then null should be set for the positionedCompetitors, which is evaluated later on. if (dbPositionedCompetitorList != null) { positionedCompetitors = loadPositionedCompetitors(dbPositionedCompetitorList); } - - return new RaceLogFinishPositioningConfirmedEventImpl(createdAt, logicalTimePoint, author, id, passId, positionedCompetitors); + + return new RaceLogFinishPositioningConfirmedEventImpl(createdAt, logicalTimePoint, author, id, passId, + positionedCompetitors); } - private RaceLogEvent loadRaceLogFinishPositioningListChangedEvent(TimePoint createdAt, AbstractLogEventAuthor author, - TimePoint logicalTimePoint, Serializable id, Integer passId, List competitors, DBObject dbObject) { - BasicDBList dbPositionedCompetitorList = (BasicDBList) dbObject.get(FieldNames.RACE_LOG_POSITIONED_COMPETITORS.name()); + private RaceLogEvent loadRaceLogFinishPositioningListChangedEvent(TimePoint createdAt, + AbstractLogEventAuthor author, TimePoint logicalTimePoint, Serializable id, Integer passId, + List competitors, DBObject dbObject) { + BasicDBList dbPositionedCompetitorList = (BasicDBList) dbObject + .get(FieldNames.RACE_LOG_POSITIONED_COMPETITORS.name()); CompetitorResults positionedCompetitors = loadPositionedCompetitors(dbPositionedCompetitorList); - - return new RaceLogFinishPositioningListChangedEventImpl(createdAt, logicalTimePoint, author, id, passId, positionedCompetitors); + + return new RaceLogFinishPositioningListChangedEventImpl(createdAt, logicalTimePoint, author, id, passId, + positionedCompetitors); } - private RaceLogEvent loadRaceLogPassChangeEvent(TimePoint createdAt, AbstractLogEventAuthor author, TimePoint logicalTimePoint, - Serializable id, Integer passId, List competitors) { + private RaceLogEvent loadRaceLogPassChangeEvent(TimePoint createdAt, AbstractLogEventAuthor author, + TimePoint logicalTimePoint, Serializable id, Integer passId, List competitors) { return new RaceLogPassChangeEventImpl(createdAt, logicalTimePoint, author, id, passId); } - private RaceLogCourseDesignChangedEvent loadRaceLogCourseDesignChangedEvent(TimePoint createdAt, AbstractLogEventAuthor author, TimePoint logicalTimePoint, Serializable id, Integer passId, List competitors, DBObject dbObject) { + private RaceLogCourseDesignChangedEvent loadRaceLogCourseDesignChangedEvent(TimePoint createdAt, + AbstractLogEventAuthor author, TimePoint logicalTimePoint, Serializable id, Integer passId, + List competitors, DBObject dbObject) { String courseName = (String) dbObject.get(FieldNames.RACE_LOG_COURSE_DESIGN_NAME.name()); - CourseBase courseData = loadCourseData((BasicDBList) dbObject.get(FieldNames.RACE_LOG_COURSE_DESIGN.name()), courseName); + CourseBase courseData = loadCourseData((BasicDBList) dbObject.get(FieldNames.RACE_LOG_COURSE_DESIGN.name()), + courseName); final String courseDesignerModeName = (String) dbObject.get(FieldNames.RACE_LOG_COURSE_DESIGNER_MODE.name()); - final CourseDesignerMode courseDesignerMode = courseDesignerModeName == null ? null : CourseDesignerMode.valueOf(courseDesignerModeName); - return new RaceLogCourseDesignChangedEventImpl(createdAt, logicalTimePoint, author, id, passId, courseData, courseDesignerMode); + final CourseDesignerMode courseDesignerMode = courseDesignerModeName == null ? null + : CourseDesignerMode.valueOf(courseDesignerModeName); + return new RaceLogCourseDesignChangedEventImpl(createdAt, logicalTimePoint, author, id, passId, courseData, + courseDesignerMode); } private RaceLogEvent loadRaceLogFixedMarkPassingEvent(TimePoint createdAt, AbstractLogEventAuthor author, TimePoint logicalTimePoint, Serializable id, Integer passId, List competitors, DBObject dbObject) { TimePoint ofFixedPassing = loadTimePoint(dbObject, FieldNames.TIMEPOINT_OF_FIXED_MARKPASSING); Integer zeroBasedIndexOfWaypoint = (Integer) dbObject.get(FieldNames.INDEX_OF_PASSED_WAYPOINT.name()); - return new RaceLogFixedMarkPassingEventImpl(createdAt, logicalTimePoint, author, id, competitors, passId, ofFixedPassing, zeroBasedIndexOfWaypoint); + return new RaceLogFixedMarkPassingEventImpl(createdAt, logicalTimePoint, author, id, competitors, passId, + ofFixedPassing, zeroBasedIndexOfWaypoint); } - private RaceLogEvent loadRaceLogSuppressedMarkPassingsEvent(TimePoint createdAt, AbstractLogEventAuthor author, TimePoint logicalTimePoint, - Serializable id, Integer passId, List competitors, DBObject dbObject) { - Integer zeroBasedIndexOfFirstSuppressedWaypoint = (Integer) dbObject.get(FieldNames.INDEX_OF_FIRST_SUPPRESSED_WAYPOINT.name()); - return new RaceLogSuppressedMarkPassingsEventImpl(createdAt, logicalTimePoint, author, id, competitors, passId, zeroBasedIndexOfFirstSuppressedWaypoint); + private RaceLogEvent loadRaceLogSuppressedMarkPassingsEvent(TimePoint createdAt, AbstractLogEventAuthor author, + TimePoint logicalTimePoint, Serializable id, Integer passId, List competitors, + DBObject dbObject) { + Integer zeroBasedIndexOfFirstSuppressedWaypoint = (Integer) dbObject + .get(FieldNames.INDEX_OF_FIRST_SUPPRESSED_WAYPOINT.name()); + return new RaceLogSuppressedMarkPassingsEventImpl(createdAt, logicalTimePoint, author, id, competitors, passId, + zeroBasedIndexOfFirstSuppressedWaypoint); } private CompetitorResults loadPositionedCompetitors(BasicDBList dbPositionedCompetitorList) { @@ -1584,19 +1811,26 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { DBObject dbObject = (DBObject) object; final Serializable competitorId = (Serializable) dbObject.get(FieldNames.COMPETITOR_ID.name()); String competitorDisplayName = (String) dbObject.get(FieldNames.COMPETITOR_DISPLAY_NAME.name()); - //The Competitor name is a new field in the list. Therefore the name might be null for existing events. In this case a standard name is set. + // The Competitor name is a new field in the list. Therefore the name might be null for existing events. In + // this case a standard name is set. if (competitorDisplayName == null) { competitorDisplayName = "loaded competitor"; } - //At this point we do not retrieve the competitor object since at any point in time, especially after a server restart, the DomainFactory and its competitor - //cache might be empty. But at this time the race log is loaded from database, so the competitor would be null. - //By not using the Competitor object retrieved from the DomainFactory we get completely independent from server restarts and the timepoint of loading - //competitors by tracking providers. + // At this point we do not retrieve the competitor object since at any point in time, especially after a + // server restart, the DomainFactory and its competitor + // cache might be empty. But at this time the race log is loaded from database, so the competitor would be + // null. + // By not using the Competitor object retrieved from the DomainFactory we get completely independent from + // server restarts and the timepoint of loading + // competitors by tracking providers. final Integer rank = (Integer) dbObject.get(FieldNames.LEADERBOARD_RANK.name()); - final MaxPointsReason maxPointsReason = MaxPointsReason.valueOf((String) dbObject.get(FieldNames.LEADERBOARD_SCORE_CORRECTION_MAX_POINTS_REASON.name())); + final MaxPointsReason maxPointsReason = MaxPointsReason + .valueOf((String) dbObject.get(FieldNames.LEADERBOARD_SCORE_CORRECTION_MAX_POINTS_REASON.name())); final Double score = (Double) dbObject.get(FieldNames.LEADERBOARD_CORRECTED_SCORE.name()); - final Long finishingTimePointAsMillis = (Long) dbObject.get(FieldNames.RACE_LOG_FINISHING_TIME_AS_MILLIS.name()); - final TimePoint finishingTime = finishingTimePointAsMillis == null ? null : new MillisecondsTimePoint(finishingTimePointAsMillis); + final Long finishingTimePointAsMillis = (Long) dbObject + .get(FieldNames.RACE_LOG_FINISHING_TIME_AS_MILLIS.name()); + final TimePoint finishingTime = finishingTimePointAsMillis == null ? null + : new MillisecondsTimePoint(finishingTimePointAsMillis); final String comment = (String) dbObject.get(FieldNames.LEADERBOARD_SCORE_CORRECTION_COMMENT.name()); final String mergeStateAsString = (String) dbObject.get(FieldNames.LEADERBOARD_SCORE_CORRECTION_MERGE_STATE.name()); final MergeState mergeState; @@ -1623,7 +1857,9 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { return competitors; } - private RaceLogFlagEvent loadRaceLogFlagEvent(TimePoint createdAt, AbstractLogEventAuthor author, TimePoint logicalTimePoint, Serializable id, Integer passId, List competitors, DBObject dbObject) { + private RaceLogFlagEvent loadRaceLogFlagEvent(TimePoint createdAt, AbstractLogEventAuthor author, + TimePoint logicalTimePoint, Serializable id, Integer passId, List competitors, + DBObject dbObject) { Flags upperFlag = Flags.valueOf((String) dbObject.get(FieldNames.RACE_LOG_EVENT_FLAG_UPPER.name())); Flags lowerFlag = Flags.valueOf((String) dbObject.get(FieldNames.RACE_LOG_EVENT_FLAG_LOWER.name())); Boolean displayed = Boolean.valueOf((String) dbObject.get(FieldNames.RACE_LOG_EVENT_FLAG_DISPLAYED.name())); @@ -1632,41 +1868,56 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { return null; } - return new RaceLogFlagEventImpl(createdAt, logicalTimePoint, author, id, passId, upperFlag, lowerFlag, displayed); + return new RaceLogFlagEventImpl(createdAt, logicalTimePoint, author, id, passId, upperFlag, lowerFlag, + displayed); } - private RaceLogStartTimeEvent loadRaceLogStartTimeEvent(TimePoint createdAt, AbstractLogEventAuthor author, TimePoint logicalTimePoint, Serializable id, Integer passId, List competitors, DBObject dbObject) { + private RaceLogStartTimeEvent loadRaceLogStartTimeEvent(TimePoint createdAt, AbstractLogEventAuthor author, + TimePoint logicalTimePoint, Serializable id, Integer passId, List competitors, + DBObject dbObject) { TimePoint startTime = loadTimePoint(dbObject, FieldNames.RACE_LOG_EVENT_START_TIME); - RaceLogRaceStatus nextStatus = RaceLogRaceStatus.valueOf((String) dbObject.get(FieldNames.RACE_LOG_EVENT_NEXT_STATUS.name())); + RaceLogRaceStatus nextStatus = RaceLogRaceStatus + .valueOf((String) dbObject.get(FieldNames.RACE_LOG_EVENT_NEXT_STATUS.name())); return new RaceLogStartTimeEventImpl(createdAt, logicalTimePoint, author, id, passId, startTime, nextStatus); } - private RaceLogStartOfTrackingEvent loadRaceLogStartOfTrackingEvent(TimePoint createdAt, AbstractLogEventAuthor author, TimePoint logicalTimePoint, Serializable id, Integer passId, List competitors, DBObject dbObject) { + private RaceLogStartOfTrackingEvent loadRaceLogStartOfTrackingEvent(TimePoint createdAt, + AbstractLogEventAuthor author, TimePoint logicalTimePoint, Serializable id, Integer passId, + List competitors, DBObject dbObject) { return new RaceLogStartOfTrackingEventImpl(createdAt, logicalTimePoint, author, id, passId); } - private RaceLogEndOfTrackingEvent loadRaceLogEndOfTrackingEvent(TimePoint createdAt, AbstractLogEventAuthor author, TimePoint logicalTimePoint, Serializable id, Integer passId, List competitors, DBObject dbObject) { + private RaceLogEndOfTrackingEvent loadRaceLogEndOfTrackingEvent(TimePoint createdAt, AbstractLogEventAuthor author, + TimePoint logicalTimePoint, Serializable id, Integer passId, List competitors, + DBObject dbObject) { return new RaceLogEndOfTrackingEventImpl(createdAt, logicalTimePoint, author, id, passId); } - private RaceLogDependentStartTimeEvent loadRaceLogDependentStartTimeEvent(TimePoint createdAt, AbstractLogEventAuthor author, TimePoint logicalTimePoint, Serializable id, Integer passId, List competitors, DBObject dbObject) { + private RaceLogDependentStartTimeEvent loadRaceLogDependentStartTimeEvent(TimePoint createdAt, + AbstractLogEventAuthor author, TimePoint logicalTimePoint, Serializable id, Integer passId, + List competitors, DBObject dbObject) { final Object regattaLikeNameObject = dbObject.get(FieldNames.RACE_LOG_DEPDENDENT_ON_REGATTALIKE.name()); final String regattaLikeName = regattaLikeNameObject == null ? null : regattaLikeNameObject.toString(); final Object raceColumnNameObject = dbObject.get(FieldNames.RACE_LOG_DEPDENDENT_ON_RACECOLUMN.name()); final String raceColumnName = raceColumnNameObject == null ? null : raceColumnNameObject.toString(); final Object fleetNameObject = dbObject.get(FieldNames.RACE_LOG_DEPDENDENT_ON_FLEET.name()); final String fleetName = fleetNameObject == null ? null : fleetNameObject.toString(); - final SimpleRaceLogIdentifier dependentRaceLog = new SimpleRaceLogIdentifierImpl(regattaLikeName, raceColumnName, fleetName); + final SimpleRaceLogIdentifier dependentRaceLog = new SimpleRaceLogIdentifierImpl(regattaLikeName, + raceColumnName, fleetName); final Object startTimeDifferenceObject = dbObject.get(FieldNames.RACE_LOG_START_TIME_DIFFERENCE_IN_MS.name()); - final Duration startTimeDifference = startTimeDifferenceObject == null ? null : - new MillisecondsDurationImpl(((Number) startTimeDifferenceObject).longValue()); - RaceLogRaceStatus nextStatus = RaceLogRaceStatus.valueOf((String) dbObject.get(FieldNames.RACE_LOG_EVENT_NEXT_STATUS.name())); - return new RaceLogDependentStartTimeEventImpl(createdAt, logicalTimePoint, author, id, - passId, dependentRaceLog, startTimeDifference, nextStatus); + final Duration startTimeDifference = startTimeDifferenceObject == null ? null + : new MillisecondsDurationImpl(((Number) startTimeDifferenceObject).longValue()); + RaceLogRaceStatus nextStatus = RaceLogRaceStatus + .valueOf((String) dbObject.get(FieldNames.RACE_LOG_EVENT_NEXT_STATUS.name())); + return new RaceLogDependentStartTimeEventImpl(createdAt, logicalTimePoint, author, id, passId, dependentRaceLog, + startTimeDifference, nextStatus); } - private RaceLogRaceStatusEvent loadRaceLogRaceStatusEvent(TimePoint createdAt, AbstractLogEventAuthor author, TimePoint logicalTimePoint, Serializable id, Integer passId, List competitors, DBObject dbObject) { - RaceLogRaceStatus nextStatus = RaceLogRaceStatus.valueOf((String) dbObject.get(FieldNames.RACE_LOG_EVENT_NEXT_STATUS.name())); + private RaceLogRaceStatusEvent loadRaceLogRaceStatusEvent(TimePoint createdAt, AbstractLogEventAuthor author, + TimePoint logicalTimePoint, Serializable id, Integer passId, List competitors, + DBObject dbObject) { + RaceLogRaceStatus nextStatus = RaceLogRaceStatus + .valueOf((String) dbObject.get(FieldNames.RACE_LOG_EVENT_NEXT_STATUS.name())); return new RaceLogRaceStatusEventImpl(createdAt, logicalTimePoint, author, id, passId, nextStatus); } @@ -1680,12 +1931,14 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { loadRegattaLogEvents(result, query, identifier); } catch (Throwable t) { // something went wrong during DB access; report, then use empty new regatta log - logger.log(Level.SEVERE, "Error connecting to MongoDB, unable to load recorded regatta log data for "+identifier+". Check MongoDB settings.", t); + logger.log(Level.SEVERE, "Error connecting to MongoDB, unable to load recorded regatta log data for " + + identifier + ". Check MongoDB settings.", t); } return result; } - private void loadRegattaLogEvents(RegattaLog targetRegattaLog, BasicDBObject query, RegattaLikeIdentifier regattaLogIdentifier) { + private void loadRegattaLogEvents(RegattaLog targetRegattaLog, BasicDBObject query, + RegattaLikeIdentifier regattaLogIdentifier) { DBCollection collection = database.getCollection(CollectionNames.REGATTA_LOGS.name()); for (DBObject o : collection.find(query)) { try { @@ -1694,7 +1947,7 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { targetRegattaLog.load(event); } } catch (IllegalStateException e) { - logger.log(Level.SEVERE, "Couldn't load regatta log event "+o+": "+e.getMessage(), e); + logger.log(Level.SEVERE, "Couldn't load regatta log event " + o + ": " + e.getMessage(), e); } } } @@ -1708,10 +1961,13 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { String authorName = (String) dbObject.get(FieldNames.REGATTA_LOG_EVENT_AUTHOR_NAME.name()); Number authorPriority = (Number) dbObject.get(FieldNames.REGATTA_LOG_EVENT_AUTHOR_PRIORITY.name()); author = new LogEventAuthorImpl(authorName, authorPriority.intValue()); - //CloseOpenEnded, DeviceCompMapping, DeviceMarkMapping, RegisterComp, Revoke + // CloseOpenEnded, DeviceCompMapping, DeviceMarkMapping, RegisterComp, Revoke String eventClass = (String) dbObject.get(FieldNames.REGATTA_LOG_EVENT_CLASS.name()); if (eventClass.equals(RegattaLogDeviceCompetitorMappingEvent.class.getSimpleName())) { - return loadRegattaLogDeviceCompetitorMappingEvent(createdAt, author, logicalTimePoint, id, dbObject, regattaLogIdentifier, o); + return loadRegattaLogDeviceCompetitorMappingEvent(createdAt, author, logicalTimePoint, id, dbObject, + regattaLogIdentifier, o); + } else if (eventClass.equals(RegattaLogDeviceBoatMappingEvent.class.getSimpleName())) { + return loadRegattaLogDeviceBoatMappingEvent(createdAt, author, logicalTimePoint, id, dbObject, regattaLogIdentifier, o); } else if (eventClass.equals(RegattaLogDeviceCompetitorBravoMappingEventImpl.class.getSimpleName())) { return loadRegattaLogDeviceCompetitorBravoMappingEvent(createdAt, author, logicalTimePoint, id, dbObject, regattaLogIdentifier, o); @@ -1721,16 +1977,30 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { } else if (eventClass.equals(RegattaLogDeviceCompetitorExpeditionExtendedMappingEventImpl.class.getSimpleName())) { return loadRegattaLogDeviceCompetitorExpeditionExtendedMappingEvent(createdAt, author, logicalTimePoint, id, dbObject, regattaLogIdentifier, o); + } else if (eventClass.equals(RegattaLogDeviceBoatBravoMappingEventImpl.class.getSimpleName())) { + return loadRegattaLogDeviceBoatBravoMappingEvent(createdAt, author, logicalTimePoint, id, dbObject, + regattaLogIdentifier, o); + } else if (eventClass.equals(RegattaLogDeviceBoatBravoExtendedMappingEventImpl.class.getSimpleName())) { + return loadRegattaLogDeviceBoatBravoExtendedMappingEvent(createdAt, author, logicalTimePoint, id, dbObject, + regattaLogIdentifier, o); + } else if (eventClass.equals(RegattaLogDeviceBoatExpeditionExtendedMappingEventImpl.class.getSimpleName())) { + return loadRegattaLogDeviceBoatExpeditionExtendedMappingEvent(createdAt, author, logicalTimePoint, id, dbObject, + regattaLogIdentifier, o); } else if (eventClass.equals(RegattaLogDeviceMarkMappingEvent.class.getSimpleName())) { - return loadRegattaLogDeviceMarkMappingEvent(createdAt, author, logicalTimePoint, id, dbObject, regattaLogIdentifier, o); + return loadRegattaLogDeviceMarkMappingEvent(createdAt, author, logicalTimePoint, id, dbObject, + regattaLogIdentifier, o); } else if (eventClass.equals(RegattaLogCloseOpenEndedDeviceMappingEvent.class.getSimpleName())) { return loadRegattaLogCloseOpenEndedDeviceMappingEvent(createdAt, author, logicalTimePoint, id, dbObject); + } else if (eventClass.equals(RegattaLogRegisterBoatEvent.class.getSimpleName())) { + return loadRegattaLogRegisterBoatEvent(createdAt, author, logicalTimePoint, id, dbObject); } else if (eventClass.equals(RegattaLogRegisterCompetitorEvent.class.getSimpleName())) { return loadRegattaLogRegisterCompetitorEvent(createdAt, author, logicalTimePoint, id, dbObject); } else if (eventClass.equals(RegattaLogSetCompetitorTimeOnTimeFactorEvent.class.getSimpleName())) { return loadRegattaLogSetCompetitorTimeOnTimeFactorEvent(createdAt, author, logicalTimePoint, id, dbObject); - } else if (eventClass.equals(RegattaLogSetCompetitorTimeOnDistanceAllowancePerNauticalMileEvent.class.getSimpleName())) { - return loadRegattaLogSetCompetitorTimeOnDistanceAllowancePerNauticalMileEvent(createdAt, author, logicalTimePoint, id, dbObject); + } else if (eventClass + .equals(RegattaLogSetCompetitorTimeOnDistanceAllowancePerNauticalMileEvent.class.getSimpleName())) { + return loadRegattaLogSetCompetitorTimeOnDistanceAllowancePerNauticalMileEvent(createdAt, author, + logicalTimePoint, id, dbObject); } else if (eventClass.equals(RegattaLogDefineMarkEvent.class.getSimpleName())) { return loadRegattaLogDefineMarkEvent(createdAt, author, logicalTimePoint, id, dbObject); } else if (eventClass.equals(RegattaLogRevokeEvent.class.getSimpleName())) { @@ -1745,57 +2015,83 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { return comp; } + private Boat getBoatByID(DBObject dbObject) { + Serializable boatId = (Serializable) dbObject.get(FieldNames.REGATTA_LOG_BOAT_ID.name()); + Boat boat = baseDomainFactory.getCompetitorStore().getExistingBoatById(boatId); + return boat; + } + private RegattaLogEvent loadRegattaLogSetCompetitorTimeOnDistanceAllowancePerNauticalMileEvent(TimePoint createdAt, AbstractLogEventAuthor author, TimePoint logicalTimePoint, Serializable id, DBObject dbObject) { final Competitor comp = getCompetitorByID(dbObject); final Double timeOnTimeFactor = (Double) dbObject.get(FieldNames.REGATTA_LOG_TIME_ON_TIME_FACTOR.name()); - return new RegattaLogSetCompetitorTimeOnTimeFactorEventImpl(createdAt, logicalTimePoint, author, id, comp, timeOnTimeFactor); + return new RegattaLogSetCompetitorTimeOnTimeFactorEventImpl(createdAt, logicalTimePoint, author, id, comp, + timeOnTimeFactor); } private RegattaLogEvent loadRegattaLogSetCompetitorTimeOnTimeFactorEvent(TimePoint createdAt, AbstractLogEventAuthor author, TimePoint logicalTimePoint, Serializable id, DBObject dbObject) { final Competitor comp = getCompetitorByID(dbObject); - final Double timeOnDistanceSecondsAllowancePerNauticalMile = (Double) dbObject.get(FieldNames.REGATTA_LOG_TIME_ON_DISTANCE_SECONDS_ALLOWANCE_PER_NAUTICAL_MILE.name()); - final Duration timeOnDistanceAllowancePerNauticalMile = timeOnDistanceSecondsAllowancePerNauticalMile == null ? null : - new MillisecondsDurationImpl((long) (timeOnDistanceSecondsAllowancePerNauticalMile*1000)); - return new RegattaLogSetCompetitorTimeOnDistanceAllowancePerNauticalMileEventImpl(createdAt, logicalTimePoint, author, id, comp, timeOnDistanceAllowancePerNauticalMile); + final Double timeOnDistanceSecondsAllowancePerNauticalMile = (Double) dbObject + .get(FieldNames.REGATTA_LOG_TIME_ON_DISTANCE_SECONDS_ALLOWANCE_PER_NAUTICAL_MILE.name()); + final Duration timeOnDistanceAllowancePerNauticalMile = timeOnDistanceSecondsAllowancePerNauticalMile == null + ? null : new MillisecondsDurationImpl((long) (timeOnDistanceSecondsAllowancePerNauticalMile * 1000)); + return new RegattaLogSetCompetitorTimeOnDistanceAllowancePerNauticalMileEventImpl(createdAt, logicalTimePoint, + author, id, comp, timeOnDistanceAllowancePerNauticalMile); } private RegattaLogRevokeEvent loadRegattaLogRevokeEvent(TimePoint createdAt, AbstractLogEventAuthor author, TimePoint logicalTimePoint, Serializable id, DBObject dbObject) { - Serializable revokedEventId = Helpers.tryUuidConversion( - (Serializable) dbObject.get(FieldNames.REGATTA_LOG_REVOKED_EVENT_ID.name())); + Serializable revokedEventId = Helpers + .tryUuidConversion((Serializable) dbObject.get(FieldNames.REGATTA_LOG_REVOKED_EVENT_ID.name())); String revokedEventType = (String) dbObject.get(FieldNames.REGATTA_LOG_REVOKED_EVENT_TYPE.name()); String revokedEventShortInfo = (String) dbObject.get(FieldNames.REGATTA_LOG_REVOKED_EVENT_SHORT_INFO.name()); String reason = (String) dbObject.get(FieldNames.REGATTA_LOG_REVOKED_REASON.name()); - return new RegattaLogRevokeEventImpl(createdAt, logicalTimePoint, author, id, - revokedEventId, revokedEventType, revokedEventShortInfo, reason); - } - - private RegattaLogEvent loadRegattaLogDefineMarkEvent(TimePoint createdAt, AbstractLogEventAuthor author, - TimePoint logicalTimePoint, Serializable id, DBObject dbObject) { - Mark mark = loadMark((DBObject) dbObject.get(FieldNames.REGATTA_LOG_MARK.name())); - return new RegattaLogDefineMarkEventImpl(createdAt, author, logicalTimePoint, id, mark); + return new RegattaLogRevokeEventImpl(createdAt, logicalTimePoint, author, id, revokedEventId, revokedEventType, + revokedEventShortInfo, reason); } - private RegattaLogRegisterCompetitorEvent loadRegattaLogRegisterCompetitorEvent(TimePoint createdAt, AbstractLogEventAuthor author, + private RegattaLogEvent loadRegattaLogDefineMarkEvent(TimePoint createdAt, AbstractLogEventAuthor author, TimePoint logicalTimePoint, Serializable id, DBObject dbObject) { + Mark mark = loadMark((DBObject) dbObject.get(FieldNames.REGATTA_LOG_MARK.name())); + return new RegattaLogDefineMarkEventImpl(createdAt, author, logicalTimePoint, id, mark); + } + + private RegattaLogRegisterCompetitorEvent loadRegattaLogRegisterCompetitorEvent(TimePoint createdAt, + AbstractLogEventAuthor author, TimePoint logicalTimePoint, Serializable id, DBObject dbObject) { Competitor comp = getCompetitorByID(dbObject); final RegattaLogRegisterCompetitorEvent result; if (comp == null) { result = null; - logger.log(Level.SEVERE, "Couldn't resolve competitor with ID "+dbObject.get(FieldNames.REGATTA_LOG_COMPETITOR_ID.name())+ - " from registration event with ID "+id+". Skipping this competitor registration."); + logger.log(Level.SEVERE, + "Couldn't resolve competitor with ID " + dbObject.get(FieldNames.REGATTA_LOG_COMPETITOR_ID.name()) + + " from registration event with ID " + id + ". Skipping this competitor registration."); } else { result = new RegattaLogRegisterCompetitorEventImpl(createdAt, logicalTimePoint, author, id, comp); } return result; } - private RegattaLogCloseOpenEndedDeviceMappingEvent loadRegattaLogCloseOpenEndedDeviceMappingEvent(TimePoint createdAt, + private RegattaLogRegisterBoatEvent loadRegattaLogRegisterBoatEvent(TimePoint createdAt, AbstractLogEventAuthor author, TimePoint logicalTimePoint, Serializable id, DBObject dbObject) { - Serializable deviceMappingEventId = Helpers.tryUuidConversion((Serializable) dbObject.get( - FieldNames.REGATTA_LOG_DEVICE_MAPPING_EVENT_ID.name())); + Boat boat = getBoatByID(dbObject); + final RegattaLogRegisterBoatEvent result; + if (boat == null) { + result = null; + logger.log(Level.SEVERE, + "Couldn't resolve boat with ID " + dbObject.get(FieldNames.REGATTA_LOG_BOAT_ID.name()) + + " from registration event with ID " + id + ". Skipping this boat registration."); + } else { + result = new RegattaLogRegisterBoatEventImpl(createdAt, logicalTimePoint, author, id, boat); + } + return result; + } + + private RegattaLogCloseOpenEndedDeviceMappingEvent loadRegattaLogCloseOpenEndedDeviceMappingEvent( + TimePoint createdAt, AbstractLogEventAuthor author, TimePoint logicalTimePoint, Serializable id, + DBObject dbObject) { + Serializable deviceMappingEventId = Helpers + .tryUuidConversion((Serializable) dbObject.get(FieldNames.REGATTA_LOG_DEVICE_MAPPING_EVENT_ID.name())); TimePoint closingTimePointInclusive = loadTimePoint(dbObject, FieldNames.REGATTA_LOG_CLOSING_TIMEPOINT); return new RegattaLogCloseOpenEndedDeviceMappingEventImpl(createdAt, author, logicalTimePoint, id, deviceMappingEventId, closingTimePointInclusive); @@ -1803,50 +2099,29 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { private RegattaLogDeviceMarkMappingEvent loadRegattaLogDeviceMarkMappingEvent(TimePoint createdAt, AbstractLogEventAuthor author, TimePoint logicalTimePoint, Serializable id, DBObject dbObject, RegattaLikeIdentifier regattaLogIdentifier, DBObject outerDBObject) { - DeviceIdentifier device = null; - try { - device = loadDeviceId(deviceIdentifierServiceFinder, - (DBObject) dbObject.get(FieldNames.DEVICE_ID.name())); - } catch (Exception e) { - logger.log(Level.WARNING, "Could not load deviceId for RaceLogEvent", e); - e.printStackTrace(); - } - //have to load complete mark, as no order is guaranteed for loading of racelog events - Mark mappedTo = loadMark((DBObject) dbObject.get(FieldNames.MARK.name())); - @SuppressWarnings("deprecation") // used only for auto-migration; may be removed in future releases - final FieldNames deprecatedFromFieldName = FieldNames.RACE_LOG_FROM; - @SuppressWarnings("deprecation") // used only for auto-migration; may be removed in future releases - final FieldNames deprecatedToFieldName = FieldNames.RACE_LOG_TO; - Triple times = loadFromToTimePoint(dbObject, FieldNames.REGATTA_LOG_FROM, deprecatedFromFieldName, FieldNames.REGATTA_LOG_TO, deprecatedToFieldName); - final TimePoint from = times.getA(); - final TimePoint to = times.getB(); - final RegattaLogDeviceMarkMappingEventImpl result = new RegattaLogDeviceMarkMappingEventImpl(createdAt, logicalTimePoint, author, id, mappedTo, device, from, to); - final boolean needsMigration = times.getC(); - if (needsMigration) { - // remove old version of mapping event - WriteResult removeResult = database.getCollection(CollectionNames.REGATTA_LOGS.name()).remove(outerDBObject); - assert removeResult.getN() == 1; - // and then insert using the fixed storage implementation - new MongoObjectFactoryImpl(database, serviceFinderFactory).storeRegattaLogEvent(regattaLogIdentifier, result); - } - return result; + return this.loadRegattaLogDeviceMappingEvent(createdAt, author, logicalTimePoint, id, dbObject, + regattaLogIdentifier, outerDBObject, () -> loadMark((DBObject) dbObject.get(FieldNames.MARK.name())), + RegattaLogDeviceMarkMappingEventImpl::new, + result -> new MongoObjectFactoryImpl(database, serviceFinderFactory) + .storeRegattaLogEvent(regattaLogIdentifier, result)); } /** * Loads a from and a to time point from fromField and toField of dbObject. - * If the fromField is not found, the fromFieldDeprecated is attempted. If found, migration - * is deemed necessary, expressed by returning true in the {@link Triple#getC()} component of the result. - * Same for the to-field. + * If the fromField is not found, the fromFieldDeprecated is attempted. If found, + * migration is deemed necessary, expressed by returning true in the {@link Triple#getC()} component of + * the result. Same for the to-field. * - * @return the from-time in {@link Triple#getA()}, the to-time in {@link Triple#getB()} and whether or not migration is - * necessary because a value was only found in a deprecated field in {@link Triple#getC()}. + * @return the from-time in {@link Triple#getA()}, the to-time in {@link Triple#getB()} and whether or not migration + * is necessary because a value was only found in a deprecated field in {@link Triple#getC()}. */ - private Triple loadFromToTimePoint(final DBObject dbObject, FieldNames fromField, FieldNames fromFieldDeprecated, - FieldNames toField, FieldNames toFieldDeprecated) { + private Triple loadFromToTimePoint(final DBObject dbObject, FieldNames fromField, + FieldNames fromFieldDeprecated, FieldNames toField, FieldNames toFieldDeprecated) { boolean needsMigration = false; TimePoint from = loadTimePoint(dbObject, fromField); if (from == null) { - // see bug 2733: erroneously, some records before the fix were written using RACE_LOG_FROM instead of REGATTA_LOG_FROM + // see bug 2733: erroneously, some records before the fix were written using RACE_LOG_FROM instead of + // REGATTA_LOG_FROM // If such a case is found here, migrate the record. from = loadTimePoint(dbObject, fromFieldDeprecated); if (from != null) { @@ -1855,7 +2130,8 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { } TimePoint to = loadTimePoint(dbObject, toField); if (to == null) { - // see bug 2733: erroneously, some records before the fix were written using RACE_LOG_FROM instead of REGATTA_LOG_FROM + // see bug 2733: erroneously, some records before the fix were written using RACE_LOG_FROM instead of + // REGATTA_LOG_FROM // If such a case is found here, migrate the record. to = loadTimePoint(dbObject, toFieldDeprecated); if (to != null) { @@ -1868,41 +2144,25 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { private RegattaLogDeviceCompetitorMappingEvent loadRegattaLogDeviceCompetitorMappingEvent(TimePoint createdAt, AbstractLogEventAuthor author, TimePoint logicalTimePoint, Serializable id, final DBObject dbObject, RegattaLikeIdentifier regattaLogIdentifier, DBObject outerDBObject) { - DeviceIdentifier device = null; - try { - device = loadDeviceId(deviceIdentifierServiceFinder, - (DBObject) dbObject.get(FieldNames.DEVICE_ID.name())); - } catch (Exception e) { - logger.log(Level.WARNING, "Could not load deviceId for RaceLogEvent", e); - e.printStackTrace(); - } - final Serializable competitorId = (Serializable) dbObject.get(FieldNames.COMPETITOR_ID.name()); - Competitor mappedTo = baseDomainFactory.getExistingCompetitorById( - competitorId); - final RegattaLogDeviceCompetitorMappingEventImpl result; - if (mappedTo == null) { - logger.severe("Found a "+RegattaLogDeviceCompetitorMappingEventImpl.class.getName()+ - " event but couldn't find competitor with ID "+competitorId); - result = null; - } else { - @SuppressWarnings("deprecation") // used only for auto-migration; may be removed in future releases - final FieldNames deprecatedFromFieldName = FieldNames.RACE_LOG_FROM; - @SuppressWarnings("deprecation") // used only for auto-migration; may be removed in future releases - final FieldNames deprecatedToFieldName = FieldNames.RACE_LOG_TO; - Triple times = loadFromToTimePoint(dbObject, FieldNames.REGATTA_LOG_FROM, deprecatedFromFieldName, FieldNames.REGATTA_LOG_TO, deprecatedToFieldName); - final TimePoint from = times.getA(); - final TimePoint to = times.getB(); - final boolean needsMigration = times.getC(); - result = new RegattaLogDeviceCompetitorMappingEventImpl(createdAt, logicalTimePoint, author, id, mappedTo, device, from, to); - if (needsMigration) { - // remove old version of mapping event - WriteResult removeResult = database.getCollection(CollectionNames.REGATTA_LOGS.name()).remove(outerDBObject); - assert removeResult.getN() == 1; - // and then insert using the fixed storage implementation - new MongoObjectFactoryImpl(database, serviceFinderFactory).storeRegattaLogEvent(regattaLogIdentifier, result); - } - } - return result; + return this.loadRegattaLogDeviceMappingEvent(createdAt, author, logicalTimePoint, id, dbObject, + regattaLogIdentifier, outerDBObject, + () -> baseDomainFactory + .getExistingCompetitorById((Serializable) dbObject.get(FieldNames.COMPETITOR_ID.name())), + RegattaLogDeviceCompetitorMappingEventImpl::new, + result -> new MongoObjectFactoryImpl(database, serviceFinderFactory) + .storeRegattaLogEvent(regattaLogIdentifier, result)); + } + + private RegattaLogDeviceBoatMappingEvent loadRegattaLogDeviceBoatMappingEvent(TimePoint createdAt, + AbstractLogEventAuthor author, TimePoint logicalTimePoint, Serializable id, final DBObject dbObject, + RegattaLikeIdentifier regattaLogIdentifier, DBObject outerDBObject) { + return this.loadRegattaLogDeviceMappingEvent(createdAt, author, logicalTimePoint, id, dbObject, + regattaLogIdentifier, outerDBObject, + () -> baseDomainFactory.getCompetitorStore() + .getExistingBoatById((Serializable) dbObject.get(FieldNames.RACE_LOG_BOAT_ID.name())), + RegattaLogDeviceBoatMappingEventImpl::new, + result -> new MongoObjectFactoryImpl(database, serviceFinderFactory) + .storeRegattaLogEvent(regattaLogIdentifier, result)); } private RegattaLogDeviceCompetitorBravoMappingEventImpl loadRegattaLogDeviceCompetitorBravoMappingEvent( @@ -1929,7 +2189,52 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { private MappingT loadRegattaLogDeviceCompetitorSensorDataMappingEvent( TimePoint createdAt, AbstractLogEventAuthor author, TimePoint logicalTimePoint, Serializable id, final DBObject dbObject, RegattaLikeIdentifier regattaLogIdentifier, DBObject outerDBObject, - AbstractRegattaLogDeviceCompetitorSensorDataMappingEventImpl.Factory factory) { + RegattaLogDeviceMappingEventImpl.Factory factory) { + return this.loadRegattaLogDeviceMappingEvent(createdAt, author, logicalTimePoint, id, + dbObject, regattaLogIdentifier, outerDBObject, + () -> baseDomainFactory + .getExistingCompetitorById((Serializable) dbObject.get(FieldNames.COMPETITOR_ID.name())), + factory, result -> new MongoObjectFactoryImpl(database, serviceFinderFactory) + .storeRegattaLogEvent(regattaLogIdentifier, result)); + } + + private RegattaLogDeviceBoatBravoMappingEventImpl loadRegattaLogDeviceBoatBravoMappingEvent( + TimePoint createdAt, AbstractLogEventAuthor author, TimePoint logicalTimePoint, Serializable id, + final DBObject dbObject, RegattaLikeIdentifier regattaLogIdentifier, DBObject outerDBObject) { + return loadRegattaLogDeviceBoatSensorDataMappingEvent(createdAt, author, logicalTimePoint, id, dbObject, + regattaLogIdentifier, outerDBObject, RegattaLogDeviceBoatBravoMappingEventImpl::new); + } + + private RegattaLogDeviceBoatBravoExtendedMappingEventImpl loadRegattaLogDeviceBoatBravoExtendedMappingEvent( + TimePoint createdAt, AbstractLogEventAuthor author, TimePoint logicalTimePoint, Serializable id, + final DBObject dbObject, RegattaLikeIdentifier regattaLogIdentifier, DBObject outerDBObject) { + return loadRegattaLogDeviceBoatSensorDataMappingEvent(createdAt, author, logicalTimePoint, id, dbObject, + regattaLogIdentifier, outerDBObject, RegattaLogDeviceBoatBravoExtendedMappingEventImpl::new); + } + + private RegattaLogDeviceBoatExpeditionExtendedMappingEventImpl loadRegattaLogDeviceBoatExpeditionExtendedMappingEvent( + TimePoint createdAt, AbstractLogEventAuthor author, TimePoint logicalTimePoint, Serializable id, + final DBObject dbObject, RegattaLikeIdentifier regattaLogIdentifier, DBObject outerDBObject) { + return loadRegattaLogDeviceBoatSensorDataMappingEvent(createdAt, author, logicalTimePoint, id, dbObject, + regattaLogIdentifier, outerDBObject, RegattaLogDeviceBoatExpeditionExtendedMappingEventImpl::new); + } + + private MappingT loadRegattaLogDeviceBoatSensorDataMappingEvent( + TimePoint createdAt, AbstractLogEventAuthor author, TimePoint logicalTimePoint, Serializable id, + final DBObject dbObject, RegattaLikeIdentifier regattaLogIdentifier, DBObject outerDBObject, + RegattaLogDeviceMappingEventImpl.Factory factory) { + return this.loadRegattaLogDeviceMappingEvent(createdAt, author, logicalTimePoint, id, + dbObject, regattaLogIdentifier, outerDBObject, + () -> baseDomainFactory + .getExistingBoatById((Serializable) dbObject.get(FieldNames.RACE_LOG_BOAT_ID.name())), + factory, result -> new MongoObjectFactoryImpl(database, serviceFinderFactory) + .storeRegattaLogEvent(regattaLogIdentifier, result)); + } + + private > MappingT loadRegattaLogDeviceMappingEvent( + TimePoint createdAt, AbstractLogEventAuthor author, TimePoint logicalTimePoint, Serializable id, + final DBObject dbObject, RegattaLikeIdentifier regattaLogIdentifier, DBObject outerDBObject, Supplier itemResolver, + RegattaLogDeviceMappingEventImpl.Factory factory, Consumer storeCallback) { DeviceIdentifier device = null; try { device = loadDeviceId(deviceIdentifierServiceFinder, (DBObject) dbObject.get(FieldNames.DEVICE_ID.name())); @@ -1937,8 +2242,7 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { logger.log(Level.WARNING, "Could not load deviceId for RaceLogEvent", e); e.printStackTrace(); } - Competitor mappedTo = baseDomainFactory.getExistingCompetitorById((Serializable) dbObject - .get(FieldNames.COMPETITOR_ID.name())); + ItemType mappedTo = itemResolver.get(); @SuppressWarnings("deprecation") // used only for auto-migration; may be removed in future releases final FieldNames deprecatedFromFieldName = FieldNames.RACE_LOG_FROM; @@ -1958,8 +2262,7 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { .remove(outerDBObject); assert removeResult.getN() == 1; // and then insert using the fixed storage implementation - new MongoObjectFactoryImpl(database, serviceFinderFactory).storeRegattaLogEvent(regattaLogIdentifier, - result); + storeCallback.accept(result); } return result; } @@ -1978,15 +2281,17 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { CourseBase courseData = new CourseDataImpl(courseName); int i = 0; for (Object object : dbCourseList) { - DBObject dbObject = (DBObject) object; + DBObject dbObject = (DBObject) object; Waypoint waypoint = null; PassingInstruction passingInstructions = null; String waypointPassingInstruction = (String) dbObject.get(FieldNames.WAYPOINT_PASSINGINSTRUCTIONS.name()); if (waypointPassingInstruction == null) { waypointPassingInstruction = (String) dbObject.get(FieldNames.WAYPOINT_PASSINGSIDE.name()); - if(waypointPassingInstruction != null) { - logger.info("Migrating PassingInstruction "+waypointPassingInstruction+" to field name WAYPOINT_PASSINGINSTRUCTIONS"); - if((i==0||i==dbCourseList.size()-1)&&waypointPassingInstruction.toLowerCase().equals("gate")){ + if (waypointPassingInstruction != null) { + logger.info("Migrating PassingInstruction " + waypointPassingInstruction + + " to field name WAYPOINT_PASSINGINSTRUCTIONS"); + if ((i == 0 || i == dbCourseList.size() - 1) + && waypointPassingInstruction.toLowerCase().equals("gate")) { logger.warning("Changing PassingInstructions of first or last Waypoint from Gate to Line."); waypointPassingInstruction = "Line"; } @@ -2016,11 +2321,13 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { Mark mark = loadMark((DBObject) dbObject.get(FieldNames.CONTROLPOINT_VALUE.name())); controlPoint = mark; } else if (controlPointClass.equals("Gate")) { - ControlPointWithTwoMarks cpwtm = loadControlPointWithTwoMarks((DBObject) dbObject.get(FieldNames.CONTROLPOINT_VALUE.name())); + ControlPointWithTwoMarks cpwtm = loadControlPointWithTwoMarks( + (DBObject) dbObject.get(FieldNames.CONTROLPOINT_VALUE.name())); dbObject.put(FieldNames.CONTROLPOINT_CLASS.name(), ControlPointWithTwoMarks.class.getSimpleName()); controlPoint = cpwtm; } else if (controlPointClass.equals(ControlPointWithTwoMarks.class.getSimpleName())) { - ControlPointWithTwoMarks cpwtm = loadControlPointWithTwoMarks((DBObject) dbObject.get(FieldNames.CONTROLPOINT_VALUE.name())); + ControlPointWithTwoMarks cpwtm = loadControlPointWithTwoMarks( + (DBObject) dbObject.get(FieldNames.CONTROLPOINT_VALUE.name())); controlPoint = cpwtm; } } @@ -2051,7 +2358,8 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { DBObject dbLeft = (DBObject) dbObject.get(FieldNames.CONTROLPOINTWITHTWOMARKS_LEFT.name()); if (dbLeft == null) { dbLeft = (DBObject) dbObject.get(FieldNames.GATE_LEFT.name()); - logger.info("Migrating left Mark of ControlPointWithTwoMarks " + controlPointName + " from old field GATE_LEFT to CONTROLPOINTWITHTWOMARKS_LEFT"); + logger.info("Migrating left Mark of ControlPointWithTwoMarks " + controlPointName + + " from old field GATE_LEFT to CONTROLPOINTWITHTWOMARKS_LEFT"); dbObject.put(FieldNames.CONTROLPOINTWITHTWOMARKS_LEFT.name(), dbLeft); dbObject.removeField(FieldNames.GATE_LEFT.name()); } @@ -2059,12 +2367,14 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { DBObject dbRight = (DBObject) dbObject.get(FieldNames.CONTROLPOINTWITHTWOMARKS_RIGHT.name()); if (dbRight == null) { dbRight = (DBObject) dbObject.get(FieldNames.GATE_RIGHT.name()); - logger.info("Migrating right Mark of ControlPointWithTwoMarks " + controlPointName + " from old field GATE_RIGHT to CONTROLPOINTWITHTWOMARKS_RIGHT"); + logger.info("Migrating right Mark of ControlPointWithTwoMarks " + controlPointName + + " from old field GATE_RIGHT to CONTROLPOINTWITHTWOMARKS_RIGHT"); dbObject.put(FieldNames.CONTROLPOINTWITHTWOMARKS_RIGHT.name(), dbRight); dbObject.removeField(FieldNames.GATE_RIGHT.name()); } Mark rightMark = loadMark(dbRight); - ControlPointWithTwoMarks gate = baseDomainFactory.createControlPointWithTwoMarks(controlPointId, leftMark, rightMark, controlPointName); + ControlPointWithTwoMarks gate = baseDomainFactory.createControlPointWithTwoMarks(controlPointId, leftMark, + rightMark, controlPointName); return gate; } @@ -2077,25 +2387,87 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { String markShape = (String) dbObject.get(FieldNames.MARK_SHAPE.name()); Object markTypeRaw = dbObject.get(FieldNames.MARK_TYPE.name()); MarkType markType = markTypeRaw == null ? null : MarkType.valueOf((String) markTypeRaw); - + Mark mark = baseDomainFactory.getOrCreateMark(markId, markName, markType, markColor, markShape, markPattern); return mark; } @Override public Collection loadAllCompetitors() { - ArrayList result = new ArrayList(); + Map competitorsById = new HashMap<>(); DBCollection collection = database.getCollection(CollectionNames.COMPETITORS.name()); try { for (DBObject o : collection.find()) { JSONObject json = Helpers.toJSONObjectSafe(new JSONParser().parse(JSON.serialize(o))); - Competitor c = competitorDeserializer.deserialize(json); - result.add(c); + Competitor c = competitorWithBoatRefDeserializer.deserialize(json); + // ensure that in case there should be multiple competitors with equal IDs in the DB + // only one will survive + if (competitorsById.containsKey(c.getId())) { + collection.remove(o); + } else { + competitorsById.put(c.getId(), c); + } } } catch (Exception e) { logger.log(Level.SEVERE, "Error connecting to MongoDB, unable to load competitors."); logger.log(Level.SEVERE, "loadCompetitors", e); } + return competitorsById.values(); + } + + @Override + public Iterable migrateLegacyCompetitorsIfRequired() { + Map competitorsById = null; + boolean competitorsCollectionExist = database.collectionExists(CollectionNames.COMPETITORS.name()); + boolean boatsCollectionCollectionExist = database.collectionExists(CollectionNames.BOATS.name()); + DBCollection orginalCompetitorCollection = database.getCollection(CollectionNames.COMPETITORS.name()); + // there is a corner case where tests can create just one competitor without boat + // before we migrate we need to check if this case + if (competitorsCollectionExist && !boatsCollectionCollectionExist) { + long competitorCount = orginalCompetitorCollection.count(); + if (competitorCount > 0) { + DBObject oneCompetitorDbObject = orginalCompetitorCollection.findOne(); + Object boatObject = oneCompetitorDbObject.get(CompetitorJsonConstants.FIELD_BOAT); + // only in case such a boat object exist we need a migration, because the new type stores only a boatID or no boat at all + if (boatObject != null) { + logger.log(Level.INFO, "Bug2822 DB-Migration: Rename COMPETITORS collection to COMPETITORS_BAK."); + competitorsById = new HashMap<>(); + orginalCompetitorCollection.rename(CollectionNames.COMPETITORS_BAK.name(), /* dropTarget */ true); + DBCollection collection = database.getCollection(CollectionNames.COMPETITORS_BAK.name()); + try { + logger.log(Level.INFO, "Bug2822 DB-Migration: Load old competitors with embedded boats from COMPETITORS_BAK."); + for (DBObject o : collection.find()) { + JSONObject json = Helpers.toJSONObjectSafe(new JSONParser().parse(JSON.serialize(o))); + CompetitorWithBoat c = legacyCompetitorWithBoatDeserializer.deserialize(json); + // accept only the first instance for any given ID + if (!competitorsById.containsKey(c.getId())) { + competitorsById.put(c.getId(), c); + } + } + } catch (Exception e) { + logger.log(Level.SEVERE, "Bug2822 DB-Migration: Error connecting to MongoDB, unable to load competitors."); + logger.log(Level.SEVERE, "Bug2822 DB-Migration: renameCompetitorsCollectionAndloadAllLegacyCompetitors", e); + } + } + } + } + return competitorsById==null?null:competitorsById.values(); + } + + @Override + public Collection loadAllBoats() { + ArrayList result = new ArrayList(); + DBCollection collection = database.getCollection(CollectionNames.BOATS.name()); + try { + for (DBObject o : collection.find()) { + JSONObject json = Helpers.toJSONObjectSafe(new JSONParser().parse(JSON.serialize(o))); + Boat b = boatDeserializer.deserialize(json); + result.add(b); + } + } catch (Exception e) { + logger.log(Level.SEVERE, "Error connecting to MongoDB, unable to load boats."); + logger.log(Level.SEVERE, "loadBoats", e); + } return result; } @@ -2103,7 +2475,7 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { public Iterable> loadAllDeviceConfigurations() { Map result = new HashMap<>(); DBCollection configurationCollection = database.getCollection(CollectionNames.CONFIGURATIONS.name()); - + try { for (DBObject dbObject : configurationCollection.find()) { Util.Pair entry = loadConfigurationEntry(dbObject); @@ -2113,20 +2485,21 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { logger.log(Level.SEVERE, "Error connecting to MongoDB, unable to load configurations."); logger.log(Level.SEVERE, "loadAllDeviceConfigurations", e); } - + return result.entrySet(); } private Util.Pair loadConfigurationEntry(DBObject dbObject) { DBObject matcherObject = (DBObject) dbObject.get(FieldNames.CONFIGURATION_MATCHER.name()); DBObject configObject = (DBObject) dbObject.get(FieldNames.CONFIGURATION_CONFIG.name()); - return new Util.Pair(loadConfigurationMatcher(matcherObject), + return new Util.Pair(loadConfigurationMatcher(matcherObject), loadConfiguration(configObject)); } private DeviceConfigurationMatcher loadConfigurationMatcher(DBObject matcherObject) { List clientIdentifiers = new ArrayList(); - BasicDBList clientIdentifiersObject = (BasicDBList) matcherObject.get(FieldNames.CONFIGURATION_MATCHER_CLIENTS.name()); + BasicDBList clientIdentifiersObject = (BasicDBList) matcherObject + .get(FieldNames.CONFIGURATION_MATCHER_CLIENTS.name()); if (clientIdentifiersObject != null) { for (Object clientIdentifier : clientIdentifiersObject) { clientIdentifiers.add(clientIdentifier.toString()); @@ -2142,25 +2515,27 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { JSONObject json = Helpers.toJSONObjectSafe(new JSONParser().parse(JSON.serialize(configObject))); configuration = deserializer.deserialize(json); } catch (JsonDeserializationException | ParseException e) { - logger.log(Level.SEVERE, "Error parsing configuration object from MongoDB, falling back to empty configuration."); + logger.log(Level.SEVERE, + "Error parsing configuration object from MongoDB, falling back to empty configuration."); logger.log(Level.SEVERE, "loadConfiguration", e); configuration = new DeviceConfigurationImpl(new RegattaConfigurationImpl()); } return configuration; } - + private DeviceIdentifier loadDeviceId( TypeBasedServiceFinder deviceIdentifierServiceFinder, DBObject deviceId) - throws TransformationException, NoCorrespondingServiceRegisteredException { + throws TransformationException, NoCorrespondingServiceRegisteredException { String deviceType = (String) deviceId.get(FieldNames.DEVICE_TYPE.name()); Object deviceTypeId = deviceId.get(FieldNames.DEVICE_TYPE_SPECIFIC_ID.name()); String stringRepresentation = (String) deviceId.get(FieldNames.DEVICE_STRING_REPRESENTATION.name()); - + try { - return deviceIdentifierServiceFinder.findService(deviceType).deserialize(deviceTypeId, deviceType, stringRepresentation); + return deviceIdentifierServiceFinder.findService(deviceType).deserialize(deviceTypeId, deviceType, + stringRepresentation); } catch (TransformationException e) { - return new PlaceHolderDeviceIdentifierSerializationHandler().deserialize( - stringRepresentation, deviceType, stringRepresentation); + return new PlaceHolderDeviceIdentifierSerializationHandler().deserialize(stringRepresentation, deviceType, + stringRepresentation); } } @@ -2186,7 +2561,7 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { } return resultUrls; } - + private ImageDescriptor loadImage(DBObject dbObject) { ImageDescriptor image = null; URL imageURL = loadURL(dbObject, FieldNames.IMAGE_URL); @@ -2194,8 +2569,8 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { String title = (String) dbObject.get(FieldNames.IMAGE_TITLE.name()); String subtitle = (String) dbObject.get(FieldNames.IMAGE_SUBTITLE.name()); String copyright = (String) dbObject.get(FieldNames.IMAGE_COPYRIGHT.name()); - String localeRaw = (String) dbObject.get(FieldNames.IMAGE_LOCALE.name()); - Locale locale = localeRaw != null ? Locale.forLanguageTag(localeRaw) : null; + String localeRaw = (String) dbObject.get(FieldNames.IMAGE_LOCALE.name()); + Locale locale = localeRaw != null ? Locale.forLanguageTag(localeRaw) : null; Number imageWidth = (Number) dbObject.get(FieldNames.IMAGE_WIDTH_IN_PX.name()); Number imageHeight = (Number) dbObject.get(FieldNames.IMAGE_HEIGHT_IN_PX.name()); TimePoint createdAtDate = loadTimePoint(dbObject, FieldNames.IMAGE_CREATEDATDATE); @@ -2218,18 +2593,18 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { } return image; } - + private VideoDescriptor loadVideo(DBObject dbObject) { VideoDescriptor video = null; URL videoURL = loadURL(dbObject, FieldNames.VIDEO_URL); - if(videoURL != null) { + if (videoURL != null) { String title = (String) dbObject.get(FieldNames.VIDEO_TITLE.name()); String subtitle = (String) dbObject.get(FieldNames.VIDEO_SUBTITLE.name()); String copyright = (String) dbObject.get(FieldNames.VIDEO_COPYRIGHT.name()); Object mimeTypeRaw = dbObject.get(FieldNames.VIDEO_MIMETYPE.name()); MimeType mimeType = mimeTypeRaw == null ? null : MimeType.valueOf((String) mimeTypeRaw); - String localeRaw = (String) dbObject.get(FieldNames.VIDEO_LOCALE.name()); - Locale locale = localeRaw != null ? Locale.forLanguageTag(localeRaw) : null; + String localeRaw = (String) dbObject.get(FieldNames.VIDEO_LOCALE.name()); + Locale locale = localeRaw != null ? Locale.forLanguageTag(localeRaw) : null; TimePoint createdAtDate = loadTimePoint(dbObject, FieldNames.VIDEO_CREATEDATDATE); BasicDBList tags = (BasicDBList) dbObject.get(FieldNames.VIDEO_TAGS.name()); Number lengthInSeconds = (Number) dbObject.get(FieldNames.VIDEO_LENGTH_IN_SECONDS.name()); @@ -2255,18 +2630,19 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { private URL loadURL(DBObject dbObject, FieldNames field) { URL result = null; String urlAsString = (String) dbObject.get(field.name()); - if(urlAsString != null) { + if (urlAsString != null) { try { result = new URL(urlAsString); } catch (MalformedURLException e) { - logger.severe("Error parsing URL '"+urlAsString+"' in field "+field.name()+"."); + logger.severe("Error parsing URL '" + urlAsString + "' in field " + field.name() + "."); } } - return result; + return result; } - + /** - * Legacy code to support conversion of old image and video URLs + * Legacy code to support conversion of old image and video URLs + * * @param event * @param eventDBObject */ @@ -2275,22 +2651,24 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { List imageURLs = new ArrayList(); List sponsorImageURLs = new ArrayList(); List videoURLs = new ArrayList(); - + String logoImageURLAsString = (String) eventDBObject.get(FieldNames.EVENT_LOGO_IMAGE_URL.name()); if (logoImageURLAsString != null) { try { logoImageURL = new URL(logoImageURLAsString); } catch (MalformedURLException e) { - logger.severe("Error parsing logo image URL "+logoImageURLAsString+" for event "+event.getName()+". Ignoring this URL."); + logger.severe("Error parsing logo image URL " + logoImageURLAsString + " for event " + event.getName() + + ". Ignoring this URL."); } } BasicDBList imageURLsJson = (BasicDBList) eventDBObject.get(FieldNames.EVENT_IMAGE_URLS.name()); if (imageURLsJson != null) { for (Object imageURL : imageURLsJson) { try { - imageURLs.add(new URL((String) imageURL)); + imageURLs.add(new URL((String) imageURL)); } catch (MalformedURLException e) { - logger.severe("Error parsing image URL "+imageURL+" for event "+event.getName()+". Ignoring this image URL."); + logger.severe("Error parsing image URL " + imageURL + " for event " + event.getName() + + ". Ignoring this image URL."); } } } @@ -2300,7 +2678,8 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { try { videoURLs.add(new URL((String) videoURL)); } catch (MalformedURLException e) { - logger.severe("Error parsing video URL "+videoURL+" for event "+event.getName()+". Ignoring this video URL."); + logger.severe("Error parsing video URL " + videoURL + " for event " + event.getName() + + ". Ignoring this video URL."); } } } @@ -2310,7 +2689,8 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { try { sponsorImageURLs.add(new URL((String) sponsorImageURL)); } catch (MalformedURLException e) { - logger.severe("Error parsing sponsor image URL "+sponsorImageURL+" for event "+event.getName()+". Ignoring this sponsor image URL."); + logger.severe("Error parsing sponsor image URL " + sponsorImageURL + " for event " + event.getName() + + ". Ignoring this sponsor image URL."); } } } @@ -2319,21 +2699,26 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { private boolean loadLegacySailorsInfoWebsiteURL(Event event, DBObject eventDBObject) { final boolean modified; - final String sailorsInfoWebSiteURLAsString = (String) eventDBObject.get(FieldNames.EVENT_SAILORS_INFO_WEBSITE_URL.name()); + final String sailorsInfoWebSiteURLAsString = (String) eventDBObject + .get(FieldNames.EVENT_SAILORS_INFO_WEBSITE_URL.name()); if (sailorsInfoWebSiteURLAsString != null) { try { - // The legacy sailors info URL (only used at Kieler/Travemuender Woche events) used to have 2 localized versions: + // The legacy sailors info URL (only used at Kieler/Travemuender Woche events) used to have 2 localized + // versions: // The German version with no suffix (e.g. http://sailorsinfo.travemuender-woche.com) - // The English/international version with "/en" suffix (e.g. http://sailorsinfo.travemuender-woche.com/en) + // The English/international version with "/en" suffix (e.g. + // http://sailorsinfo.travemuender-woche.com/en) if (!event.hasSailorsInfoWebsiteURL(null)) { - final String englishURL = sailorsInfoWebSiteURLAsString + (sailorsInfoWebSiteURLAsString.endsWith("/") ? "" : "/") + "en"; + final String englishURL = sailorsInfoWebSiteURLAsString + + (sailorsInfoWebSiteURLAsString.endsWith("/") ? "" : "/") + "en"; event.setSailorsInfoWebsiteURL(null, new URL(englishURL)); } if (!event.hasSailorsInfoWebsiteURL(Locale.GERMAN)) { event.setSailorsInfoWebsiteURL(Locale.GERMAN, new URL(sailorsInfoWebSiteURLAsString)); } } catch (MalformedURLException e) { - logger.severe("Error parsing sailors info website URL "+sailorsInfoWebSiteURLAsString+" for event "+event.getName()+". Ignoring this URL."); + logger.severe("Error parsing sailors info website URL " + sailorsInfoWebSiteURLAsString + " for event " + + event.getName() + ". Ignoring this URL."); } modified = true; } else { @@ -2343,18 +2728,21 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { } @Override - public ConnectivityParametersLoadingResult loadConnectivityParametersForRacesToRestore(Consumer callback) { - final DBCollection collection = database.getCollection(CollectionNames.CONNECTIVITY_PARAMS_FOR_RACES_TO_BE_RESTORED.name()); + public ConnectivityParametersLoadingResult loadConnectivityParametersForRacesToRestore( + Consumer callback) { + final DBCollection collection = database + .getCollection(CollectionNames.CONNECTIVITY_PARAMS_FOR_RACES_TO_BE_RESTORED.name()); final DBCursor cursor = collection.find(); final int count = cursor.count(); - logger.info("Restoring "+count+" races"); + logger.info("Restoring " + count + " races"); final List restoreParameters = new ArrayList<>(); // consume all elements quickly to avoid cursor/DB timeouts while restoring many races; // MongoDB cursors by default time out after ten minutes if no more batch (of by default 100 elements) // has been requested during this time. Util.addAll(cursor, restoreParameters); - logger.info("Obtained "+restoreParameters.size()+" race parameters to restore"); - final ScheduledExecutorService backgroundExecutor = ThreadPoolUtil.INSTANCE.getDefaultBackgroundTaskThreadPoolExecutor(); + logger.info("Obtained " + restoreParameters.size() + " race parameters to restore"); + final ScheduledExecutorService backgroundExecutor = ThreadPoolUtil.INSTANCE + .getDefaultBackgroundTaskThreadPoolExecutor(); final Set> waiters = new HashSet<>(); logger.info("Starting to restore races"); final AtomicInteger i = new AtomicInteger(); @@ -2362,35 +2750,39 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { final FutureTask waiter = new FutureTask<>(() -> { final String type = (String) o.get(TypeBasedServiceFinder.TYPE); final int finalI = i.incrementAndGet(); - logger.info("Applying to restore race #"+ finalI +"/"+count+" of type "+type); - raceTrackingConnectivityParamsServiceFinder.applyServiceWhenAvailable(type, connectivityParamsPersistenceService -> { - logger.info("Restoring race #"+ finalI +"/"+count+" of type "+type); - final Map map = new HashMap<>(); - for (final String key : o.keySet()) { - if (!key.equals(TypeBasedServiceFinder.TYPE)) { - map.put(key, o.get(key)); - } - } - try { - final RaceTrackingConnectivityParameters params = connectivityParamsPersistenceService.mapTo(map); - if (params != null) { - callback.accept(params); - logger.info("Done restoring race #"+ finalI +"/"+count+" of type "+type); - } else { - logger.warning("Couldn't restore race #"+ finalI +"/"+count+" of type "+type+ - " because the parameters loaded from the DB couldn't be mapped. Maybe the owning leaderboard was removed?"); - } - } catch (Exception e) { - logger.log(Level.SEVERE, "Exception trying to load race #"+ finalI +"/"+count+" of type "+type+ - " from restore connectivity parameters " - + o + " with handler " + connectivityParamsPersistenceService, e); - } - }); + logger.info("Applying to restore race #" + finalI + "/" + count + " of type " + type); + raceTrackingConnectivityParamsServiceFinder.applyServiceWhenAvailable(type, + connectivityParamsPersistenceService -> { + logger.info("Restoring race #" + finalI + "/" + count + " of type " + type); + final Map map = new HashMap<>(); + for (final String key : o.keySet()) { + if (!key.equals(TypeBasedServiceFinder.TYPE)) { + map.put(key, o.get(key)); + } + } + try { + final RaceTrackingConnectivityParameters params = connectivityParamsPersistenceService + .mapTo(map); + if (params != null) { + callback.accept(params); + logger.info("Done restoring race #" + finalI + "/" + count + " of type " + type); + } else { + logger.warning("Couldn't restore race #" + finalI + "/" + count + " of type " + type + + " because the parameters loaded from the DB couldn't be mapped. Maybe the owning leaderboard was removed?"); + } + } catch (Exception e) { + logger.log(Level.SEVERE, + "Exception trying to load race #" + finalI + "/" + count + " of type " + type + + " from restore connectivity parameters " + o + " with handler " + + connectivityParamsPersistenceService, + e); + } + }); }, /* void result */ null); waiters.add(waiter); backgroundExecutor.execute(waiter); } - logger.info("Done restoring races; restored "+i+" of "+count+" races"); + logger.info("Done restoring races; restored " + i + " of " + count + " races"); return new ConnectivityParametersLoadingResult() { @Override public int getNumberOfParametersToLoad() { diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/FieldNames.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/FieldNames.java index 54efc610203..b557b018195 100644 --- a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/FieldNames.java +++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/FieldNames.java @@ -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 diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/MigratableRegattaImpl.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/MigratableRegattaImpl.java new file mode 100644 index 00000000000..4b15db549b3 --- /dev/null +++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/MigratableRegattaImpl.java @@ -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 MigratableRegattaImpl(RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, String name, + BoatClass boatClass, boolean canBoatsOfCompetitorsChangePerRace, TimePoint startDate, TimePoint endDate, + Iterable 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); + } + } +} diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/MongoObjectFactoryImpl.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/MongoObjectFactoryImpl.java index bdf4558bd65..10127a7c2bf 100644 --- a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/MongoObjectFactoryImpl.java +++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/MongoObjectFactoryImpl.java @@ -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 deviceIdentifierServiceFinder; private final TypeBasedServiceFinder 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 competitors) { + public void storeCompetitors(Iterable competitors) { if (competitors != null && !Util.isEmpty(competitors)) { + List competitorsWithoutBoat = new ArrayList<>(); + List 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 competitors) { + if (!Util.isEmpty(competitors)) { DBCollection collection = database.getCollection(CollectionNames.COMPETITORS.name()); List competitorsDB = new ArrayList<>(); for (Competitor competitor : competitors) { @@ -1313,9 +1357,22 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory { } } + private void storeCompetitorsWithBoat(Iterable competitors) { + if (!Util.isEmpty(competitors)) { + DBCollection collection = database.getCollection(CollectionNames.COMPETITORS.name()); + List 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 boats) { + if (boats != null && !Util.isEmpty(boats)) { + DBCollection collection = database.getCollection(CollectionNames.BOATS.name()); + List 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()); diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/MongoRegattaLogStoreVisitor.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/MongoRegattaLogStoreVisitor.java index 09398ffcbaa..39cd2cc9869 100644 --- a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/MongoRegattaLogStoreVisitor.java +++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/MongoRegattaLogStoreVisitor.java @@ -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); diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter.test/META-INF/MANIFEST.MF b/java/com.sap.sailing.domain.racelogtrackingadapter.test/META-INF/MANIFEST.MF index 26586285954..0921b8acdcf 100644 --- a/java/com.sap.sailing.domain.racelogtrackingadapter.test/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.domain.racelogtrackingadapter.test/META-INF/MANIFEST.MF @@ -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, diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/AbstractGPSFixStoreTest.java b/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/AbstractGPSFixStoreTest.java index dcb29f6b6e5..802d90c8fbe 100644 --- a/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/AbstractGPSFixStoreTest.java +++ b/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/AbstractGPSFixStoreTest.java @@ -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. emptyList(), EmptyWindStore.INSTANCE, 0, 0, 0, /* useMarkPassingCalculator */ false, OneDesignRankingMetric::new, mock(RaceLogResolver.class)); diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/CreateAndTrackWithRaceLogTest.java b/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/CreateAndTrackWithRaceLogTest.java index 21a9059b81e..286b25910ee 100644 --- a/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/CreateAndTrackWithRaceLogTest.java +++ b/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/CreateAndTrackWithRaceLogTest.java @@ -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. singletonList(series), /* persistent */ true, + /* canBoatsOfCompetitorsChangePerRace */ true, /* startDate */null, /* endDate */null, + UUID.randomUUID(), Collections. 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))); diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/RaceLogFixTrackerManagerTest.java b/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/RaceLogFixTrackerManagerTest.java index 5558db801d9..69197beb49d 100644 --- a/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/RaceLogFixTrackerManagerTest.java +++ b/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/RaceLogFixTrackerManagerTest.java @@ -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 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. emptyList(), EmptyWindStore.INSTANCE, 0, 0, 0, /* useMarkPassingCalculator */ false, OneDesignRankingMetric::new, mock(RaceLogResolver.class)); diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/SensorFixStoreAndLoadTest.java b/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/SensorFixStoreAndLoadTest.java index 1ec8c9a5c6e..9c104ebeac4 100644 --- a/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/SensorFixStoreAndLoadTest.java +++ b/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/SensorFixStoreAndLoadTest.java @@ -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 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. 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 > SensorFixMapper createCompetitorMapper( Class> 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 { diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/TrackedRaceLoadsFixesTest.java b/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/TrackedRaceLoadsFixesTest.java index 56bc80be5bc..0aae191aa2c 100755 --- a/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/TrackedRaceLoadsFixesTest.java +++ b/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/TrackedRaceLoadsFixesTest.java @@ -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 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); diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/TrackedRaceStartTimeInferenceTest.java b/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/TrackedRaceStartTimeInferenceTest.java index a2daa7c3899..d9d3763e380 100755 --- a/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/TrackedRaceStartTimeInferenceTest.java +++ b/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/TrackedRaceStartTimeInferenceTest.java @@ -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 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. 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 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. 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 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. 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 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. 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 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. emptyList(), EmptyWindStore.INSTANCE, 0, 0, 0, /* useMarkPassingCalculator */ false, diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/BravoDataFixMapper.java b/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/BravoDataFixMapper.java index da5934327e4..184cc881c02 100644 --- a/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/BravoDataFixMapper.java +++ b/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/BravoDataFixMapper.java @@ -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> eventType) { - return RegattaLogDeviceCompetitorBravoMappingEventImpl.class.isAssignableFrom(eventType); + return RegattaLogDeviceCompetitorBravoMappingEventImpl.class.isAssignableFrom(eventType) + || RegattaLogDeviceBoatBravoMappingEventImpl.class.isAssignableFrom(eventType); } } \ No newline at end of file diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/BravoExtendedDataFixMapper.java b/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/BravoExtendedDataFixMapper.java index 15f96e22bc9..a75ee57633a 100644 --- a/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/BravoExtendedDataFixMapper.java +++ b/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/BravoExtendedDataFixMapper.java @@ -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> eventType) { - return RegattaLogDeviceCompetitorBravoExtendedMappingEventImpl.class.isAssignableFrom(eventType); + return RegattaLogDeviceCompetitorBravoExtendedMappingEventImpl.class.isAssignableFrom(eventType) + || RegattaLogDeviceBoatBravoExtendedMappingEventImpl.class.isAssignableFrom(eventType); } } \ No newline at end of file diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/ExpeditionExtendedDataFixMapper.java b/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/ExpeditionExtendedDataFixMapper.java index 09405591969..e1aa45aad0c 100755 --- a/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/ExpeditionExtendedDataFixMapper.java +++ b/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/ExpeditionExtendedDataFixMapper.java @@ -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> eventType) { - return RegattaLogDeviceCompetitorExpeditionExtendedMappingEventImpl.class.isAssignableFrom(eventType); + return RegattaLogDeviceCompetitorExpeditionExtendedMappingEventImpl.class.isAssignableFrom(eventType) + || RegattaLogDeviceBoatExpeditionExtendedMappingEventImpl.class.isAssignableFrom(eventType); } } \ No newline at end of file diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/RaceLogConnectivityParams.java b/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/RaceLogConnectivityParams.java index 07a26c8e37e..c9a161847e7 100644 --- a/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/RaceLogConnectivityParams.java +++ b/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/RaceLogConnectivityParams.java @@ -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( diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/RaceLogRaceTracker.java b/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/RaceLogRaceTracker.java index e354df7ba7e..e66c859c2e5 100755 --- a/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/RaceLogRaceTracker.java +++ b/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/RaceLogRaceTracker.java @@ -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 competitors = raceColumn.getAllCompetitors(params.getFleet()); + Map 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 sidelines = Collections. emptyList(); // set race definition, so race is linked to leaderboard automatically trackedRegatta.getRegatta().addRace(raceDef); diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/RaceLogTrackingAdapterImpl.java b/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/RaceLogTrackingAdapterImpl.java index 9b9dc0567f6..ff9016b5e0f 100755 --- a/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/RaceLogTrackingAdapterImpl.java +++ b/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/RaceLogTrackingAdapterImpl.java @@ -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 allCompetitors) { - return Util.getDominantObject(()->StreamSupport.stream(allCompetitors.spliterator(), /* parallel */ false).map(c->c.getBoat().getBoatClass()).iterator()); + private BoatClass findDominatingBoatClass(Iterable 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> toRaces) { - Iterable competitorsToCopy = fromRaceColumn.getAllCompetitors(fromFleet); + Map competitorsAndBoatsToCopy = fromRaceColumn.getAllCompetitorsAndTheirBoats(fromFleet); for (Pair 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: diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/fixtracker/FixLoaderAndTracker.java b/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/fixtracker/FixLoaderAndTracker.java index d83e9517bb5..fad3607d81f 100755 --- a/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/fixtracker/FixLoaderAndTracker.java +++ b/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/fixtracker/FixLoaderAndTracker.java @@ -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>() { + @Override + public void accept(DeviceMappingWithRegattaLogEvent mapping) { + mapping.getRegattaLogEvent().accept(new MappingEventVisitor() { + @Override + public void visit(RegattaLogDeviceCompetitorSensorDataMappingEvent event) { + recordSensorFixForCompetitor(event.getMappedTo(), event); } - SensorFixMapper, Competitor> mapper = sensorFixMapperFactory - .createCompetitorMapper(event.getClass()); - DynamicSensorFixTrack 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, Competitor> mapper = sensorFixMapperFactory + .createCompetitorMapper((Class>) event.getClass()); + DynamicSensorFixTrack 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 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 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 progressConsumer, + final BooleanSupplier stopCallback, RegattaLogDeviceMappingEvent event, + final Competitor competitor) { + @SuppressWarnings("unchecked") SensorFixMapper, Competitor> mapper = sensorFixMapperFactory - .createCompetitorMapper(event.getClass()); - DynamicTrack track = mapper.getTrack(trackedRace, event.getMappedTo()); + .createCompetitorMapper((Class>) event.getClass()); + DynamicTrack 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 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 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 track = trackedRace.getOrCreateTrack(event.getMappedTo()); diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/fixtracker/RegattaLogDeviceMappings.java b/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/fixtracker/RegattaLogDeviceMappings.java index b837aa38419..bcf7c25dc7c 100644 --- a/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/fixtracker/RegattaLogDeviceMappings.java +++ b/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/fixtracker/RegattaLogDeviceMappings.java @@ -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 { 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 { "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) { diff --git a/java/com.sap.sailing.domain.shared.android.test/src/com/sap/sailing/domain/base/racegroup/impl/CurrentRaceFilterImplTest.java b/java/com.sap.sailing.domain.shared.android.test/src/com/sap/sailing/domain/base/racegroup/impl/CurrentRaceFilterImplTest.java index a1acdfca3ab..d8aa8e0a8e3 100644 --- a/java/com.sap.sailing.domain.shared.android.test/src/com/sap/sailing/domain/base/racegroup/impl/CurrentRaceFilterImplTest.java +++ b/java/com.sap.sailing.domain.shared.android.test/src/com/sap/sailing/domain/base/racegroup/impl/CurrentRaceFilterImplTest.java @@ -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 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 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 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 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; } diff --git a/java/com.sap.sailing.domain.shared.android.test/src/com/sap/sailing/domain/regattalog/tracking/analyzing/test/CompetitorDeregistrationTest.java b/java/com.sap.sailing.domain.shared.android.test/src/com/sap/sailing/domain/regattalog/tracking/analyzing/test/CompetitorDeregistrationTest.java index f008ccf6d5d..44d2fd0286d 100755 --- a/java/com.sap.sailing.domain.shared.android.test/src/com/sap/sailing/domain/regattalog/tracking/analyzing/test/CompetitorDeregistrationTest.java +++ b/java/com.sap.sailing.domain.shared.android.test/src/com/sap/sailing/domain/regattalog/tracking/analyzing/test/CompetitorDeregistrationTest.java @@ -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() { diff --git a/java/com.sap.sailing.domain.shared.android.test/src/com/sap/sailing/domain/regattalog/tracking/analyzing/test/DeviceMappingFinderTest.java b/java/com.sap.sailing.domain.shared.android.test/src/com/sap/sailing/domain/regattalog/tracking/analyzing/test/DeviceMappingFinderTest.java index e43e8cece89..b66663f93f2 100644 --- a/java/com.sap.sailing.domain.shared.android.test/src/com/sap/sailing/domain/regattalog/tracking/analyzing/test/DeviceMappingFinderTest.java +++ b/java/com.sap.sailing.domain.shared.android.test/src/com/sap/sailing/domain/regattalog/tracking/analyzing/test/DeviceMappingFinderTest.java @@ -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; diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/RaceLogEventData.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/RaceLogEventData.java index c715c3351d9..5b7ab1c3611 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/RaceLogEventData.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/RaceLogEventData.java @@ -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 getInvolvedBoats(); + List getInvolvedCompetitors(); } diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/RaceLogEventVisitor.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/RaceLogEventVisitor.java index a8d8af89858..f51bda94195 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/RaceLogEventVisitor.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/RaceLogEventVisitor.java @@ -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); diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/analyzing/impl/MarkPassingDataFinder.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/analyzing/impl/MarkPassingDataFinder.java index e5ead97f468..247811d4b65 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/analyzing/impl/MarkPassingDataFinder.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/analyzing/impl/MarkPassingDataFinder.java @@ -23,11 +23,11 @@ public class MarkPassingDataFinder extends RaceLogAnalyzer(castedEvent.getInvolvedBoats().get(0), castedEvent + result.add(new Triple(castedEvent.getInvolvedCompetitors().get(0), castedEvent .getZeroBasedIndexOfPassedWaypoint(), castedEvent.getTimePointOfFixedPassing())); } else if (event instanceof RaceLogSuppressedMarkPassingsEvent){ RaceLogSuppressedMarkPassingsEvent castedEvent = (RaceLogSuppressedMarkPassingsEvent) event; - result.add(new Triple(castedEvent.getInvolvedBoats().get(0), castedEvent.getZeroBasedIndexOfFirstSuppressedWaypoint(), null)); + result.add(new Triple(castedEvent.getInvolvedCompetitors().get(0), castedEvent.getZeroBasedIndexOfFirstSuppressedWaypoint(), null)); } } return result; diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/analyzing/impl/RaceLogRegisteredCompetitorsAndBoatsAnalyzer.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/analyzing/impl/RaceLogRegisteredCompetitorsAndBoatsAnalyzer.java new file mode 100644 index 00000000000..c03888b1cfe --- /dev/null +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/analyzing/impl/RaceLogRegisteredCompetitorsAndBoatsAnalyzer.java @@ -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> { + + public RaceLogRegisteredCompetitorsAndBoatsAnalyzer(RaceLog raceLog) { + super(raceLog); + } + + @Override + protected Map performAnalysis() { + final Map 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; + } +} diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/impl/AbstractRaceLogChangedVisitor.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/impl/AbstractRaceLogChangedVisitor.java index 55f3e0be220..9662e231f16 100755 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/impl/AbstractRaceLogChangedVisitor.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/impl/AbstractRaceLogChangedVisitor.java @@ -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); diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/impl/BaseRaceLogEventVisitor.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/impl/BaseRaceLogEventVisitor.java index 9aa980bb7d9..8f35eb16aba 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/impl/BaseRaceLogEventVisitor.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/impl/BaseRaceLogEventVisitor.java @@ -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) { } + } diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/impl/RaceLogEventDataImpl.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/impl/RaceLogEventDataImpl.java index 68f1f88def4..45933ee3450 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/impl/RaceLogEventDataImpl.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/impl/RaceLogEventDataImpl.java @@ -21,7 +21,7 @@ public class RaceLogEventDataImpl implements RaceLogEventData { } @Override - public List getInvolvedBoats() { + public List 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(); } } diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/impl/RaceLogEventImpl.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/impl/RaceLogEventImpl.java index 2c4092a3130..ffd67eda973 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/impl/RaceLogEventImpl.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/impl/RaceLogEventImpl.java @@ -36,8 +36,8 @@ public abstract class RaceLogEventImpl extends AbstractLogEventImpl getInvolvedBoats() { - return raceLogEventData.getInvolvedBoats(); + public List getInvolvedCompetitors() { + return raceLogEventData.getInvolvedCompetitors(); } @Override diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/impl/RaceLogFixedMarkPassingEventImpl.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/impl/RaceLogFixedMarkPassingEventImpl.java index ebd95a0a6fe..c3ef632647b 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/impl/RaceLogFixedMarkPassingEventImpl.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/impl/RaceLogFixedMarkPassingEventImpl.java @@ -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(); } } diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/impl/RaceLogRevokeEventImpl.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/impl/RaceLogRevokeEventImpl.java index 8f5d7560275..ab7dd407aef 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/impl/RaceLogRevokeEventImpl.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/impl/RaceLogRevokeEventImpl.java @@ -35,8 +35,8 @@ public class RaceLogRevokeEventImpl extends RevokeEventImpl } @Override - public List getInvolvedBoats() { - return raceLogEventData.getInvolvedBoats(); + public List getInvolvedCompetitors() { + return raceLogEventData.getInvolvedCompetitors(); } @Override diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/impl/RaceLogSuppressedMarkPassingsEventImpl.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/impl/RaceLogSuppressedMarkPassingsEventImpl.java index f3eb85c285a..941515811f9 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/impl/RaceLogSuppressedMarkPassingsEventImpl.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/impl/RaceLogSuppressedMarkPassingsEventImpl.java @@ -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(); } } diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/tracking/RaceLogRegisterCompetitorEvent.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/tracking/RaceLogRegisterCompetitorEvent.java index 185a9f226a8..22ae763a964 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/tracking/RaceLogRegisterCompetitorEvent.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/tracking/RaceLogRegisterCompetitorEvent.java @@ -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 { - +/** + * 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 { + Boat getBoat(); } diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/tracking/analyzing/impl/RegisteredCompetitorsAndBoatsAnalyzer.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/tracking/analyzing/impl/RegisteredCompetitorsAndBoatsAnalyzer.java new file mode 100644 index 00000000000..a6606a2aae7 --- /dev/null +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/tracking/analyzing/impl/RegisteredCompetitorsAndBoatsAnalyzer.java @@ -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> { + private RegattaLog regattaLog; + + public RegisteredCompetitorsAndBoatsAnalyzer(RaceLog raceLog, RegattaLog regattaLog) { + super(raceLog); + this.regattaLog = regattaLog; + } + + @Override + protected Map performAnalysis() { + final Map 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; + } +} diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/tracking/impl/RaceLogRegisterCompetitorEventImpl.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/tracking/impl/RaceLogRegisterCompetitorEventImpl.java index 1c84b6b429b..8dd0fae8af4 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/tracking/impl/RaceLogRegisterCompetitorEventImpl.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/tracking/impl/RaceLogRegisterCompetitorEventImpl.java @@ -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 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 getInvolvedBoats() { - return Collections.singletonList(getCompetitor()); + public List 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(); + } } diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/MappingEventVisitor.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/MappingEventVisitor.java index 9fc3472b47c..6497e1729ee 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/MappingEventVisitor.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/MappingEventVisitor.java @@ -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); } diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/RegattaLogEventVisitor.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/RegattaLogEventVisitor.java index 28cc48fc441..5fd54895049 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/RegattaLogEventVisitor.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/RegattaLogEventVisitor.java @@ -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); diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/events/RegattaLogDeviceBoatMappingEvent.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/events/RegattaLogDeviceBoatMappingEvent.java new file mode 100644 index 00000000000..0e7bf65c364 --- /dev/null +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/events/RegattaLogDeviceBoatMappingEvent.java @@ -0,0 +1,7 @@ +package com.sap.sailing.domain.abstractlog.regatta.events; + +import com.sap.sailing.domain.base.Boat; + +public interface RegattaLogDeviceBoatMappingEvent extends RegattaLogDeviceMappingEvent { + +} diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/events/RegattaLogDeviceBoatSensorDataMappingEvent.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/events/RegattaLogDeviceBoatSensorDataMappingEvent.java new file mode 100644 index 00000000000..f8f7623b10a --- /dev/null +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/events/RegattaLogDeviceBoatSensorDataMappingEvent.java @@ -0,0 +1,7 @@ +package com.sap.sailing.domain.abstractlog.regatta.events; + +import com.sap.sailing.domain.base.Boat; + +public interface RegattaLogDeviceBoatSensorDataMappingEvent extends RegattaLogDeviceMappingEvent { + +} diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/events/RegattaLogDeviceMappingEventImpl.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/events/RegattaLogDeviceMappingEventImpl.java index 08f4e08c1d9..f9921024fd0 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/events/RegattaLogDeviceMappingEventImpl.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/events/RegattaLogDeviceMappingEventImpl.java @@ -13,6 +13,12 @@ public abstract class RegattaLogDeviceMappingEventImpl RegattaLogDeviceMappingEvent { private static final long serialVersionUID = -8439653251231710356L; + public interface Factory> { + 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; diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/events/RegattaLogRegisterBoatEvent.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/events/RegattaLogRegisterBoatEvent.java new file mode 100644 index 00000000000..65aed03c3b6 --- /dev/null +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/events/RegattaLogRegisterBoatEvent.java @@ -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(); +} diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/events/RegattaLogRegisterCompetitorEvent.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/events/RegattaLogRegisterCompetitorEvent.java index 4362da82dd3..4c253bf9c85 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/events/RegattaLogRegisterCompetitorEvent.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/events/RegattaLogRegisterCompetitorEvent.java @@ -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 { +/** +* 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 { } diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/events/impl/AbstractRegattaLogDeviceBoatSensorDataMappingEventImpl.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/events/impl/AbstractRegattaLogDeviceBoatSensorDataMappingEventImpl.java new file mode 100644 index 00000000000..d02664387c0 --- /dev/null +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/events/impl/AbstractRegattaLogDeviceBoatSensorDataMappingEventImpl.java @@ -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 + 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); + } +} diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/events/impl/AbstractRegattaLogDeviceCompetitorSensorDataMappingEventImpl.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/events/impl/AbstractRegattaLogDeviceCompetitorSensorDataMappingEventImpl.java index dbe9f1e80ac..073c203fbbe 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/events/impl/AbstractRegattaLogDeviceCompetitorSensorDataMappingEventImpl.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/events/impl/AbstractRegattaLogDeviceCompetitorSensorDataMappingEventImpl.java @@ -18,12 +18,6 @@ public abstract class AbstractRegattaLogDeviceCompetitorSensorDataMappingEventIm implements RegattaLogDeviceCompetitorSensorDataMappingEvent { private static final long serialVersionUID = -1494030544804758753L; - public interface Factory { - 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) { diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/events/impl/RegattaLogDeviceBoatBravoExtendedMappingEventImpl.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/events/impl/RegattaLogDeviceBoatBravoExtendedMappingEventImpl.java new file mode 100644 index 00000000000..8f6e5899fc1 --- /dev/null +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/events/impl/RegattaLogDeviceBoatBravoExtendedMappingEventImpl.java @@ -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); + } +} diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/events/impl/RegattaLogDeviceBoatBravoMappingEventImpl.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/events/impl/RegattaLogDeviceBoatBravoMappingEventImpl.java new file mode 100644 index 00000000000..0a51ccaf52b --- /dev/null +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/events/impl/RegattaLogDeviceBoatBravoMappingEventImpl.java @@ -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); + } +} diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/events/impl/RegattaLogDeviceBoatExpeditionExtendedMappingEventImpl.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/events/impl/RegattaLogDeviceBoatExpeditionExtendedMappingEventImpl.java new file mode 100644 index 00000000000..6b314a88c18 --- /dev/null +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/events/impl/RegattaLogDeviceBoatExpeditionExtendedMappingEventImpl.java @@ -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); + } +} diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/events/impl/RegattaLogDeviceBoatMappingEventImpl.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/events/impl/RegattaLogDeviceBoatMappingEventImpl.java new file mode 100644 index 00000000000..1e0cea374c6 --- /dev/null +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/events/impl/RegattaLogDeviceBoatMappingEventImpl.java @@ -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 + 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); + } +} diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/events/impl/RegattaLogDeviceCompetitorBravoMappingEventImpl.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/events/impl/RegattaLogDeviceCompetitorBravoMappingEventImpl.java index 831aa75a927..fde9a164692 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/events/impl/RegattaLogDeviceCompetitorBravoMappingEventImpl.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/events/impl/RegattaLogDeviceCompetitorBravoMappingEventImpl.java @@ -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) { diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/events/impl/RegattaLogRegisterBoatEventImpl.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/events/impl/RegattaLogRegisterBoatEventImpl.java new file mode 100644 index 00000000000..20b5c649900 --- /dev/null +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/events/impl/RegattaLogRegisterBoatEventImpl.java @@ -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 + 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(); + } +} diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/events/impl/RegattaLogRegisterCompetitorEventImpl.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/events/impl/RegattaLogRegisterCompetitorEventImpl.java index d1f19322fa3..988642ebe66 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/events/impl/RegattaLogRegisterCompetitorEventImpl.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/events/impl/RegattaLogRegisterCompetitorEventImpl.java @@ -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 implements RegattaLogRegisterCompetitorEvent { private static final long serialVersionUID = -5114645637316367845L; diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/impl/BaseRegattaLogEventVisitor.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/impl/BaseRegattaLogEventVisitor.java index 808a9b9af97..72c39e98d77 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/impl/BaseRegattaLogEventVisitor.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/impl/BaseRegattaLogEventVisitor.java @@ -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) { } diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/impl/RegattaLogEventListener.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/impl/RegattaLogEventListener.java index bf9d9fd1cb2..f2cc5bc69ee 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/impl/RegattaLogEventListener.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/impl/RegattaLogEventListener.java @@ -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); + } } diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/tracking/analyzing/impl/RegattaLogBoatDeregistrator.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/tracking/analyzing/impl/RegattaLogBoatDeregistrator.java new file mode 100644 index 00000000000..33cb57dbad8 --- /dev/null +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/tracking/analyzing/impl/RegattaLogBoatDeregistrator.java @@ -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.

+ * + * Clients must call {@link #deregister} with the result of {@link #analyze()} in order to actually perform the + * deregistration. + * + * @author Frank Mittag + * + */ +public class RegattaLogBoatDeregistrator, EventT extends AbstractLogEvent, VisitorT> +extends BaseLogAnalyzer> { + + private static final Logger logger = Logger.getLogger(RegattaLogBoatDeregistrator.class.getName()); + protected final Iterable boatsToDeregister; + private AbstractLogEventAuthor eventAuthor; + + public RegattaLogBoatDeregistrator(LogT log, Iterable boatsToDeregister, AbstractLogEventAuthor eventAuthor) { + super(log); + this.boatsToDeregister = boatsToDeregister; + this.eventAuthor = eventAuthor; + } + + @Override + protected Set performAnalysis() { + final Set result = new HashSet<>(); + final HashSet boatSet = new HashSet(); + 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 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); + } + } + } +} diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/tracking/analyzing/impl/RegattaLogBoatsInLogAnalyzer.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/tracking/analyzing/impl/RegattaLogBoatsInLogAnalyzer.java new file mode 100644 index 00000000000..5f6d438def8 --- /dev/null +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/tracking/analyzing/impl/RegattaLogBoatsInLogAnalyzer.java @@ -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, EventT extends AbstractLogEvent, VisitorT> + extends BaseLogAnalyzer> { + + public RegattaLogBoatsInLogAnalyzer(LogT log) { + super(log); + } + + @Override + protected Set performAnalysis() { + Set result = new HashSet(); + for (EventT event : getLog().getUnrevokedEvents()) { + if (event instanceof RegattaLogRegisterBoatEvent) { + result.add(((RegattaLogRegisterBoatEvent) event).getBoat()); + } + } + return result; + } +} \ No newline at end of file diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/shared/analyzing/CompetitorsAndBoatsInLogAnalyzer.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/shared/analyzing/CompetitorsAndBoatsInLogAnalyzer.java new file mode 100644 index 00000000000..54a573f99dd --- /dev/null +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/shared/analyzing/CompetitorsAndBoatsInLogAnalyzer.java @@ -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, EventT extends AbstractLogEvent, VisitorT> + extends BaseLogAnalyzer> { + + public CompetitorsAndBoatsInLogAnalyzer(LogT log) { + super(log); + } + + @Override + protected Map performAnalysis() { + Map 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; + } +} \ No newline at end of file diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/shared/analyzing/CompetitorsInLogAnalyzer.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/shared/analyzing/CompetitorsInLogAnalyzer.java index 7001fda0406..43f55497b0e 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/shared/analyzing/CompetitorsInLogAnalyzer.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/shared/analyzing/CompetitorsInLogAnalyzer.java @@ -35,7 +35,7 @@ public class CompetitorsInLogAnalyzer @Override protected Set performAnalysis() { - Set result = new HashSet(); + Set result = new HashSet<>(); for (EventT event : getLog().getUnrevokedEvents()) { if (event instanceof RegisterCompetitorEvent) { diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/shared/events/RegisterCompetitorEvent.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/shared/events/RegisterCompetitorEvent.java index 9533e293da3..95897699c6c 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/shared/events/RegisterCompetitorEvent.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/shared/events/RegisterCompetitorEvent.java @@ -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 diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/Boat.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/Boat.java index f1fa13d3469..660e9d87a50 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/Boat.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/Boat.java @@ -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 { + 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); + } diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/BoatChangeListener.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/BoatChangeListener.java index cc86744e89b..6127dad6b2e 100755 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/BoatChangeListener.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/BoatChangeListener.java @@ -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); } diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/BoatFactory.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/BoatFactory.java new file mode 100644 index 00000000000..d3c43f650d6 --- /dev/null +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/BoatFactory.java @@ -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 true, + * must call {@link #getOrCreateBoat()} to cause an update of the boat's values. + */ + Boat getExistingBoatById(Serializable boatId); + + /** + * Checks if the boat 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); +} diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/Competitor.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/Competitor.java index 3f09bbafd44..d6e1069255f 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/Competitor.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/Competitor.java @@ -19,15 +19,28 @@ public interface Competitor extends NamedWithID, IsManagedByCachetrue, - * 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 competitor 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); + } diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/CompetitorStore.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/CompetitorStore.java index 9aa1019ed12..d60d4960678 100755 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/CompetitorStore.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/CompetitorStore.java @@ -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 true, - * 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 true, + * 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 * competitor'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 getCompetitors(); - - void removeCompetitor(Competitor competitor); + Iterable getAllCompetitors(); + + /** + * Obtains a non-live snapshot of the list of competitors with an assigned boat. + */ + Iterable getCompetitorsWithBoat(); + + /** + * Obtains a non-live snapshot of the list of competitors without an assigned boat. + */ + Iterable getCompetitorsWithoutBoat(); /** * Updates the competitor with {@link Competitor#getId() ID} id 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 competitors); - CompetitorDTO convertToCompetitorDTO(Competitor c); + void addNewCompetitorsWithBoat(Iterable competitorsWithBoat); + + CompetitorWithoutBoatDTO convertToCompetitorDTO(Competitor competitor); + + CompetitorDTO convertToCompetitorWithBoatDTO(Competitor c, Boat b); + + CompetitorDTO convertToCompetitorWithOptionalBoatDTO(Competitor c); + + Map convertToCompetitorAndBoatDTOs(Map 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} id 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.

+ * + * 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 true, + * 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 boat'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 getBoats(); + + /** + * Obtains a non-live snapshot of the list of all boats which are NOT embedded into a competitor + */ + Iterable 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} id 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.

+ * + * 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 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); } diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/CompetitorWithBoat.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/CompetitorWithBoat.java new file mode 100644 index 00000000000..f74ffc61b9b --- /dev/null +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/CompetitorWithBoat.java @@ -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 { +} diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/SharedDomainFactory.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/SharedDomainFactory.java index 38cc46548bc..f2c9fa6aeaf 100755 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/SharedDomainFactory.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/SharedDomainFactory.java @@ -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 threeLetterIOCCode also as the diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/WithBoat.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/WithBoat.java new file mode 100644 index 00000000000..593d883fd8b --- /dev/null +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/WithBoat.java @@ -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(); +} diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/BoatImpl.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/BoatImpl.java index a7fa6970b01..d30a984dcc3 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/BoatImpl.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/BoatImpl.java @@ -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 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(); } - + private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { ois.defaultReadObject(); listeners = new HashSet(); } + @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(listeners); } } + + @Override + public String toString() { + return getName()==null?getSailID():getName(); + } } diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/CompetitorAndBoatImpl.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/CompetitorAndBoatImpl.java new file mode 100644 index 00000000000..287a0f182ef --- /dev/null +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/CompetitorAndBoatImpl.java @@ -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; + } + +} diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/CompetitorImpl.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/CompetitorImpl.java index eb9ecd43343..8f4876ebd31 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/CompetitorImpl.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/CompetitorImpl.java @@ -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 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); } diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/CompetitorWithBoatImpl.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/CompetitorWithBoatImpl.java new file mode 100644 index 00000000000..284cf5fa6c9 --- /dev/null +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/CompetitorWithBoatImpl.java @@ -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(); + } +} diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/DynamicBoat.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/DynamicBoat.java index b3131fc9e4c..68a3dbee886 100755 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/DynamicBoat.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/DynamicBoat.java @@ -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); } diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/DynamicCompetitor.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/DynamicCompetitor.java index 234a65c5f43..9d985153646 100755 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/DynamicCompetitor.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/DynamicCompetitor.java @@ -8,14 +8,14 @@ import com.sap.sse.common.Duration; import com.sap.sse.common.Renamable; public interface DynamicCompetitor extends Competitor, Renamable { - DynamicBoat getBoat(); - DynamicTeam getTeam(); void setColor(Color displayColor); void setEmail(String email); + void setShortName(String shortName); + void setSearchTag(String searchTag); void setFlagImage(URI flagImage); diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/DynamicCompetitorWithBoat.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/DynamicCompetitorWithBoat.java new file mode 100644 index 00000000000..d2fdf61cebf --- /dev/null +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/DynamicCompetitorWithBoat.java @@ -0,0 +1,12 @@ +package com.sap.sailing.domain.base.impl; + +import com.sap.sailing.domain.base.CompetitorWithBoat; + +public interface DynamicCompetitorWithBoat extends DynamicCompetitor, CompetitorWithBoat { + DynamicBoat getBoat(); + + /** Sets the boat to null + * Should only used only for migration purposes when + */ + void clearBoat(); +} diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/SharedDomainFactoryImpl.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/SharedDomainFactoryImpl.java index c535db99ab9..44bfc9a9b32 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/SharedDomainFactoryImpl.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/SharedDomainFactoryImpl.java @@ -16,9 +16,11 @@ import java.util.logging.Level; import java.util.logging.Logger; import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver; +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.CompetitorStore; +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; @@ -64,7 +66,7 @@ public class SharedDomainFactoryImpl implements SharedDomainFactory { private final Map boatClassCache; - protected final CompetitorStore competitorStore; + protected final CompetitorStore competitorAndBoatStore; private final Map courseAreaCache; @@ -126,7 +128,7 @@ public class SharedDomainFactoryImpl implements SharedDomainFactory { controlPointWithTwoMarksCache = new HashMap(); controlPointWithTwoMarksIdCache = new HashMap(); boatClassCache = new HashMap(); - this.competitorStore = competitorStore; + this.competitorAndBoatStore = competitorStore; waypointCache = new ConcurrentHashMap(); // FIXME ass also bug 3347: mapping to lower case should rather work through a common unification / canonicalization of boat class names mayStartWithNoUpwindLeg = Collections.singleton(BoatClassMasterdata.unifyBoatClassName(BoatClassMasterdata.EXTREME_40.getDisplayName())); @@ -332,7 +334,7 @@ public class SharedDomainFactoryImpl implements SharedDomainFactory { @Override public CompetitorStore getCompetitorStore() { - return competitorStore; + return competitorAndBoatStore; } @Override @@ -341,19 +343,50 @@ public class SharedDomainFactoryImpl implements SharedDomainFactory { } @Override - public boolean isCompetitorToUpdateDuringGetOrCreate(Competitor result) { - return getCompetitorStore().isCompetitorToUpdateDuringGetOrCreate(result); + public CompetitorWithBoat getExistingCompetitorWithBoatById(Serializable competitorId) { + return getCompetitorStore().getExistingCompetitorWithBoatById(competitorId); } @Override - public Competitor getOrCreateCompetitor(Serializable competitorId, String name, Color displayColor, String email, - URI flagImage, DynamicTeam team, DynamicBoat boat, Double timeOnTimeFactor, + public boolean isCompetitorToUpdateDuringGetOrCreate(Competitor competitor) { + return getCompetitorStore().isCompetitorToUpdateDuringGetOrCreate(competitor); + } + + @Override + public Competitor getOrCreateCompetitor(Serializable competitorId, String name, String shortname, Color displayColor, String email, + URI flagImage, DynamicTeam team, Double timeOnTimeFactor, Duration timeOnDistanceAllowancePerNauticalMile, String searchTag) { if (logger.isLoggable(Level.FINEST)) { logger.log(Level.FINEST, "getting or creating competitor "+name+" with ID "+competitorId+" in domain factory "+this); } - return getCompetitorStore().getOrCreateCompetitor(competitorId, name, displayColor, email, flagImage, team, - boat, timeOnTimeFactor, timeOnDistanceAllowancePerNauticalMile, searchTag); + return getCompetitorStore().getOrCreateCompetitor(competitorId, name, shortname, displayColor, email, flagImage, team, + timeOnTimeFactor, timeOnDistanceAllowancePerNauticalMile, searchTag); + } + + @Override + public CompetitorWithBoat getOrCreateCompetitorWithBoat(Serializable competitorId, String name, String shortName, + Color displayColor, String email, URI flagImageURI, DynamicTeam team, Double timeOnTimeFactor, + Duration timeOnDistanceAllowancePerNauticalMile, String searchTag, DynamicBoat boat) { + if (logger.isLoggable(Level.FINEST)) { + logger.log(Level.FINEST, "getting or creating competitor "+name+" with ID "+competitorId+" in domain factory "+this); + } + return getCompetitorStore().getOrCreateCompetitorWithBoat(competitorId, name, shortName, displayColor, email, flagImageURI, team, + timeOnTimeFactor, timeOnDistanceAllowancePerNauticalMile, searchTag, boat); + } + + @Override + public Boat getExistingBoatById(Serializable boatId) { + return getCompetitorStore().getExistingBoatById(boatId); + } + + @Override + public boolean isBoatToUpdateDuringGetOrCreate(Boat boat) { + return getCompetitorStore().isBoatToUpdateDuringGetOrCreate(boat); + } + + @Override + public Boat getOrCreateBoat(Serializable id, String name, BoatClass boatClass, String sailId, Color color) { + return getCompetitorStore().getOrCreateBoat(id, name, boatClass, sailId, color); } @Override diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/TransientCompetitorStoreImpl.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/TransientCompetitorStoreImpl.java index 23cfe35674d..05f4efae4ec 100755 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/TransientCompetitorStoreImpl.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/TransientCompetitorStoreImpl.java @@ -8,19 +8,26 @@ import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Set; import java.util.WeakHashMap; import java.util.logging.Level; import java.util.logging.Logger; +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.CompetitorStore; +import com.sap.sailing.domain.base.CompetitorWithBoat; import com.sap.sailing.domain.base.Nationality; import com.sap.sailing.domain.common.dto.BoatClassDTO; import com.sap.sailing.domain.common.dto.BoatDTO; import com.sap.sailing.domain.common.dto.CompetitorDTO; import com.sap.sailing.domain.common.dto.CompetitorDTOImpl; +import com.sap.sailing.domain.common.dto.CompetitorWithoutBoatDTO; +import com.sap.sailing.domain.common.dto.CompetitorWithoutBoatDTOImpl; import com.sap.sse.common.Color; import com.sap.sse.common.CountryCode; import com.sap.sse.common.Duration; @@ -30,18 +37,26 @@ import com.sap.sse.concurrent.NamedReentrantReadWriteLock; public class TransientCompetitorStoreImpl implements CompetitorStore, Serializable { private static final Logger logger = Logger.getLogger(TransientCompetitorStoreImpl.class.getName()); private static final long serialVersionUID = -4198298775476586931L; + private final Map competitorCache; private final Map competitorsByIdAsString; - private transient Set listeners; + private transient Set competitorUpdateListeners; + + private final Map boatCache; + private final Map boatsByIdAsString; + private transient Set boatUpdateListeners; /** * The competitors contained in this map will have their changeable properties - * {@link #updateCompetitor(String, String, String, Nationality) updated} upon the next call to - * {@link #getOrCreateCompetitor(Serializable, String, DynamicTeam, DynamicBoat)} for their ID. + * {@link #updateCompetitor() updated} upon the next call to {@link #getOrCreateCompetitor()} for their ID. */ private final Set competitorsToUpdateDuringGetOrCreate; - private transient WeakHashMap weakCompetitorDTOCache; + private transient WeakHashMap weakCompetitorDTOCache; + + private final Set boatsToUpdateDuringGetOrCreate; + + private transient WeakHashMap weakBoatDTOCache; private final NamedReentrantReadWriteLock lock; @@ -50,29 +65,42 @@ public class TransientCompetitorStoreImpl implements CompetitorStore, Serializab competitorCache = new HashMap(); competitorsByIdAsString = new HashMap(); competitorsToUpdateDuringGetOrCreate = new HashSet(); - weakCompetitorDTOCache = new WeakHashMap(); - listeners = Collections.synchronizedSet(new HashSet()); + weakCompetitorDTOCache = new WeakHashMap(); + competitorUpdateListeners = Collections.synchronizedSet(new HashSet()); + boatCache = new HashMap(); + boatsByIdAsString = new HashMap(); + boatsToUpdateDuringGetOrCreate = new HashSet(); + weakBoatDTOCache = new WeakHashMap(); + boatUpdateListeners = Collections.synchronizedSet(new HashSet()); } private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException { ois.defaultReadObject(); - weakCompetitorDTOCache = new WeakHashMap(); - listeners = Collections.synchronizedSet(new HashSet()); + weakCompetitorDTOCache = new WeakHashMap(); + competitorUpdateListeners = Collections.synchronizedSet(new HashSet()); + weakBoatDTOCache = new WeakHashMap(); + boatUpdateListeners = Collections.synchronizedSet(new HashSet()); } - + + @Override + public void clear() { + clearCompetitors(); + clearBoats(); + } + @Override public void addCompetitorUpdateListener(CompetitorUpdateListener listener) { - listeners.add(listener); + competitorUpdateListeners.add(listener); } @Override public void removeCompetitorUpdateListener(CompetitorUpdateListener listener) { - listeners.remove(listener); + competitorUpdateListeners.remove(listener); } - private Competitor createCompetitor(Serializable id, String name, Color displayColor, String email, URI flagImage, - DynamicTeam team, DynamicBoat boat, Double timeOnTimeFactor, Duration timeOnDistanceAllowancePerNauticalMile, String searchTag) { - Competitor result = new CompetitorImpl(id, name, displayColor, email, flagImage, team, boat, + private Competitor createCompetitor(Serializable id, String name, String shortName, Color displayColor, String email, URI flagImage, + DynamicTeam team, Double timeOnTimeFactor, Duration timeOnDistanceAllowancePerNauticalMile, String searchTag) { + Competitor result = new CompetitorImpl(id, name, shortName, displayColor, email, flagImage, team, timeOnTimeFactor, timeOnDistanceAllowancePerNauticalMile, searchTag); addNewCompetitor(result); if (logger.isLoggable(Level.FINEST)) { @@ -81,29 +109,55 @@ public class TransientCompetitorStoreImpl implements CompetitorStore, Serializab return result; } + @Override + public synchronized Competitor migrateToCompetitorWithoutBoat(CompetitorWithBoat competitorWithBoat) { + // It should not be possible to call this for a competitor which has already been migrated. + assert competitorWithBoat.hasBoat(); + // We can't create a new competitor without boat here as the existing competitorWithBoat might be already referenced. + // Therefore we only clear the 'boat' property. + removeBoat(competitorWithBoat.getBoat()); + ((DynamicCompetitorWithBoat) competitorWithBoat).clearBoat(); + return competitorWithBoat; + } + /** * Adds the competitor to this transient competitor collection so that it is available in * {@link #getExistingCompetitorById(Serializable)}. Subclasses may override in case they need to take additional * measures such as durably storing the competitor. Overriding implementations must call this implementation. + *

+ * + * If a competitor with an ID equal to that of {@code competitor} already exists in this store, an + * {@link IllegalArgumentException} will be thrown. This is necessary because there is a rule in place saying each + * competitor entity can be represented in the scope of the VM by at most one Java {@link Competitor} object, and + * replacing an object in this store would leave us with at least two. */ protected void addNewCompetitor(Competitor competitor) { LockUtil.lockForWrite(lock); try { - competitorCache.put(competitor.getId(), competitor); + final Competitor existingCompetitorWithEqualId = competitorCache.put(competitor.getId(), competitor); + if (existingCompetitorWithEqualId != null && existingCompetitorWithEqualId != competitor) { + final String msg = "Replaced competitor "+existingCompetitorWithEqualId+" with ID "+ + existingCompetitorWithEqualId.getId()+" of type "+existingCompetitorWithEqualId.getClass().getName()+ + " by another object "+competitor+" of type "+ + competitor.getClass().getName()+ + " that has an equal ID. This is a pretty bad thing because we expect each competitor to be represented by exactly one Java object."; + logger.severe(msg); + throw new IllegalArgumentException(msg); + } competitorsByIdAsString.put(competitor.getId().toString(), competitor); } finally { LockUtil.unlockAfterWrite(lock); } - synchronized (listeners) { - for (final CompetitorUpdateListener listener : listeners) { + synchronized (competitorUpdateListeners) { + for (final CompetitorUpdateListener listener : competitorUpdateListeners) { listener.competitorCreated(competitor); } } } @Override - public Competitor getOrCreateCompetitor(Serializable competitorId, String name, Color displayColor, String email, - URI flagImage, DynamicTeam team, DynamicBoat boat, Double timeOnTimeFactor, + public Competitor getOrCreateCompetitor(Serializable competitorId, String name, String shortName, Color displayColor, String email, + URI flagImage, DynamicTeam team, Double timeOnTimeFactor, Duration timeOnDistanceAllowancePerNauticalMile, String searchTag) { Competitor result = getExistingCompetitorById(competitorId); // avoid synchronization for successful read access if (result == null) { @@ -111,34 +165,35 @@ public class TransientCompetitorStoreImpl implements CompetitorStore, Serializab try { result = getExistingCompetitorById(competitorId); // try again, now while holding the write lock if (result == null) { - result = createCompetitor(competitorId, name, displayColor, email, flagImage, team, boat, + result = createCompetitor(competitorId, name, shortName, displayColor, email, flagImage, team, timeOnTimeFactor, timeOnDistanceAllowancePerNauticalMile, searchTag); } } finally { LockUtil.unlockAfterWrite(lock); } } else if (isCompetitorToUpdateDuringGetOrCreate(result)) { - updateCompetitor(result.getId().toString(), name, displayColor, email, boat.getSailID(), team.getNationality(), + updateCompetitor(result.getId().toString(), name, shortName, displayColor, email, team.getNationality(), team.getImage(), flagImage, timeOnTimeFactor, timeOnDistanceAllowancePerNauticalMile, searchTag); competitorNoLongerToUpdateDuringGetOrCreate(result); } return result; } - private void competitorNoLongerToUpdateDuringGetOrCreate(Competitor result) { - competitorsToUpdateDuringGetOrCreate.remove(result); + private void competitorNoLongerToUpdateDuringGetOrCreate(Competitor competitor) { + competitorsToUpdateDuringGetOrCreate.remove(competitor); } @Override - public boolean isCompetitorToUpdateDuringGetOrCreate(Competitor result) { - return competitorsToUpdateDuringGetOrCreate.contains(result); + public boolean isCompetitorToUpdateDuringGetOrCreate(Competitor competitor) { + return competitorsToUpdateDuringGetOrCreate.contains(competitor); } @Override public Competitor getExistingCompetitorById(Serializable competitorId) { LockUtil.lockForRead(lock); try { - return competitorCache.get(competitorId); + Competitor competitor = competitorCache.get(competitorId); + return competitor; } finally { LockUtil.unlockAfterRead(lock); } @@ -154,8 +209,45 @@ public class TransientCompetitorStoreImpl implements CompetitorStore, Serializab } } + protected boolean isValidCompetitorWithBoat(Competitor competitor) { + assert competitor != null; + return competitor.hasBoat(); + } + @Override - public int size() { + public CompetitorWithBoat getExistingCompetitorWithBoatById(Serializable competitorId) { + final CompetitorWithBoat result; + LockUtil.lockForRead(lock); + try { + Competitor competitor = competitorCache.get(competitorId); + if (competitor != null && isValidCompetitorWithBoat(competitor)) { + result = (CompetitorWithBoat) competitor; + } else { + result = null; + } + return result; + } finally { + LockUtil.unlockAfterRead(lock); + } + } + + @Override + public CompetitorWithBoat getExistingCompetitorWithBoatByIdAsString(String competitorIdAsString) { + LockUtil.lockForRead(lock); + try { + Competitor competitor = competitorsByIdAsString.get(competitorIdAsString); + if (competitor != null && isValidCompetitorWithBoat(competitor)) { + return (CompetitorWithBoat) competitor; + } else { + return null; + } + } finally { + LockUtil.unlockAfterRead(lock); + } + } + + @Override + public int getCompetitorsCount() { LockUtil.lockForRead(lock); try { return competitorCache.size(); @@ -165,7 +257,7 @@ public class TransientCompetitorStoreImpl implements CompetitorStore, Serializab } @Override - public void clear() { + public void clearCompetitors() { LockUtil.lockForWrite(lock); try { competitorCache.clear(); @@ -180,7 +272,7 @@ public class TransientCompetitorStoreImpl implements CompetitorStore, Serializab } @Override - public Iterable getCompetitors() { + public Iterable getAllCompetitors() { LockUtil.lockForRead(lock); try { return new ArrayList(competitorCache.values()); @@ -190,7 +282,38 @@ public class TransientCompetitorStoreImpl implements CompetitorStore, Serializab } @Override - public void removeCompetitor(Competitor competitor) { + public Iterable getCompetitorsWithBoat() { + LockUtil.lockForRead(lock); + try { + List competitors = new ArrayList<>(); + for (Competitor c: competitorCache.values()) { + if (c.hasBoat()) { + competitors.add((CompetitorWithBoat) c); + } + } + return competitors; + } finally { + LockUtil.unlockAfterRead(lock); + } + } + + @Override + public Iterable getCompetitorsWithoutBoat() { + LockUtil.lockForRead(lock); + try { + List competitors = new ArrayList<>(); + for (Competitor c: competitorCache.values()) { + if (!c.hasBoat()) { + competitors.add(c); + } + } + return competitors; + } finally { + LockUtil.unlockAfterRead(lock); + } + } + + protected void removeCompetitor(Competitor competitor) { LockUtil.lockForWrite(lock); try { logger.fine("removing competitor "+competitor+" from competitor store "+this); @@ -203,18 +326,18 @@ public class TransientCompetitorStoreImpl implements CompetitorStore, Serializab } @Override - public Competitor updateCompetitor(String idAsString, String newName, Color newDisplayColor, String newEmail, - String newSailId, Nationality newNationality, URI newTeamImageUri, URI newFlagImageUri, + public Competitor updateCompetitor(String idAsString, String newName, String newShortName, Color newDisplayColor, String newEmail, + Nationality newNationality, URI newTeamImageUri, URI newFlagImageUri, Double timeOnTimeFactor, Duration timeOnDistanceAllowancePerNauticalMile, String newSearchTag) { DynamicCompetitor competitor = (DynamicCompetitor) getExistingCompetitorByIdAsString(idAsString); if (competitor != null) { LockUtil.lockForWrite(lock); try { competitor.setName(newName); + competitor.setShortName(newShortName); competitor.setColor(newDisplayColor); competitor.setEmail(newEmail); competitor.setFlagImage(newFlagImageUri); - competitor.getBoat().setSailId(newSailId); competitor.getTeam().setNationality(newNationality); competitor.getTeam().setImage(newTeamImageUri); competitor.setTimeOnTimeFactor(timeOnTimeFactor); @@ -225,8 +348,8 @@ public class TransientCompetitorStoreImpl implements CompetitorStore, Serializab LockUtil.unlockAfterWrite(lock); } } - synchronized (listeners) { - for (CompetitorUpdateListener listener : listeners) { + synchronized (competitorUpdateListeners) { + for (CompetitorUpdateListener listener : competitorUpdateListeners) { listener.competitorUpdated(competitor); } } @@ -234,11 +357,11 @@ public class TransientCompetitorStoreImpl implements CompetitorStore, Serializab } @Override - public CompetitorDTO convertToCompetitorDTO(Competitor c) { + public CompetitorWithoutBoatDTO convertToCompetitorDTO(Competitor c) { LockUtil.lockForRead(lock); boolean needToUnlockReadLock = true; try { - CompetitorDTO competitorDTO = weakCompetitorDTOCache.get(c); + CompetitorWithoutBoatDTO competitorDTO = weakCompetitorDTOCache.get(c); if (competitorDTO == null) { LockUtil.unlockAfterRead(lock); needToUnlockReadLock = false; @@ -247,16 +370,12 @@ public class TransientCompetitorStoreImpl implements CompetitorStore, Serializab if (competitorDTO == null) { final Nationality nationality = c.getTeam().getNationality(); CountryCode countryCode = nationality == null ? null : nationality.getCountryCode(); - competitorDTO = new CompetitorDTOImpl(c.getName(), c.getColor(), c.getEmail(), countryCode == null ? "" + competitorDTO = new CompetitorWithoutBoatDTOImpl(c.getName(), c.getShortName(), c.getColor(), c.getEmail(), countryCode == null ? "" : countryCode.getTwoLetterISOCode(), countryCode == null ? "" : countryCode.getThreeLetterIOCCode(), countryCode == null ? "" : countryCode.getName(), c.getId().toString(), c.getTeam().getImage() == null ? null : c.getTeam().getImage().toString(), c.getFlagImage() == null ? null : c.getFlagImage().toString(), - new BoatDTO(c.getBoat().getName(), c.getBoat().getSailID(), c.getBoat().getColor()), - new BoatClassDTO(c.getBoat().getBoatClass() - .getName(), c.getBoat().getBoatClass().getDisplayName(), c.getBoat().getBoatClass().getHullLength(), - c.getBoat().getBoatClass().getHullBeam()), c.getTimeOnTimeFactor(), c.getTimeOnDistanceAllowancePerNauticalMile(), c.getSearchTag()); weakCompetitorDTOCache.put(c, competitorDTO); } @@ -270,7 +389,7 @@ public class TransientCompetitorStoreImpl implements CompetitorStore, Serializab } } } - + @Override public void allowCompetitorResetToDefaults(Competitor competitor) { LockUtil.lockForWrite(lock); @@ -287,4 +406,347 @@ public class TransientCompetitorStoreImpl implements CompetitorStore, Serializab addNewCompetitor(competitor); } } + + @Override + public void addNewCompetitorsWithBoat(Iterable competitors) { + for (CompetitorWithBoat competitor: competitors) { + addNewCompetitor(competitor); + addNewBoat(competitor.getBoat()); + } + } + + private CompetitorWithBoat createCompetitorWithBoat(Serializable id, String name, String shortName, Color displayColor, String email, URI flagImage, + DynamicTeam team, Double timeOnTimeFactor, Duration timeOnDistanceAllowancePerNauticalMile, String searchTag, DynamicBoat boat) { + CompetitorWithBoat competitor = new CompetitorWithBoatImpl(id, name, shortName, displayColor, email, flagImage, team, + timeOnTimeFactor, timeOnDistanceAllowancePerNauticalMile, searchTag, boat); + addNewCompetitor(competitor); + addNewBoat(boat); + if (logger.isLoggable(Level.FINEST)) { + logger.log(Level.FINEST, "Created competitor "+name+" with ID "+id, new Exception("Here is where it happened")); + } + return competitor ; + } + + @Override + public CompetitorWithBoat getOrCreateCompetitorWithBoat(Serializable competitorId, String name, String shortName, Color displayColor, String email, + URI flagImage, DynamicTeam team, Double timeOnTimeFactor, + Duration timeOnDistanceAllowancePerNauticalMile, String searchTag, DynamicBoat boat) { + CompetitorWithBoat result = null; + result = getExistingCompetitorWithBoatById(competitorId); // avoid synchronization for successful read access + if (result == null) { + LockUtil.lockForWrite(lock); + try { + result = getExistingCompetitorWithBoatById(competitorId); // try again, now while holding the write lock + if (result == null) { + result = createCompetitorWithBoat(competitorId, name, shortName, displayColor, email, flagImage, team, + timeOnTimeFactor, timeOnDistanceAllowancePerNauticalMile, searchTag, boat); + } + } finally { + LockUtil.unlockAfterWrite(lock); + } + } else if (isCompetitorToUpdateDuringGetOrCreate(result)) { + updateCompetitorWithBoat(result.getId().toString(), name, shortName, displayColor, email, team.getNationality(), + team.getImage(), flagImage, timeOnTimeFactor, timeOnDistanceAllowancePerNauticalMile, searchTag, boat); + competitorNoLongerToUpdateDuringGetOrCreate(result); + } + return result; + } + + @Override + public CompetitorWithBoat updateCompetitorWithBoat(String idAsString, String newName, String newShortName, Color newDisplayColor, String newEmail, + Nationality newNationality, URI newTeamImageUri, URI newFlagImageUri, + Double timeOnTimeFactor, Duration timeOnDistanceAllowancePerNauticalMile, String newSearchTag, DynamicBoat boat) { + DynamicCompetitorWithBoat competitor = (DynamicCompetitorWithBoat) getExistingCompetitorByIdAsString(idAsString); + if (competitor != null) { + LockUtil.lockForWrite(lock); + try { + competitor.setName(newName); + competitor.setShortName(newShortName); + competitor.setColor(newDisplayColor); + competitor.setEmail(newEmail); + competitor.setFlagImage(newFlagImageUri); + competitor.getTeam().setNationality(newNationality); + competitor.getTeam().setImage(newTeamImageUri); + competitor.setTimeOnTimeFactor(timeOnTimeFactor); + competitor.setTimeOnDistanceAllowancePerNauticalMile(timeOnDistanceAllowancePerNauticalMile); + competitor.setSearchTag(newSearchTag); + DynamicBoat boatOfCompetitor = competitor.getBoat(); + if (boatOfCompetitor != null && boat != null) { + boatOfCompetitor.setName(boat.getName()); + boatOfCompetitor.setSailId(boat.getSailID()); + boatOfCompetitor.setColor(boat.getColor()); + } + weakCompetitorDTOCache.remove(competitor); + } finally { + LockUtil.unlockAfterWrite(lock); + } + } + synchronized (competitorUpdateListeners) { + for (CompetitorUpdateListener listener : competitorUpdateListeners) { + listener.competitorUpdated(competitor); + } + } + return (CompetitorWithBoat) competitor; + } + + /** Boat stuff starts here */ + + @Override + public void addBoatUpdateListener(BoatUpdateListener listener) { + boatUpdateListeners.add(listener); + } + + @Override + public void removeBoatUpdateListener(BoatUpdateListener listener) { + boatUpdateListeners.remove(listener); + } + + private Boat createBoat(Serializable id, String name, BoatClass boatClass, String sailID, Color color) { + Boat boat = new BoatImpl(id, name, boatClass, sailID, color); + addNewBoat(boat); + if (logger.isLoggable(Level.FINEST)) { + logger.log(Level.FINEST, "Created boat "+name+" with ID "+id, new Exception("Here is where it happened")); + } + return boat; + } + + /** + * Adds the boat to this transient boat collection so that it is available in + * {@link #getExistingBoatById(Serializable)}. Subclasses may override in case they need to take additional + * measures such as durably storing the boat. Overriding implementations must call this implementation. + */ + protected void addNewBoat(Boat boat) { + LockUtil.lockForWrite(lock); + try { + boatCache.put(boat.getId(), boat); + boatsByIdAsString.put(boat.getId().toString(), boat); + } finally { + LockUtil.unlockAfterWrite(lock); + } + synchronized (boatUpdateListeners) { + for (final BoatUpdateListener listener : boatUpdateListeners) { + listener.boatCreated(boat); + } + } + } + + @Override + public Boat getOrCreateBoat(Serializable id, String name, BoatClass boatClass, String sailId, Color color) { + Boat result = getExistingBoatById(id); // avoid synchronization for successful read access + if (result == null) { + LockUtil.lockForWrite(lock); + try { + result = getExistingBoatById(id); // try again, now while holding the write lock + if (result == null) { + result = createBoat(id, name, boatClass, sailId, color); + } + } finally { + LockUtil.unlockAfterWrite(lock); + } + } else if (isBoatToUpdateDuringGetOrCreate(result)) { + updateBoat(result.getId().toString(), name, color, sailId); + boatNoLongerToUpdateDuringGetOrCreate(result); + } + return result; + } + + private void boatNoLongerToUpdateDuringGetOrCreate(Boat boat) { + boatsToUpdateDuringGetOrCreate.remove(boat); + } + + @Override + public boolean isBoatToUpdateDuringGetOrCreate(Boat boat) { + return boatsToUpdateDuringGetOrCreate.contains(boat); + } + + @Override + public Boat getExistingBoatById(Serializable boatId) { + LockUtil.lockForRead(lock); + try { + return boatCache.get(boatId); + } finally { + LockUtil.unlockAfterRead(lock); + } + } + + @Override + public Boat getExistingBoatByIdAsString(String boatIdAsString) { + LockUtil.lockForRead(lock); + try { + return boatsByIdAsString.get(boatIdAsString); + } finally { + LockUtil.unlockAfterRead(lock); + } + } + + @Override + public int getBoatsCount() { + LockUtil.lockForRead(lock); + try { + return boatCache.size(); + } finally { + LockUtil.unlockAfterRead(lock); + } + } + + @Override + public void clearBoats() { + LockUtil.lockForWrite(lock); + try { + boatCache.clear(); + boatsByIdAsString.clear(); + boatsToUpdateDuringGetOrCreate.clear(); + if (logger.isLoggable(Level.FINEST)) { + logger.log(Level.FINEST, "Clearing boat store "+this, new Exception("here is where it happened")); + } + } finally { + LockUtil.unlockAfterWrite(lock); + } + } + + @Override + public Iterable getBoats() { + LockUtil.lockForRead(lock); + try { + return new ArrayList(boatCache.values()); + } finally { + LockUtil.unlockAfterRead(lock); + } + } + + @Override + public Iterable getStandaloneBoats() { + LockUtil.lockForRead(lock); + try { + Set boats = new HashSet<>(boatCache.values()); + Set boatsEmbeddedInCompetitors = new HashSet<>(); + for (Competitor competitor : competitorCache.values()) { + if (isValidCompetitorWithBoat(competitor)) { + boatsEmbeddedInCompetitors.add(((CompetitorWithBoat) competitor).getBoat()); + } + } + boats.removeAll(boatsEmbeddedInCompetitors); + return boats; + } finally { + LockUtil.unlockAfterRead(lock); + } + } + + protected void removeBoat(Boat boat) { + LockUtil.lockForWrite(lock); + try { + logger.fine("Removing boat "+boat+" from boat store "+this); + boatCache.remove(boat.getId()); + boatsByIdAsString.remove(boat.getId().toString()); + weakBoatDTOCache.remove(boat); + } finally { + LockUtil.unlockAfterWrite(lock); + } + } + + @Override + public Boat updateBoat(String idAsString, String newName, Color newColor, String newSailId) { + DynamicBoat boat = (DynamicBoat) getExistingBoatByIdAsString(idAsString); + if (boat != null) { + LockUtil.lockForWrite(lock); + try { + boat.setName(newName); + boat.setSailId(newSailId); + boat.setColor(newColor); + weakBoatDTOCache.remove(boat); + } finally { + LockUtil.unlockAfterWrite(lock); + } + } + synchronized (boatUpdateListeners) { + for (BoatUpdateListener listener : boatUpdateListeners) { + listener.boatUpdated(boat); + } + } + return boat; + } + + @Override + public BoatDTO convertToBoatDTO(Boat b) { + LockUtil.lockForRead(lock); + boolean needToUnlockReadLock = true; + try { + BoatDTO boatDTO = weakBoatDTOCache.get(b); + if (boatDTO == null) { + LockUtil.unlockAfterRead(lock); + needToUnlockReadLock = false; + LockUtil.lockForWrite(lock); + boatDTO = weakBoatDTOCache.get(b); + if (boatDTO == null) { + BoatClassDTO boatClassDTO = new BoatClassDTO(b.getBoatClass().getName(), + b.getBoatClass().getDisplayName(), b.getBoatClass().getHullLength(), b.getBoatClass().getHullBeam()); + boatDTO = new BoatDTO(b.getId().toString(), b.getName(), boatClassDTO, b.getSailID(), b.getColor()); + weakBoatDTOCache.put(b, boatDTO); + } + } + return boatDTO; + } finally { + if (needToUnlockReadLock) { + LockUtil.unlockAfterRead(lock); + } else { + LockUtil.unlockAfterWrite(lock); + } + } + } + + @Override + public void allowBoatResetToDefaults(Boat boat) { + LockUtil.lockForWrite(lock); + try { + boatsToUpdateDuringGetOrCreate.add(boat); + } finally { + LockUtil.unlockAfterWrite(lock); + } + } + + @Override + public void addNewBoats(Iterable boats) { + LockUtil.lockForWrite(lock); + try { + for (Boat boat: boats) { + boatCache.put(boat.getId(), boat); + boatsByIdAsString.put(boat.getId().toString(), boat); + } + } finally { + LockUtil.unlockAfterWrite(lock); + } + } + + @Override + public CompetitorDTO convertToCompetitorWithBoatDTO(Competitor competitor, Boat boat) { + CompetitorWithoutBoatDTO c = convertToCompetitorDTO(competitor); + BoatDTO boatDTO = null; + if (boat != null) { + boatDTO = convertToBoatDTO(boat); + } + CompetitorDTO competitorDTO = new CompetitorDTOImpl(c, boatDTO); + + return competitorDTO; + } + + @Override + public CompetitorDTO convertToCompetitorWithOptionalBoatDTO(Competitor competitor) { + if (isValidCompetitorWithBoat(competitor)) { + return convertToCompetitorWithBoatDTO(competitor, ((CompetitorWithBoat) competitor).getBoat()); + } else { + return convertToCompetitorWithBoatDTO(competitor, null); + } + } + + @Override + public Map convertToCompetitorAndBoatDTOs(Map competitorsAndBoats) { + Map result = new HashMap<>(); + for (Entry entry: competitorsAndBoats.entrySet()) { + CompetitorDTO competitorDTO = convertToCompetitorWithOptionalBoatDTO(entry.getKey()); + BoatDTO boatDTO = convertToBoatDTO(entry.getValue()); + result.put(competitorDTO, boatDTO); + } + return result; + } + } diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/racegroup/RaceGroup.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/racegroup/RaceGroup.java index f8cbb0de031..e274d79a78e 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/racegroup/RaceGroup.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/racegroup/RaceGroup.java @@ -41,6 +41,12 @@ public interface RaceGroup extends Named { */ public BoatClass getBoatClass(); + /** + * Indicates whether the competitors use the same boat for the whole regatta or change the boat used during the competition + * @return true when the competitors change their boats, false otherwise + */ + public boolean canBoatsOfCompetitorsChangePerRace(); + /** * Gets the collection of series, for a {@link RaceGroup} representing a regatta in the same order as * the {@code Regatta.getSeries()} call returns them. diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/racegroup/impl/RaceGroupImpl.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/racegroup/impl/RaceGroupImpl.java index d77453bd073..55399b6f23f 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/racegroup/impl/RaceGroupImpl.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/racegroup/impl/RaceGroupImpl.java @@ -16,17 +16,19 @@ public class RaceGroupImpl extends NamedImpl implements RaceGroup { private final CourseArea courseArea; private final Iterable series; private final RegattaConfiguration regattaConfiguration; + private final boolean canBoatsOfCompetitorsChangePerRace; /** * @param series * the series; for a regatta they are expected to be in the same order as {@code Regatta.getSeries()} * would deliver them. */ - public RaceGroupImpl(String name, String displayName, BoatClass boatClass, CourseArea courseArea, + public RaceGroupImpl(String name, String displayName, BoatClass boatClass, boolean canBoatsOfCompetitorsChangePerRace, CourseArea courseArea, Iterable series, RegattaConfiguration regattaConfiguration) { super(name); this.displayName = displayName; this.boatClass = boatClass; + this.canBoatsOfCompetitorsChangePerRace = canBoatsOfCompetitorsChangePerRace; this.courseArea = courseArea; this.series = series; this.regattaConfiguration = regattaConfiguration; @@ -47,6 +49,11 @@ public class RaceGroupImpl extends NamedImpl implements RaceGroup { return series; } + @Override + public boolean canBoatsOfCompetitorsChangePerRace() { + return canBoatsOfCompetitorsChangePerRace; + } + @Override public BoatClass getBoatClass() { return boatClass; diff --git a/java/com.sap.sailing.domain.swisstimingadapter.test/src/com/sap/sailing/domain/swisstimingadapter/test/SimpleDomainFactoryTest.java b/java/com.sap.sailing.domain.swisstimingadapter.test/src/com/sap/sailing/domain/swisstimingadapter/test/SimpleDomainFactoryTest.java index eadfb9b4093..cf9771ddb6a 100755 --- a/java/com.sap.sailing.domain.swisstimingadapter.test/src/com/sap/sailing/domain/swisstimingadapter/test/SimpleDomainFactoryTest.java +++ b/java/com.sap.sailing.domain.swisstimingadapter.test/src/com/sap/sailing/domain/swisstimingadapter/test/SimpleDomainFactoryTest.java @@ -109,7 +109,8 @@ public class SimpleDomainFactoryTest { public void testCourseConfigForMark() throws PatchFailedException { DomainFactory domainFactory = DomainFactory.INSTANCE; Regatta regatta = new RegattaImpl(EmptyRaceLogStore.INSTANCE, EmptyRegattaLogStore.INSTANCE, "TestEvent", - /* boatClass */ null, /*startDate*/ null, /*endDate*/ null, new RacingEventServiceImpl(), + /* boatClass */ null, /* canBoatsOfCompetitorsChangePerRace */ true, + /*startDate*/ null, /*endDate*/ null, new RacingEventServiceImpl(), com.sap.sailing.domain.base.DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), "123", null); Race race = new RaceImpl("1234", "R1", "Race 1234"); Iterable competitors = Collections.emptyList(); @@ -135,7 +136,8 @@ public class SimpleDomainFactoryTest { public void testCourseConfigForGate() throws PatchFailedException { DomainFactory domainFactory = DomainFactory.INSTANCE; Regatta regatta = new RegattaImpl(EmptyRaceLogStore.INSTANCE, EmptyRegattaLogStore.INSTANCE, - "TestEvent", /* boatClass */ null, /*startDate*/ null, /*endDate*/ null, new RacingEventServiceImpl(), + "TestEvent", /* boatClass */ null, /* canBoatsOfCompetitorsChangePerRace */ true, + /*startDate*/ null, /*endDate*/ null, new RacingEventServiceImpl(), com.sap.sailing.domain.base.DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), "123", null); Race race = new RaceImpl("1234", "R1", "Race 1234"); Iterable competitors = Collections.emptyList(); diff --git a/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/DomainFactory.java b/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/DomainFactory.java index 0d0869b12b1..bd8494217ce 100755 --- a/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/DomainFactory.java +++ b/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/DomainFactory.java @@ -1,7 +1,9 @@ package com.sap.sailing.domain.swisstimingadapter; import java.util.List; +import java.util.Map; +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; @@ -19,6 +21,7 @@ import com.sap.sailing.domain.tracking.MarkPassing; import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParameters; import com.sap.sailing.domain.tracking.TrackedRegattaRegistry; import com.sap.sse.common.TimePoint; +import com.sap.sse.common.Util.Pair; import difflib.PatchFailedException; @@ -39,11 +42,11 @@ public interface DomainFactory { Nationality getOrCreateNationality(String threeLetterIOCCode); - Competitor createCompetitorWithoutID(com.sap.sailing.domain.swisstimingadapter.Competitor competitor, String raceId, BoatClass boatClass); + Pair createCompetitorWithoutID(com.sap.sailing.domain.swisstimingadapter.Competitor competitor, String raceId, BoatClass boatClass); - Competitor createCompetitorWithID(com.sap.sailing.domain.swisstimingadapter.Competitor competitor, BoatClass boatClass); + Pair createCompetitorWithID(com.sap.sailing.domain.swisstimingadapter.Competitor competitor, BoatClass boatClass); - Competitor createCompetitorWithoutID(String boatID, String threeLetterIOCCode, String name, String raceId, BoatClass boatClass); + Pair createCompetitorWithoutID(String boatID, String threeLetterIOCCode, String name, String raceId, BoatClass boatClass); String getCompetitorID(String boatID, RaceType raceType); @@ -70,6 +73,6 @@ public interface DomainFactory { ControlPoint getOrCreateControlPoint(Iterable devices, MarkType markType); - RaceDefinition createRaceDefinition(Regatta regatta, String swissTimingRaceID, Iterable competitors, + RaceDefinition createRaceDefinition(Regatta regatta, String swissTimingRaceID, Map competitorsAndBoats, List courseDefinition, String raceName, String raceIdForRaceDefinition); } diff --git a/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/DomainFactoryImpl.java b/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/DomainFactoryImpl.java index 9aef27aa605..d3a303701ca 100755 --- a/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/DomainFactoryImpl.java +++ b/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/DomainFactoryImpl.java @@ -2,18 +2,21 @@ package com.sap.sailing.domain.swisstimingadapter.impl; import java.util.ArrayList; import java.util.Calendar; -import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.UUID; import java.util.logging.Logger; +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.CompetitorStore; +import com.sap.sailing.domain.base.CompetitorWithBoat; import com.sap.sailing.domain.base.ControlPoint; import com.sap.sailing.domain.base.Nationality; import com.sap.sailing.domain.base.RaceDefinition; @@ -52,6 +55,7 @@ import com.sap.sailing.domain.tracking.TrackedRegattaRegistry; import com.sap.sse.common.Named; import com.sap.sse.common.TimePoint; import com.sap.sse.common.Util; +import com.sap.sse.common.Util.Pair; import com.sap.sse.common.WithID; import com.sap.sse.common.impl.MillisecondsTimePoint; @@ -105,7 +109,8 @@ public class DomainFactoryImpl implements DomainFactory { Calendar calendar = Calendar.getInstance(); result = new RegattaImpl(raceLogStore, regattaLogStore, RegattaImpl.getDefaultName( "ST Regatta " + calendar.get(Calendar.YEAR) + " for race " + raceID, regattaBoatClass.getName()), - regattaBoatClass, /*startDate*/ null, /*endDate*/ null, trackedRegattaRegistry, getBaseDomainFactory().createScoringScheme( + regattaBoatClass, /* canBoatsOfCompetitorsChangePerRace */ true, /*startDate*/ null, /*endDate*/ null, + trackedRegattaRegistry, getBaseDomainFactory().createScoringScheme( ScoringSchemeType.LOW_POINT), raceID, null); logger.info("Created regatta "+result.getName()+" ("+result.hashCode()+")"); raceIDToRegattaCache.put(raceID, result); @@ -114,11 +119,10 @@ public class DomainFactoryImpl implements DomainFactory { } @Override - public Competitor createCompetitorWithID(com.sap.sailing.domain.swisstimingadapter.Competitor competitor, BoatClass boatClass) { - CompetitorStore competitorStore = baseDomainFactory.getCompetitorStore(); - Competitor result = competitorStore.getExistingCompetitorByIdAsString(competitor.getID()); - if (result == null || competitorStore.isCompetitorToUpdateDuringGetOrCreate(result)) { - DynamicBoat boat = new BoatImpl(competitor.getName(), boatClass, competitor.getBoatID()); + public Pair createCompetitorWithID(com.sap.sailing.domain.swisstimingadapter.Competitor competitor, BoatClass boatClass) { + CompetitorStore competitorAndBoatStore = baseDomainFactory.getCompetitorStore(); + CompetitorWithBoat domainCompetitor = competitorAndBoatStore.getExistingCompetitorWithBoatByIdAsString(competitor.getID()); + if (domainCompetitor == null || competitorAndBoatStore.isCompetitorToUpdateDuringGetOrCreate(domainCompetitor)) { List teamMembers = new ArrayList(); for (CrewMember crewMember: competitor.getCrew()) { DynamicPerson person = new PersonImpl(crewMember.getName().trim(), getOrCreateNationality(crewMember.getNationality()), @@ -126,30 +130,32 @@ public class DomainFactoryImpl implements DomainFactory { teamMembers.add(person); } DynamicTeam team = new TeamImpl(competitor.getName(), teamMembers, /* coach */ null); - result = competitorStore.getOrCreateCompetitor(competitor.getID(), competitor.getName(), null /*displayColor*/, null /*email*/, null, team, boat, - /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null); + DynamicBoat domainBoat = new BoatImpl(competitor.getID(), null, boatClass, competitor.getBoatID(), null); + domainCompetitor = competitorAndBoatStore.getOrCreateCompetitorWithBoat(competitor.getID(), competitor.getName(), null /* shortName */, null /*displayColor*/, null /*email*/, null, team, + /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null, domainBoat); } - return result; + return new Pair(domainCompetitor, domainCompetitor.getBoat()); } @Override - public Competitor createCompetitorWithoutID(com.sap.sailing.domain.swisstimingadapter.Competitor competitor, String raceId, BoatClass boatClass) { - Competitor result = null; - DynamicBoat boat = new BoatImpl(competitor.getName(), boatClass, competitor.getBoatID()); + public Pair createCompetitorWithoutID(com.sap.sailing.domain.swisstimingadapter.Competitor competitor, String raceId, BoatClass boatClass) { + CompetitorStore competitorAndBoatStore = baseDomainFactory.getCompetitorStore(); List teamMembers = new ArrayList(); for (String teamMemberName : competitor.getName().split("[-+&]")) { teamMembers.add(new PersonImpl(teamMemberName.trim(), getOrCreateNationality(competitor.getThreeLetterIOCCode()), /* dateOfBirth */ null, teamMemberName.trim())); } DynamicTeam team = new TeamImpl(competitor.getName(), teamMembers, /* coach */ null); - result = baseDomainFactory.getCompetitorStore().getOrCreateCompetitor(getCompetitorID(competitor.getBoatID(), competitor.getName(), raceId, boatClass), - competitor.getName(), null /*displayColor*/, null /*email*/, null, team, boat, - /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null); - return result; + String competitorID = getCompetitorID(competitor.getBoatID(), competitor.getName(), raceId, boatClass); + DynamicBoat domainBoat = new BoatImpl(UUID.randomUUID(), null, boatClass, competitor.getBoatID(), null); + Competitor domainCompetitor = competitorAndBoatStore.getOrCreateCompetitorWithBoat(competitorID, + competitor.getName(), null /* short name */, null /*displayColor*/, null /*email*/, null, team, + /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null, domainBoat); + return new Pair(domainCompetitor, domainBoat); } @Override - public Competitor createCompetitorWithoutID(String boatID, String threeLetterIOCCode, String name, String raceId, BoatClass boatClass) { + public Pair createCompetitorWithoutID(String boatID, String threeLetterIOCCode, String name, String raceId, BoatClass boatClass) { return createCompetitorWithoutID(new CompetitorWithoutID(boatID, threeLetterIOCCode, name), raceId, boatClass); } @@ -185,7 +191,7 @@ public class DomainFactoryImpl implements DomainFactory { } @Override - public RaceDefinition createRaceDefinition(Regatta regatta, String swissTimingRaceID, Iterable competitors, + public RaceDefinition createRaceDefinition(Regatta regatta, String swissTimingRaceID, Map competitorsAndBoats, List courseDefinition, String raceName, String raceIdForRaceDefinition) { List waypoints = new ArrayList<>(); for (ControlPoint controlPoint : courseDefinition) { @@ -195,7 +201,7 @@ public class DomainFactoryImpl implements DomainFactory { com.sap.sailing.domain.base.Course domainCourse = new CourseImpl("Course", waypoints); BoatClass boatClass = getRaceTypeFromRaceID(swissTimingRaceID).getBoatClass(); logger.info("Creating RaceDefinitionImpl for race "+swissTimingRaceID); - RaceDefinition result = new RaceDefinitionImpl(raceName, domainCourse, boatClass, competitors, raceIdForRaceDefinition); + RaceDefinition result = new RaceDefinitionImpl(raceName, domainCourse, boatClass, competitorsAndBoats, raceIdForRaceDefinition); regatta.addRace(result); return result; } @@ -203,10 +209,10 @@ public class DomainFactoryImpl implements DomainFactory { @Override public RaceDefinition createRaceDefinition(Regatta regatta, Race race, StartList startList, Course course) { com.sap.sailing.domain.base.Course domainCourse = createCourse(race.getDescription(), course); - Iterable competitors = createCompetitorList(startList, race.getRaceID(), race.getBoatClass()); + Map competitorsAndBoats = createCompetitorsAndBoats(startList, race.getRaceID(), race.getBoatClass()); logger.info("Creating RaceDefinitionImpl for race "+race.getRaceID()); BoatClass boatClass = race.getBoatClass() != null ? race.getBoatClass() : getRaceTypeFromRaceID(race.getRaceID()).getBoatClass(); - RaceDefinition result = new RaceDefinitionImpl(race.getRaceName(), domainCourse, boatClass, competitors, /* competitorsAndTheirBoats */ Collections.emptyMap(), race.getRaceID()); + RaceDefinition result = new RaceDefinitionImpl(race.getRaceName(), domainCourse, boatClass, competitorsAndBoats, race.getRaceID()); regatta.addRace(result); return result; } @@ -256,16 +262,16 @@ public class DomainFactoryImpl implements DomainFactory { return result; } - private Iterable createCompetitorList(StartList startList, String raceId, BoatClass boatClass) { - List result = new ArrayList(); + private Map createCompetitorsAndBoats(StartList startList, String raceId, BoatClass boatClass) { + Map result = new LinkedHashMap<>(); for (com.sap.sailing.domain.swisstimingadapter.Competitor swissTimingCompetitor : startList.getCompetitors()) { - Competitor domainCompetitor; + Pair domainCompetitorAndBoat; if (swissTimingCompetitor.getID() != null) { - domainCompetitor = createCompetitorWithID(swissTimingCompetitor, boatClass); + domainCompetitorAndBoat = createCompetitorWithID(swissTimingCompetitor, boatClass); } else { - domainCompetitor = createCompetitorWithoutID(swissTimingCompetitor, raceId, boatClass); + domainCompetitorAndBoat = createCompetitorWithoutID(swissTimingCompetitor, raceId, boatClass); } - result.add(domainCompetitor); + result.put(domainCompetitorAndBoat.getA(), domainCompetitorAndBoat.getB()); } return result; } diff --git a/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/SwissTimingTrackingConnectivityParameters.java b/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/SwissTimingTrackingConnectivityParameters.java index 1ffacc6bedb..de24889e36e 100755 --- a/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/SwissTimingTrackingConnectivityParameters.java +++ b/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/SwissTimingTrackingConnectivityParameters.java @@ -4,6 +4,7 @@ package com.sap.sailing.domain.swisstimingadapter.impl; import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver; import com.sap.sailing.domain.base.BoatClass; import com.sap.sailing.domain.base.Regatta; +import com.sap.sailing.domain.leaderboard.LeaderboardGroupResolver; import com.sap.sailing.domain.racelog.RaceLogStore; import com.sap.sailing.domain.regattalog.RegattaLogStore; import com.sap.sailing.domain.swisstimingadapter.DomainFactory; @@ -58,7 +59,7 @@ public class SwissTimingTrackingConnectivityParameters extends AbstractRaceTrack @Override public RaceTracker createRaceTracker(TrackedRegattaRegistry trackedRegattaRegistry, WindStore windStore, - RaceLogResolver raceLogResolver, long timeoutInMilliseconds) throws Exception { + RaceLogResolver raceLogResolver, LeaderboardGroupResolver leaderboardGroupResolver, long timeoutInMilliseconds) throws Exception { return swissTimingFactory.createRaceTracker(raceID, raceName, raceDescription, boatClass, hostname, port, startList, delayToLiveInMillis, raceLogStore, regattaLogStore, windStore, useInternalMarkPassingAlgorithm, @@ -67,7 +68,7 @@ public class SwissTimingTrackingConnectivityParameters extends AbstractRaceTrack @Override public RaceTracker createRaceTracker(Regatta regatta, TrackedRegattaRegistry trackedRegattaRegistry, - WindStore windStore, RaceLogResolver raceLogResolver, long timeoutInMilliseconds) throws Exception { + WindStore windStore, RaceLogResolver raceLogResolver, LeaderboardGroupResolver leaderboardGroupResolver, long timeoutInMilliseconds) throws Exception { return swissTimingFactory.createRaceTracker(regatta, raceID, raceName, raceDescription, boatClass, hostname, port, startList, delayToLiveInMillis, windStore, useInternalMarkPassingAlgorithm, domainFactory, trackedRegattaRegistry, raceLogResolver, raceLogStore, regattaLogStore, this); diff --git a/java/com.sap.sailing.domain.swisstimingreplayadapter.test/src/com/sap/sailing/domain/swisstimingreplayadapter/test/SwissTimingReplayAdapterServiceTest.java b/java/com.sap.sailing.domain.swisstimingreplayadapter.test/src/com/sap/sailing/domain/swisstimingreplayadapter/test/SwissTimingReplayAdapterServiceTest.java index 63148dc1c45..2d9746526a3 100644 --- a/java/com.sap.sailing.domain.swisstimingreplayadapter.test/src/com/sap/sailing/domain/swisstimingreplayadapter/test/SwissTimingReplayAdapterServiceTest.java +++ b/java/com.sap.sailing.domain.swisstimingreplayadapter.test/src/com/sap/sailing/domain/swisstimingreplayadapter/test/SwissTimingReplayAdapterServiceTest.java @@ -1,11 +1,11 @@ package com.sap.sailing.domain.swisstimingreplayadapter.test; import static org.junit.Assert.assertArrayEquals; -import static org.mockito.Mockito.mock; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -13,11 +13,13 @@ import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.List; +import java.util.Map.Entry; import org.junit.Ignore; import org.junit.Test; import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver; +import com.sap.sailing.domain.base.Boat; import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.common.Distance; import com.sap.sailing.domain.racelog.impl.EmptyRaceLogStore; @@ -202,9 +204,9 @@ public class SwissTimingReplayAdapterServiceTest { new SwissTimingReplayParserImpl().readData(getClass().getResourceAsStream("/SAW005905.20120805.replay"), replayListener); Iterable trackedRaces = replayListener.getTrackedRaces(); TrackedRace trackedRace = trackedRaces.iterator().next(); - for (Competitor competitor : trackedRace.getRace().getCompetitors()) { - assertSame(DomainFactory.INSTANCE.getBaseDomainFactory().getOrCreateBoatClass("470"), competitor.getBoat().getBoatClass()); - Distance distanceToLineAtStart = trackedRace.getDistanceToStartLine(competitor, trackedRace.getStartOfRace()); + for (Entry competitorAndBoatEntry : trackedRace.getRace().getCompetitorsAndTheirBoats().entrySet()) { + assertSame(DomainFactory.INSTANCE.getBaseDomainFactory().getOrCreateBoatClass("470"), competitorAndBoatEntry.getValue().getBoatClass()); + Distance distanceToLineAtStart = trackedRace.getDistanceToStartLine(competitorAndBoatEntry.getKey(), trackedRace.getStartOfRace()); assertTrue(distanceToLineAtStart.getMeters() > 0); assertTrue(distanceToLineAtStart.getMeters() < 20); } diff --git a/java/com.sap.sailing.domain.swisstimingreplayadapter/src/com/sap/sailing/domain/swisstimingreplayadapter/impl/SwissTimingReplayConnectivityParameters.java b/java/com.sap.sailing.domain.swisstimingreplayadapter/src/com/sap/sailing/domain/swisstimingreplayadapter/impl/SwissTimingReplayConnectivityParameters.java index 84844f7b314..abce2aac1c0 100644 --- a/java/com.sap.sailing.domain.swisstimingreplayadapter/src/com/sap/sailing/domain/swisstimingreplayadapter/impl/SwissTimingReplayConnectivityParameters.java +++ b/java/com.sap.sailing.domain.swisstimingreplayadapter/src/com/sap/sailing/domain/swisstimingreplayadapter/impl/SwissTimingReplayConnectivityParameters.java @@ -3,6 +3,7 @@ package com.sap.sailing.domain.swisstimingreplayadapter.impl; import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver; import com.sap.sailing.domain.base.RaceDefinition; import com.sap.sailing.domain.base.Regatta; +import com.sap.sailing.domain.leaderboard.LeaderboardGroupResolver; import com.sap.sailing.domain.racelog.RaceLogStore; import com.sap.sailing.domain.regattalog.RegattaLogStore; import com.sap.sailing.domain.swisstimingadapter.DomainFactory; @@ -132,7 +133,7 @@ public class SwissTimingReplayConnectivityParameters extends AbstractRaceTrackin @Override public RaceTracker createRaceTracker(TrackedRegattaRegistry trackedRegattaRegistry, final WindStore windStore, - RaceLogResolver raceLogResolver, long timeoutInMilliseconds) throws Exception { + RaceLogResolver raceLogResolver, LeaderboardGroupResolver leaderboardGroupResolver, long timeoutInMilliseconds) throws Exception { SwissTimingReplayToDomainAdapter listener = new SwissTimingReplayToDomainAdapter(/* regatta */ null, raceName, raceID, domainFactory.getBaseDomainFactory().getOrCreateBoatClass(boatClassName), domainFactory, trackedRegattaRegistry, useInternalMarkPassingAlgorithm, raceLogResolver, raceLogStore, @@ -143,7 +144,7 @@ public class SwissTimingReplayConnectivityParameters extends AbstractRaceTrackin @Override public RaceTracker createRaceTracker(Regatta regatta, TrackedRegattaRegistry trackedRegattaRegistry, - WindStore windStore, RaceLogResolver raceLogResolver, long timeoutInMilliseconds) throws Exception { + WindStore windStore, RaceLogResolver raceLogResolver, LeaderboardGroupResolver leaderboardGroupResolver, long timeoutInMilliseconds) throws Exception { SwissTimingReplayToDomainAdapter listener = new SwissTimingReplayToDomainAdapter(regatta, raceName, raceID, domainFactory.getBaseDomainFactory().getOrCreateBoatClass(boatClassName), domainFactory, trackedRegattaRegistry, useInternalMarkPassingAlgorithm, raceLogResolver, diff --git a/java/com.sap.sailing.domain.swisstimingreplayadapter/src/com/sap/sailing/domain/swisstimingreplayadapter/impl/SwissTimingReplayToDomainAdapter.java b/java/com.sap.sailing.domain.swisstimingreplayadapter/src/com/sap/sailing/domain/swisstimingreplayadapter/impl/SwissTimingReplayToDomainAdapter.java index dee036575db..63f048a03a8 100755 --- a/java/com.sap.sailing.domain.swisstimingreplayadapter/src/com/sap/sailing/domain/swisstimingreplayadapter/impl/SwissTimingReplayToDomainAdapter.java +++ b/java/com.sap.sailing.domain.swisstimingreplayadapter/src/com/sap/sailing/domain/swisstimingreplayadapter/impl/SwissTimingReplayToDomainAdapter.java @@ -5,18 +5,17 @@ import java.util.Calendar; import java.util.Collections; import java.util.GregorianCalendar; import java.util.HashMap; -import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.NavigableSet; import java.util.Optional; -import java.util.Set; import java.util.TimeZone; import java.util.logging.Logger; import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver; +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; @@ -101,11 +100,11 @@ public class SwissTimingReplayToDomainAdapter extends SwissTimingReplayAdapter i private TimePoint referenceTimePoint; /** - * feference location for location / lat/lng specifications + * reference location for location / lat/lng specifications */ private Position referenceLocation; - private final Map> competitorsPerRaceID; + private final Map> competitorsAndBoatsPerRaceID; private final Map> marksPerRaceIDPerMarkID; @@ -203,7 +202,7 @@ public class SwissTimingReplayToDomainAdapter extends SwissTimingReplayAdapter i trackedRacePerRaceID = new HashMap<>(); bestStartTimePerRaceID = new HashMap<>(); raceTimePerRaceID = new HashMap<>(); - competitorsPerRaceID = new HashMap<>(); + competitorsAndBoatsPerRaceID = new HashMap<>(); marksPerRaceIDPerMarkID = new HashMap<>(); markByHashValue = new HashMap<>(); competitorByHashValue = new HashMap<>(); @@ -298,15 +297,15 @@ public class SwissTimingReplayToDomainAdapter extends SwissTimingReplayAdapter i CompetitorStatus competitorStatus, BoatType boatType, short cRank_Bracket, short cnPoints_x10_Bracket, short ctPoints_x10_Winner) { if (boatType == BoatType.Competitor) { - Competitor competitor = domainFactory.createCompetitorWithoutID(sailNumberOrTrackerID, threeLetterIOCCode.trim(), name.trim(), + Pair competitorAndBoat = domainFactory.createCompetitorWithoutID(sailNumberOrTrackerID, threeLetterIOCCode.trim(), name.trim(), currentRaceID, domainFactory.getRaceTypeFromRaceID(currentRaceID).getBoatClass()); - Set competitorsOfCurrentRace = competitorsPerRaceID.get(currentRaceID); - if (competitorsOfCurrentRace == null) { - competitorsOfCurrentRace = new HashSet<>(); - competitorsPerRaceID.put(currentRaceID, competitorsOfCurrentRace); + Map competitorAndBoatsOfCurrentRace = competitorsAndBoatsPerRaceID.get(currentRaceID); + if (competitorAndBoatsOfCurrentRace == null) { + competitorAndBoatsOfCurrentRace = new HashMap<>(); + competitorsAndBoatsPerRaceID.put(currentRaceID, competitorAndBoatsOfCurrentRace); } - competitorsOfCurrentRace.add(competitor); - competitorByHashValue.put(hashValue, competitor); + competitorAndBoatsOfCurrentRace.put(competitorAndBoat.getA(), competitorAndBoat.getB()); + competitorByHashValue.put(hashValue, competitorAndBoat.getA()); } else { // consider it a mark Mark mark = domainFactory.getOrCreateMark(sailNumberOrTrackerID.trim()); @@ -391,7 +390,7 @@ public class SwissTimingReplayToDomainAdapter extends SwissTimingReplayAdapter i private void createRace() { RaceDefinition race = domainFactory.createRaceDefinition(regatta, - currentRaceID, competitorsPerRaceID.get(currentRaceID), currentCourseDefinition, raceName, raceIdForRaceDefinition); + currentRaceID, competitorsAndBoatsPerRaceID.get(currentRaceID), currentCourseDefinition, raceName, raceIdForRaceDefinition); synchronized (racePerRaceIdForRaceDefinition) { racePerRaceIdForRaceDefinition.put(raceIdForRaceDefinition, race); racePerRaceIdForRaceDefinition.notifyAll(); diff --git a/java/com.sap.sailing.domain.test/resources/IncrementalLeaderboardDTO.ser b/java/com.sap.sailing.domain.test/resources/IncrementalLeaderboardDTO.ser index 435b8ce147b..d1c0dd99382 100755 Binary files a/java/com.sap.sailing.domain.test/resources/IncrementalLeaderboardDTO.ser and b/java/com.sap.sailing.domain.test/resources/IncrementalLeaderboardDTO.ser differ diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/common/test/LeaderboardDTODiffingTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/common/test/LeaderboardDTODiffingTest.java index 6d81cdba6b7..16e9520489e 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/common/test/LeaderboardDTODiffingTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/common/test/LeaderboardDTODiffingTest.java @@ -236,10 +236,13 @@ public class LeaderboardDTODiffingTest { @Test public void testCompetitorListChange() { newVersion.competitors = new ArrayList(newVersion.competitors); // clone competitor list so it's not identical to that of previous version - CompetitorDTO somebodyNew = new CompetitorDTOImpl("Someone New", Color.RED, "someone@nobody.de", "DE", "GER", "Germany", "912p09871203987", - /* imageURL */ null, /* flagImageURL */ null, new BoatDTO("LSC", "GER 1234"), new BoatClassDTO("505", - BoatClassMasterdata._5O5.getHullLength(), BoatClassMasterdata._5O5.getHullBeam()), /* timeOnTimeFactor */ null, - /* timeOnDistanceAllowancePerNauticalMile */ null, null); + BoatDTO boat = new BoatDTO("123", "LSC", new BoatClassDTO("505", BoatClassMasterdata._5O5.getHullLength(), BoatClassMasterdata._5O5.getHullBeam()), "GER 1234"); + CompetitorDTO somebodyNew = new CompetitorDTOImpl("Someone New", "SN", Color.RED, "someone@nobody.de", "DE", "GER", "Germany", "912p09871203987", + /* imageURL */ null, /* flagImageURL */ null, + /* timeOnTimeFactor */ null, + /* timeOnDistanceAllowancePerNauticalMile */ null, + /* searchTag */ null, + boat); newVersion.competitors.add(13, somebodyNew); // insert a competitor; this should mess up all others' indexes; check if this works CompetitorDTO wolfgang = getPreviousCompetitorByName("HUNGER +JESS"); newVersion.competitors.remove(wolfgang); @@ -258,10 +261,13 @@ public class LeaderboardDTODiffingTest { Util.addAll(newVersion.getSuppressedCompetitors(), newSuppressedCompetitors); newVersion.setSuppressedCompetitors(newSuppressedCompetitors); newVersion.competitors = new ArrayList(newVersion.competitors); // clone competitor list so it's not identical to that of previous version - CompetitorDTO somebodyNew = new CompetitorDTOImpl("Someone New", Color.RED, "someone@nobody.de", "DE", "GER", "Germany", "912p09871203987", - /* imageURL */ null, /* flagImageURL */ null, new BoatDTO("LSC", "GER 1234"), new BoatClassDTO("505", - BoatClassMasterdata._5O5.getHullLength(), BoatClassMasterdata._5O5.getHullBeam()), /* timeOnTimeFactor */ null, - /* timeOnDistanceAllowancePerNauticalMile */ null, null); + BoatDTO boat = new BoatDTO("123", "LSC", new BoatClassDTO("505", BoatClassMasterdata._5O5.getHullLength(), BoatClassMasterdata._5O5.getHullBeam()), "GER 1234"); + CompetitorDTO somebodyNew = new CompetitorDTOImpl("Someone New", "SN", Color.RED, "someone@nobody.de", "DE", "GER", "Germany", "912p09871203987", + /* imageURL */ null, /* flagImageURL */ null, + /* timeOnTimeFactor */ null, + /* timeOnDistanceAllowancePerNauticalMile */ null, + /* searchTag */ null, + boat); newVersion.setSuppressed(newVersion.competitors.get(13), true); // suppress an existing competitor; compaction should reduce this to a single number only newVersion.setSuppressed(somebodyNew, true); // check that mixed mode with existing and new competitors works as well List newSuppressedCompetitorsBeforeStripping = new ArrayList(); @@ -282,10 +288,13 @@ public class LeaderboardDTODiffingTest { @Test public void testDisplayNameChange() { newVersion.competitors = new ArrayList(newVersion.competitors); // clone competitor list so it's not identical to that of previous version - CompetitorDTO somebodyNew = new CompetitorDTOImpl("Someone New", Color.RED, "someone@nobody.de", "DE", "GER", "Germany", "912p09871203987", - /* imageURL */ null, /* flagImageURL */ null, new BoatDTO("LSC", "GER 1234"), new BoatClassDTO("505", - BoatClassMasterdata._5O5.getHullLength(), BoatClassMasterdata._5O5.getHullBeam()), /* timeOnTimeFactor */ null, - /* timeOnDistanceAllowancePerNauticalMile */ null, null); + BoatDTO boat = new BoatDTO("123", "LSC", new BoatClassDTO("505", BoatClassMasterdata._5O5.getHullLength(), BoatClassMasterdata._5O5.getHullBeam()), "GER 1234"); + CompetitorDTO somebodyNew = new CompetitorDTOImpl("Someone New", "SN", Color.RED, "someone@nobody.de", "DE", "GER", "Germany", "912p09871203987", + /* imageURL */ null, /* flagImageURL */ null, + /* timeOnTimeFactor */ null, + /* timeOnDistanceAllowancePerNauticalMile */ null, + /* searchTag */ null, + boat); newVersion.competitors.add(somebodyNew); newVersion.competitorDisplayNames = new HashMap(newVersion.competitorDisplayNames); newVersion.competitorDisplayNames.put(newVersion.competitors.get(13), "Humba"); @@ -313,10 +322,13 @@ public class LeaderboardDTODiffingTest { List newOrdering = new ArrayList(newVersion.getCompetitorsFromBestToWorst(r9)); newVersion.setCompetitorsFromBestToWorst(r9, newOrdering); newVersion.competitors = new ArrayList(newVersion.competitors); // clone competitor list so it's not identical to that of previous version - CompetitorDTO somebodyNew = new CompetitorDTOImpl("Someone New", Color.RED, "someone@nobody.de", "DE", "GER", "Germany", "912p09871203987", - /* imageURL */ null, /* flagImageURL */ null, new BoatDTO("LSC", "GER 1234"), new BoatClassDTO("505", - BoatClassMasterdata._5O5.getHullLength(), BoatClassMasterdata._5O5.getHullBeam()), /* timeOnTimeFactor */ null, - /* timeOnDistanceAllowancePerNauticalMile */ null, null); + BoatDTO boat = new BoatDTO("123", "LSC", new BoatClassDTO("505", BoatClassMasterdata._5O5.getHullLength(), BoatClassMasterdata._5O5.getHullBeam()), "GER 1234"); + CompetitorDTO somebodyNew = new CompetitorDTOImpl("Someone New", "SN", Color.RED, "someone@nobody.de", "DE", "GER", "Germany", "912p09871203987", + /* imageURL */ null, /* flagImageURL */ null, + /* timeOnTimeFactor */ null, + /* timeOnDistanceAllowancePerNauticalMile */ null, + /* searchTag */ null, + boat); newVersion.competitors.add(somebodyNew); newOrdering.add(somebodyNew); CompetitorDTO formerRank13 = newOrdering.remove(13); diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/markpassingcalculation/impl/CandidateFinderImplWhiteBoxTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/markpassingcalculation/impl/CandidateFinderImplWhiteBoxTest.java index 4eafae651f6..02808ebf6f5 100644 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/markpassingcalculation/impl/CandidateFinderImplWhiteBoxTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/markpassingcalculation/impl/CandidateFinderImplWhiteBoxTest.java @@ -7,8 +7,8 @@ import static org.junit.Assert.assertTrue; import java.lang.reflect.Field; import java.util.ArrayList; -import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.stream.StreamSupport; import org.junit.Before; @@ -18,7 +18,9 @@ import com.sap.sailing.domain.abstractlog.impl.LogEventAuthorImpl; import com.sap.sailing.domain.abstractlog.race.impl.RaceLogImpl; import com.sap.sailing.domain.abstractlog.race.state.impl.RaceStateImpl; import com.sap.sailing.domain.abstractlog.race.state.racingprocedure.impl.ReadonlyRacingProcedureFactory; +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.Waypoint; import com.sap.sailing.domain.base.configuration.impl.EmptyRegattaConfiguration; import com.sap.sailing.domain.common.Position; @@ -54,15 +56,16 @@ public class CandidateFinderImplWhiteBoxTest { } private CandidateFinderWithPublicGetProbabilityOfStartBasedOnOtherCompetitorsStartLineDistances finder; - private Competitor competitor; + private CompetitorWithBoat competitor; private DynamicTrackedRace trackedRace; private TimePoint now; @Before public void setUp() { now = MillisecondsTimePoint.now(); - competitor = TrackBasedTest.createCompetitor("Competitor"); - trackedRace = TrackBasedTest.createTestTrackedRace("Test Regatta", "Test Race", "505", Collections.singleton(competitor), now, /* useMarkPassingCalculator */ true); + competitor = TrackBasedTest.createCompetitorWithBoat("Competitor"); + Map competitorAndBoats = TrackBasedTest.createCompetitorAndBoatsMap(competitor); + trackedRace = TrackBasedTest.createTestTrackedRace("Test Regatta", "Test Race", "505", competitorAndBoats, now, /* useMarkPassingCalculator */ true); finder = new CandidateFinderWithPublicGetProbabilityOfStartBasedOnOtherCompetitorsStartLineDistances(trackedRace); } diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/ranking/TestFarthestAheadWithSkippedMarkPassing.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/ranking/TestFarthestAheadWithSkippedMarkPassing.java index 80cfd44e54e..feaf8d260b5 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/ranking/TestFarthestAheadWithSkippedMarkPassing.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/ranking/TestFarthestAheadWithSkippedMarkPassing.java @@ -9,12 +9,15 @@ import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.List; +import java.util.Map; import java.util.function.Function; import org.junit.Test; import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver; +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.Course; import com.sap.sailing.domain.base.DomainFactory; @@ -73,25 +76,28 @@ import com.sap.sse.common.impl.MillisecondsTimePoint; public class TestFarthestAheadWithSkippedMarkPassing { private RankingMetric rankingMetric; private DynamicTrackedRace trackedRace; - private Competitor c1, c2; + private CompetitorWithBoat c1, c2; private Waypoint start; private Waypoint windward; private Waypoint finish; private void setUp(TimeOnTimeFactorMapping timeOnTimeFactors, Function timeOnDistanceFactors) { - c1 = TrackBasedTest.createCompetitor("SlowBoat"); - c2 = TrackBasedTest.createCompetitor("FastBoat"); - trackedRace = createTrackedRace(Arrays.asList(c1, c2)); + CompetitorWithBoat fastCompetitor = TrackBasedTest.createCompetitorWithBoat("FastBoat"); + c1 = fastCompetitor; + CompetitorWithBoat slowCompetitor = TrackBasedTest.createCompetitorWithBoat("SlowBoat"); + c2 = slowCompetitor; + trackedRace = createTrackedRace(TrackBasedTest.createCompetitorAndBoatsMap(fastCompetitor, slowCompetitor)); rankingMetric = trackedRace.getRankingMetric(); assertEquals(120, trackedRace.getCourseLength().getNauticalMiles(), 0.02); assertSame(RankingMetrics.ONE_DESIGN, trackedRace.getTrackedRegatta().getRegatta().getRankingMetricType()); } - private DynamicTrackedRace createTrackedRace(Iterable competitors) { + private DynamicTrackedRace createTrackedRace(Map competitorsAndBoats) { final TimePoint timePointForFixes = MillisecondsTimePoint.now(); BoatClassImpl boatClass = new BoatClassImpl("Some Handicap Boat Class", /* typicallyStartsUpwind */ true); Regatta regatta = new RegattaImpl(EmptyRaceLogStore.INSTANCE, EmptyRegattaLogStore.INSTANCE, - RegattaImpl.getDefaultName("Test Regatta", boatClass.getName()), boatClass, /*startDate*/ null, /*endDate*/ null, /* trackedRegattaRegistry */ null, + RegattaImpl.getDefaultName("Test Regatta", boatClass.getName()), boatClass, + /* canBoatsOfCompetitorsChangePerRace */ true, /*startDate*/ null, /*endDate*/ null, /* trackedRegattaRegistry */ null, DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), "123", /* courseArea */ null, /* controlTrackingFromStartAndFinishTimes */ false, OneDesignRankingMetric::new); TrackedRegatta trackedRegatta = new DynamicTrackedRegattaImpl(regatta); List waypoints = new ArrayList(); @@ -107,7 +113,7 @@ public class TestFarthestAheadWithSkippedMarkPassing { finish = new WaypointImpl(leeGate); waypoints.add(finish); Course course = new CourseImpl("Test Course", waypoints); - RaceDefinition race = new RaceDefinitionImpl("Test Race", course, boatClass, competitors); + RaceDefinition race = new RaceDefinitionImpl("Test Race", course, boatClass, competitorsAndBoats); DynamicTrackedRaceImpl trackedRace = new DynamicTrackedRaceImpl(trackedRegatta, race, Collections. emptyList(), EmptyWindStore.INSTANCE, /* delayToLiveInMillis */ 0, /* millisecondsOverWhichToAverageWind */ 30000, /* millisecondsOverWhichToAverageSpeed */ 30000, diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/ranking/TestSimpleTimeOnTimeRankingWithOneUpwindLeg.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/ranking/TestSimpleTimeOnTimeRankingWithOneUpwindLeg.java index 272fa635e58..702ef040606 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/ranking/TestSimpleTimeOnTimeRankingWithOneUpwindLeg.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/ranking/TestSimpleTimeOnTimeRankingWithOneUpwindLeg.java @@ -9,12 +9,15 @@ import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.List; +import java.util.Map; import java.util.function.Function; import org.junit.Test; import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver; +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.Course; import com.sap.sailing.domain.base.DomainFactory; @@ -58,22 +61,25 @@ import com.sap.sse.common.impl.MillisecondsTimePoint; public class TestSimpleTimeOnTimeRankingWithOneUpwindLeg { private RankingMetric tot; private DynamicTrackedRace trackedRace; - private Competitor c1, c2; + private CompetitorWithBoat c1, c2; private void setUp(TimeOnTimeFactorMapping timeOnTimeFactors, Function timeOnDistanceFactors) { - c1 = TrackBasedTest.createCompetitor("FastBoat"); - c2 = TrackBasedTest.createCompetitor("SlowBoat"); - trackedRace = createTrackedRace(Arrays.asList(c1, c2), timeOnTimeFactors, timeOnDistanceFactors); + CompetitorWithBoat fastCompetitor = TrackBasedTest.createCompetitorWithBoat("FastBoat"); + c1 = fastCompetitor; + CompetitorWithBoat slowCompetitor = TrackBasedTest.createCompetitorWithBoat("SlowBoat"); + c2 = slowCompetitor; + trackedRace = createTrackedRace(TrackBasedTest.createCompetitorAndBoatsMap(fastCompetitor, slowCompetitor), timeOnTimeFactors, timeOnDistanceFactors); tot = trackedRace.getRankingMetric(); assertEquals(60, trackedRace.getCourseLength().getNauticalMiles(), 0.01); assertSame(RankingMetrics.TIME_ON_TIME_AND_DISTANCE, trackedRace.getTrackedRegatta().getRegatta().getRankingMetricType()); } - private DynamicTrackedRace createTrackedRace(Iterable competitors, TimeOnTimeFactorMapping timeOnTimeFactors, Function timeOnDistanceFactors) { + private DynamicTrackedRace createTrackedRace(Map competitorsAndBoats, TimeOnTimeFactorMapping timeOnTimeFactors, Function timeOnDistanceFactors) { final TimePoint timePointForFixes = MillisecondsTimePoint.now(); BoatClassImpl boatClass = new BoatClassImpl("Some Handicap Boat Class", /* typicallyStartsUpwind */ true); Regatta regatta = new RegattaImpl(EmptyRaceLogStore.INSTANCE, EmptyRegattaLogStore.INSTANCE, - RegattaImpl.getDefaultName("Test Regatta", boatClass.getName()), boatClass, /*startDate*/ null, /*endDate*/ null, /* trackedRegattaRegistry */ null, + RegattaImpl.getDefaultName("Test Regatta", boatClass.getName()), boatClass, + /* canBoatsOfCompetitorsChangePerRace */ true, /*startDate*/ null, /*endDate*/ null, /* trackedRegattaRegistry */ null, DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), "123", /* courseArea */ null, /* controlTrackingFromStartAndFinishTimes */ false, TimeOnTimeAndDistanceRankingMetric::new); TrackedRegatta trackedRegatta = new DynamicTrackedRegattaImpl(regatta); List waypoints = new ArrayList(); @@ -85,7 +91,7 @@ public class TestSimpleTimeOnTimeRankingWithOneUpwindLeg { waypoints.add(new WaypointImpl(leeGate)); waypoints.add(new WaypointImpl(windwardMark)); Course course = new CourseImpl("Test Course", waypoints); - RaceDefinition race = new RaceDefinitionImpl("Test Race", course, boatClass, competitors); + RaceDefinition race = new RaceDefinitionImpl("Test Race", course, boatClass, competitorsAndBoats); DynamicTrackedRaceImpl trackedRace = new DynamicTrackedRaceImpl(trackedRegatta, race, Collections. emptyList(), EmptyWindStore.INSTANCE, /* delayToLiveInMillis */ 0, /* millisecondsOverWhichToAverageWind */ 30000, /* millisecondsOverWhichToAverageSpeed */ 30000, diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/AbstractLeaderboardTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/AbstractLeaderboardTest.java index d5bdb4cccc5..a862947ea90 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/AbstractLeaderboardTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/AbstractLeaderboardTest.java @@ -1,44 +1,44 @@ package com.sap.sailing.domain.test; -import java.io.Serializable; import java.util.Collections; +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.CompetitorFactory; +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.CompetitorImpl; +import com.sap.sailing.domain.base.impl.CompetitorWithBoatImpl; +import com.sap.sailing.domain.base.impl.DynamicBoat; import com.sap.sailing.domain.base.impl.NationalityImpl; import com.sap.sailing.domain.base.impl.PersonImpl; import com.sap.sailing.domain.base.impl.TeamImpl; import com.sap.sse.common.Color; public abstract class AbstractLeaderboardTest { - public static CompetitorImpl createCompetitor(String competitorName) { - return new CompetitorImpl(competitorName, competitorName, Color.RED, null, null, new TeamImpl("STG", Collections.singleton( + private final static BoatClass boatClass = new BoatClassImpl("505", /* typicallyStartsUpwind */ true); + + public static Competitor createCompetitor(String competitorName) { + return new CompetitorImpl(competitorName, competitorName, "KYC", Color.RED, null, null, new TeamImpl("STG", Collections.singleton( new PersonImpl(competitorName, new NationalityImpl("GER"), /* dateOfBirth */ null, "This is famous "+competitorName)), new PersonImpl("Rigo van Maas", new NationalityImpl("NED"), - /* dateOfBirth */null, "This is Rigo, the coach")), new BoatImpl(competitorName + "'s boat", - new BoatClassImpl("505", /* typicallyStartsUpwind */ true), /* sailID */ null), /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null); + /* dateOfBirth */null, "This is Rigo, the coach")), + /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, /* searchTag */ null); } - public static Competitor createCompetitor(String competitorName, CompetitorFactory competitorFactory) { - return competitorFactory.getOrCreateCompetitor(competitorName, competitorName, Color.RED, "someone@nobody.de", null, new TeamImpl("STG", Collections.singleton( - new PersonImpl(competitorName, new NationalityImpl("GER"), - /* dateOfBirth */ null, "This is famous "+competitorName)), - new PersonImpl("Rigo van Maas", new NationalityImpl("NED"), - /* dateOfBirth */null, "This is Rigo, the coach")), new BoatImpl(competitorName + "'s boat", - new BoatClassImpl("505", /* typicallyStartsUpwind */ true), /* sailID */ null), - /* timeOnTimeFactor */ null, /* timeOnDistanceAllowanceInSecondsPerNauticalMile */ null, null); + public static Boat createBoat(String competitorName) { + return new BoatImpl("id12345", competitorName + "'s boat", boatClass, /* sailID */ null); } - public static CompetitorImpl createCompetitor(String competitorName, Serializable id) { - return new CompetitorImpl(id, competitorName, Color.RED, null, null, new TeamImpl("STG", Collections.singleton( + public static CompetitorWithBoat createCompetitorWithBoat(String competitorName) { + DynamicBoat b = (DynamicBoat) createBoat(competitorName); + return new CompetitorWithBoatImpl(competitorName, competitorName, "KYC", Color.RED, null, null, new TeamImpl("STG", Collections.singleton( new PersonImpl(competitorName, new NationalityImpl("GER"), /* dateOfBirth */ null, "This is famous "+competitorName)), new PersonImpl("Rigo van Maas", new NationalityImpl("NED"), - /* dateOfBirth */null, "This is Rigo, the coach")), new BoatImpl(competitorName + "'s boat", - new BoatClassImpl("505", /* typicallyStartsUpwind */ true), /* sailID */ null), /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null); + /* dateOfBirth */null, "This is Rigo, the coach")), + /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, /* searchTag */ null, b); } } diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/CompetitorListenerTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/CompetitorListenerTest.java index 1a170f91d5f..867102db523 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/CompetitorListenerTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/CompetitorListenerTest.java @@ -37,12 +37,12 @@ public class CompetitorListenerTest extends AbstractSerializationTest { private Object whatChangedNationality; private Object oldNationality; private Object newNationality; - private boolean sailIdChanged; - private String oldSailId; - private String newSailId; private boolean nameChanged; private String oldName; private String newName; + private boolean shortNameChanged; + private String oldShortName; + private String newShortName; private boolean colorChanged; private Color oldColor; private Color newColor; @@ -67,17 +67,17 @@ public class CompetitorListenerTest extends AbstractSerializationTest { @Before public void setUp() { baseDomainFactory = new DomainFactoryImpl((srlid)->null); - competitor = TrackBasedTest.createCompetitor("Hasso"); + competitor = (DynamicCompetitor) TrackBasedTest.createCompetitorWithBoat("Hasso"); nationalityChanged = false; whatChangedNationality = null; oldNationality = null; newNationality = null; - sailIdChanged = false; - oldSailId = null; - newSailId = null; nameChanged = false; oldName = null; newName = null; + shortNameChanged = false; + oldShortName = null; + newShortName = null; colorChanged = false; oldColor = null; newColor = null; @@ -107,20 +107,20 @@ public class CompetitorListenerTest extends AbstractSerializationTest { CompetitorListenerTest.this.newNationality = newNationality; } - @Override - public void sailIdChanged(String oldSailId, String newSailId) { - sailIdChanged = true; - CompetitorListenerTest.this.oldSailId = oldSailId; - CompetitorListenerTest.this.newSailId = newSailId; - } - @Override public void nameChanged(String oldName, String newName) { nameChanged = true; CompetitorListenerTest.this.oldName = oldName; CompetitorListenerTest.this.newName = newName; } - + + @Override + public void shortNameChanged(String oldShortName, String newShortName) { + shortNameChanged = true; + CompetitorListenerTest.this.oldShortName = oldShortName; + CompetitorListenerTest.this.newShortName = newShortName; + } + @Override public void colorChanged(Color oldColor, Color newColor) { colorChanged = true; @@ -202,11 +202,18 @@ public class CompetitorListenerTest extends AbstractSerializationTest { assertTrue(nameChanged); assertEquals("Hasso", oldName); assertEquals("Dr. Hasso Plattner", newName); - assertFalse(sailIdChanged); assertFalse(colorChanged); assertFalse(nationalityChanged); } + @Test + public void testShortNameChange() throws URISyntaxException { + competitor.setShortName("Dr. HP"); + assertTrue(shortNameChanged); + assertEquals("HP", oldShortName); + assertEquals("Dr. HP", newShortName); + } + @Test public void testFlagChange() throws URISyntaxException { competitor.setFlagImage(new URI("http://www.something.de/pic.png")); @@ -231,25 +238,12 @@ public class CompetitorListenerTest extends AbstractSerializationTest { assertEquals("hasso", newSearchTag); } - @Test - public void testSimpleCompetitorListenerPatternForSailId() { - final String myOldSailId = competitor.getBoat().getSailID(); - competitor.getBoat().setSailId("POR 40"); - assertFalse(nameChanged); - assertTrue(sailIdChanged); - assertEquals(myOldSailId, oldSailId); - assertEquals("POR 40", newSailId); - assertFalse(colorChanged); - assertFalse(nationalityChanged); - } - @Test public void testSimpleCompetitorListenerPatternForColor() { final Color myOldColor = competitor.getColor(); final RGBColor myNewColor = new RGBColor(123, 12, 234); competitor.setColor(myNewColor); assertFalse(nameChanged); - assertFalse(sailIdChanged); assertTrue(colorChanged); assertEquals(myOldColor, oldColor); assertEquals(myNewColor, newColor); @@ -262,7 +256,6 @@ public class CompetitorListenerTest extends AbstractSerializationTest { final Nationality myNewNationality = DomainFactory.INSTANCE.getOrCreateNationality("POR"); competitor.getTeam().setNationality(myNewNationality); assertFalse(nameChanged); - assertFalse(sailIdChanged); assertFalse(colorChanged); assertTrue(nationalityChanged); assertEquals(myOldNationality, oldNationality); @@ -275,13 +268,11 @@ public class CompetitorListenerTest extends AbstractSerializationTest { DomainFactory baseDomainFactory = new DomainFactoryImpl((srlid)->null); DynamicCompetitor clonedCompetitor = cloneBySerialization(competitor, baseDomainFactory); clonedCompetitor.setName("Dr. Hasso Plattner"); - clonedCompetitor.getBoat().setSailId("POR 40"); final RGBColor myNewColor = new RGBColor(123, 12, 234); clonedCompetitor.setColor(myNewColor); final Nationality myNewNationality = baseDomainFactory.getOrCreateNationality("POR"); clonedCompetitor.getTeam().setNationality(myNewNationality); assertFalse(nameChanged); - assertFalse(sailIdChanged); assertFalse(colorChanged); assertFalse(nationalityChanged); } @@ -307,7 +298,6 @@ public class CompetitorListenerTest extends AbstractSerializationTest { DynamicCompetitor clonedCompetitor = cloneBySerialization(competitor, baseDomainFactory); competitor = clonedCompetitor; competitor.addCompetitorChangeListener(listener); - testSimpleCompetitorListenerPatternForSailId(); } @Test diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/CompetitorProviderCacheInvalidationTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/CompetitorProviderCacheInvalidationTest.java index 61002947418..547c9c81241 100644 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/CompetitorProviderCacheInvalidationTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/CompetitorProviderCacheInvalidationTest.java @@ -20,11 +20,13 @@ import com.sap.sailing.domain.abstractlog.impl.LogEventAuthorImpl; import com.sap.sailing.domain.abstractlog.race.RaceLog; import com.sap.sailing.domain.abstractlog.race.impl.RaceLogRevokeEventImpl; import com.sap.sailing.domain.abstractlog.race.tracking.RaceLogRegisterCompetitorEvent; +import com.sap.sailing.domain.abstractlog.race.tracking.RaceLogUseCompetitorsFromRaceLogEvent; import com.sap.sailing.domain.abstractlog.race.tracking.impl.RaceLogRegisterCompetitorEventImpl; import com.sap.sailing.domain.abstractlog.race.tracking.impl.RaceLogUseCompetitorsFromRaceLogEventImpl; import com.sap.sailing.domain.abstractlog.regatta.RegattaLog; import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogRegisterCompetitorEvent; import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogRegisterCompetitorEventImpl; +import com.sap.sailing.domain.base.Boat; import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.Series; import com.sap.sailing.domain.base.impl.BoatClassImpl; @@ -49,7 +51,7 @@ import com.sap.sse.common.impl.MillisecondsTimePoint; /** * Tests the behavior of the class {@link CompetitorProviderFromRaceColumnsAndRegattaLike}, paying particular - * attention to its caching and cache invalidation logic. When competitors are added to regatta logs or race logs + * attention to its caching and cache invalidation logic. When competitors and their boats are added to regatta logs or race logs * or when a tracked race is attached to a column or a column is added or removed, its caches need to be * invalidated and re-calculated accordingly. * @@ -65,6 +67,7 @@ public class CompetitorProviderCacheInvalidationTest extends AbstractLeaderboard final int NUMBER_OF_COMP_LISTS = 4; @SuppressWarnings("unchecked") private List[] compLists = (List[]) new List[NUMBER_OF_COMP_LISTS]; + private Map boats = new HashMap<>(); @Before public void setUp() { @@ -74,8 +77,9 @@ public class CompetitorProviderCacheInvalidationTest extends AbstractLeaderboard new ThresholdBasedResultDiscardingRuleImpl(new int[0]), new LowPoint(), courseArea); competitorProviderFlexibleLeaderboard = new CompetitorProviderFromRaceColumnsAndRegattaLike(flexibleLeaderboard); regatta = new RegattaImpl(EmptyRaceLogStore.INSTANCE, - EmptyRegattaLogStore.INSTANCE, "Test Regatta", new BoatClassImpl("49er", BoatClassMasterdata._49ER), /* startDate */ - null, /* endDate */null, /* trackedRegattaRegistry */null, new LowPoint(), UUID.randomUUID(), + EmptyRegattaLogStore.INSTANCE, "Test Regatta", new BoatClassImpl("49er", BoatClassMasterdata._49ER), + /* canBoatsOfCompetitorsChangePerRace */ true, /* startDate */ null, /* endDate */null, + /* trackedRegattaRegistry */null, new LowPoint(), UUID.randomUUID(), courseArea); regattaLeaderboard = new RegattaLeaderboardImpl(regatta, new ThresholdBasedResultDiscardingRuleImpl(new int[0])); regatta.addSeries(new SeriesImpl("Test Series", /* isMedal */false, /* isFleetsCanRunInParallel */ true, Arrays.asList(new FleetImpl("Yellow"), @@ -83,7 +87,9 @@ public class CompetitorProviderCacheInvalidationTest extends AbstractLeaderboard for (int l = 0; l < NUMBER_OF_COMP_LISTS; l++) { compLists[l] = new ArrayList(); for (int i = 0; i < 10; i++) { - compLists[l].add(createCompetitor("" + l + "/" + i)); + Competitor c = createCompetitor("" + l + "/" + i); + compLists[l].add(c); + boats.put(c, createBoat("" + l + "/" + i)); } } competitorProviderRegattaLeaderboard = new CompetitorProviderFromRaceColumnsAndRegattaLike(regattaLeaderboard); @@ -102,7 +108,7 @@ public class CompetitorProviderCacheInvalidationTest extends AbstractLeaderboard final Map competitorOnRegattaLogRegistrationEvents = new HashMap<>(); final LogEventAuthorImpl author = new LogEventAuthorImpl("Me", 0); for (Competitor c : compLists[0]) { - final RegattaLogRegisterCompetitorEventImpl registerCompetitorEvent = new RegattaLogRegisterCompetitorEventImpl(MillisecondsTimePoint.now(), MillisecondsTimePoint.now(), author, UUID.randomUUID(), c); + final RegattaLogRegisterCompetitorEvent registerCompetitorEvent = new RegattaLogRegisterCompetitorEventImpl(MillisecondsTimePoint.now(), MillisecondsTimePoint.now(), author, UUID.randomUUID(), c); regattaLog.add(registerCompetitorEvent); competitorOnRegattaLogRegistrationEvents.put(c, registerCompetitorEvent); } @@ -139,7 +145,7 @@ public class CompetitorProviderCacheInvalidationTest extends AbstractLeaderboard raceLog.add(new RaceLogUseCompetitorsFromRaceLogEventImpl(MillisecondsTimePoint.now(), author, MillisecondsTimePoint.now(), UUID.randomUUID(), passId)); final Map competitorOnRaceLogRegistrationEvents = new HashMap<>(); for (Competitor c : compLists[0]) { - final RaceLogRegisterCompetitorEvent registerCompetitorEvent = new RaceLogRegisterCompetitorEventImpl(MillisecondsTimePoint.now(), author, passId, c); + final RaceLogRegisterCompetitorEvent registerCompetitorEvent = new RaceLogRegisterCompetitorEventImpl(MillisecondsTimePoint.now(), author, passId, c, boats.get(c)); raceLog.add(registerCompetitorEvent); competitorOnRaceLogRegistrationEvents.put(c, registerCompetitorEvent); } @@ -202,10 +208,10 @@ public class CompetitorProviderCacheInvalidationTest extends AbstractLeaderboard RaceLog raceLog = flexibleLeaderboard.getRacelog("R1", LeaderboardNameConstants.DEFAULT_FLEET_NAME); final LogEventAuthorImpl author = new LogEventAuthorImpl("Me", 0); int passId = 1; - final RaceLogUseCompetitorsFromRaceLogEventImpl usesCompetitorsFromRaceLogEvent = new RaceLogUseCompetitorsFromRaceLogEventImpl(MillisecondsTimePoint.now(), author, MillisecondsTimePoint.now(), UUID.randomUUID(), passId); + final RaceLogUseCompetitorsFromRaceLogEvent usesCompetitorsFromRaceLogEvent = new RaceLogUseCompetitorsFromRaceLogEventImpl(MillisecondsTimePoint.now(), author, MillisecondsTimePoint.now(), UUID.randomUUID(), passId); raceLog.add(usesCompetitorsFromRaceLogEvent); for (Competitor c : compLists[1]) { - raceLog.add(new RaceLogRegisterCompetitorEventImpl(MillisecondsTimePoint.now(), new LogEventAuthorImpl("Me", 0), 1, c)); + raceLog.add(new RaceLogRegisterCompetitorEventImpl(MillisecondsTimePoint.now(), new LogEventAuthorImpl("Me", 0), 1, c, boats.get(c))); } Set allCompetitors = new HashSet<>(); allCompetitors.addAll(compLists[0]); @@ -305,7 +311,7 @@ public class CompetitorProviderCacheInvalidationTest extends AbstractLeaderboard int passId = 1; raceLog.add(new RaceLogUseCompetitorsFromRaceLogEventImpl(MillisecondsTimePoint.now(), author, MillisecondsTimePoint.now(), UUID.randomUUID(), passId)); for (Competitor c : compLists[0]) { - raceLog.add(new RaceLogRegisterCompetitorEventImpl(MillisecondsTimePoint.now(), new LogEventAuthorImpl("Me", 0), 1, c)); + raceLog.add(new RaceLogRegisterCompetitorEventImpl(MillisecondsTimePoint.now(), new LogEventAuthorImpl("Me", 0), 1, c, boats.get(c))); } Set expected = new HashSet<>(compLists[0]); Set actual = new HashSet<>(); @@ -323,7 +329,7 @@ public class CompetitorProviderCacheInvalidationTest extends AbstractLeaderboard final Map competitorOnRegattaLogRegistrationEvents = new HashMap<>(); final LogEventAuthorImpl author = new LogEventAuthorImpl("Me", 0); for (Competitor c : compLists[0]) { - final RegattaLogRegisterCompetitorEventImpl registerCompetitorEvent = new RegattaLogRegisterCompetitorEventImpl(MillisecondsTimePoint.now(), MillisecondsTimePoint.now(), author, UUID.randomUUID(), c); + final RegattaLogRegisterCompetitorEvent registerCompetitorEvent = new RegattaLogRegisterCompetitorEventImpl(MillisecondsTimePoint.now(), MillisecondsTimePoint.now(), author, UUID.randomUUID(), c); regattaLog.add(registerCompetitorEvent); competitorOnRegattaLogRegistrationEvents.put(c, registerCompetitorEvent); } @@ -363,7 +369,7 @@ public class CompetitorProviderCacheInvalidationTest extends AbstractLeaderboard raceLog.add(new RaceLogUseCompetitorsFromRaceLogEventImpl(MillisecondsTimePoint.now(), author, MillisecondsTimePoint.now(), UUID.randomUUID(), passId)); final Map competitorOnRaceLogRegistrationEvents = new HashMap<>(); for (Competitor c : compLists[passId]) { - final RaceLogRegisterCompetitorEvent registerCompetitorEvent = new RaceLogRegisterCompetitorEventImpl(MillisecondsTimePoint.now(), author, passId, c); + final RaceLogRegisterCompetitorEvent registerCompetitorEvent = new RaceLogRegisterCompetitorEventImpl(MillisecondsTimePoint.now(), author, passId, c, boats.get(c)); raceLog.add(registerCompetitorEvent); competitorOnRaceLogRegistrationEvents.put(c, registerCompetitorEvent); } diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/CourseTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/CourseTest.java index 12ed885053a..5ca55645c38 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/CourseTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/CourseTest.java @@ -9,14 +9,19 @@ import static org.mockito.Mockito.mock; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; -import java.util.Set; +import java.util.Map; import org.junit.Test; import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver; +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.ControlPoint; import com.sap.sailing.domain.base.Course; import com.sap.sailing.domain.base.DomainFactory; @@ -25,7 +30,6 @@ import com.sap.sailing.domain.base.Series; import com.sap.sailing.domain.base.Sideline; import com.sap.sailing.domain.base.Waypoint; import com.sap.sailing.domain.base.impl.BoatClassImpl; -import com.sap.sailing.domain.base.impl.CompetitorImpl; import com.sap.sailing.domain.base.impl.ControlPointWithTwoMarksImpl; import com.sap.sailing.domain.base.impl.CourseImpl; import com.sap.sailing.domain.base.impl.MarkImpl; @@ -299,10 +303,13 @@ public class CourseTest { waypoints.add(wp3); Course course = new CourseImpl("Test Course", waypoints); assertWaypointIndexes(course); - final Set hasso = Collections.singleton(AbstractLeaderboardTest.createCompetitor("Hasso")); + final BoatClass boatClass = new BoatClassImpl("505", /* upwind start */true); + final CompetitorWithBoat hasso = AbstractLeaderboardTest.createCompetitorWithBoat("Hasso"); + final Map competitorsAndBoats = new HashMap<>(); + competitorsAndBoats.put(hasso, hasso.getBoat()); DynamicTrackedRace trackedRace = new DynamicTrackedRaceImpl(/* trackedRegatta */new DynamicTrackedRegattaImpl( - new RegattaImpl("test", null, null, null, new HashSet(), false, null, "test", null, OneDesignRankingMetric::new)), - new RaceDefinitionImpl("Test Race", course, new BoatClassImpl("49er", /* upwind start */true), hasso), + new RegattaImpl("test", null, true, null, null, new HashSet(), false, null, "test", null, OneDesignRankingMetric::new)), + new RaceDefinitionImpl("Test Race", course, boatClass, competitorsAndBoats), Collections. emptyList(), EmptyWindStore.INSTANCE, /* delayToLiveInMillis */3000, /* millisecondsOverWhichToAverageWind */30000, /* millisecondsOverWhichToAverageSpeed */8000, /*useMarkPassingCalculator*/ false, OneDesignRankingMetric::new, @@ -330,11 +337,13 @@ public class CourseTest { final WaypointImpl wp2 = new WaypointImpl(new MarkImpl("Test Mark 2")); waypoints.add(wp2); Course course = new CourseImpl("Test Course", waypoints); - final Set hasso = Collections.singleton(AbstractLeaderboardTest.createCompetitor("Hasso")); + final BoatClass boatClass = new BoatClassImpl("505", /* upwind start */true); + final CompetitorWithBoat hasso = AbstractLeaderboardTest.createCompetitorWithBoat("Hasso"); + final Map competitorsAndBoats = new HashMap<>(); + competitorsAndBoats.put(hasso, hasso.getBoat()); DynamicTrackedRace trackedRace = new DynamicTrackedRaceImpl(/* trackedRegatta */ new DynamicTrackedRegattaImpl( - new RegattaImpl("test", null, null, null, new HashSet(), false, null, "test", null, OneDesignRankingMetric::new)), - new RaceDefinitionImpl("Test Race", course, new BoatClassImpl("49er", /* upwind start */ true), - hasso), Collections. emptyList(), + new RegattaImpl("test", null, true, null, null, new HashSet(), false, null, "test", null, OneDesignRankingMetric::new)), + new RaceDefinitionImpl("Test Race", course, boatClass, competitorsAndBoats), Collections. emptyList(), EmptyWindStore.INSTANCE, /* delayToLiveInMillis */ 3000, /* millisecondsOverWhichToAverageWind */ 30000, /* millisecondsOverWhichToAverageSpeed */ 8000, /*useMarkPassingCalculator*/ false, OneDesignRankingMetric::new, diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/CourseUpdateDuringNonAtomicSerializationTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/CourseUpdateDuringNonAtomicSerializationTest.java index c7d1deee589..107bc2d3be2 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/CourseUpdateDuringNonAtomicSerializationTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/CourseUpdateDuringNonAtomicSerializationTest.java @@ -21,6 +21,7 @@ import java.util.UUID; import org.junit.Test; import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver; +import com.sap.sailing.domain.base.Boat; import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.Course; import com.sap.sailing.domain.base.RaceDefinition; @@ -75,10 +76,11 @@ public class CourseUpdateDuringNonAtomicSerializationTest implements Serializabl removeCalls = new HashMap<>(); course = new CourseImpl("Test Course", waypoints); Regatta regatta = new RegattaImpl(EmptyRaceLogStore.INSTANCE, EmptyRegattaLogStore.INSTANCE, "Test Regatta", - new BoatClassImpl("505", BoatClassMasterdata._5O5), /*startDate*/ null, /*endDate*/ null, /* trackedRegattaRegistry */ null, + new BoatClassImpl("505", BoatClassMasterdata._5O5), + /* canBoatsOfCompetitorsChangePerRace */ true, /*startDate*/ null, /*endDate*/ null, /* trackedRegattaRegistry */ null, new LowPoint(), UUID.randomUUID(), new CourseAreaImpl("Alpha", UUID.randomUUID())); TrackedRegatta trackedRegatta = new TrackedRegattaImpl(regatta); - RaceDefinition race = new RaceDefinitionImpl("Test Race", course, regatta.getBoatClass(), Collections.emptySet()); + RaceDefinition race = new RaceDefinitionImpl("Test Race", course, regatta.getBoatClass(), Collections.emptyMap()); trackedRace = new DynamicTrackedRaceImpl(trackedRegatta, race, Collections. emptySet(), EmptyWindStore.INSTANCE, /* delayToLiveInMillis */10000, /* millisecondsOverWhichToAverageWind */30000, /* millisecondsOverWhichToAverageSpeed */ diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/CourseUpdateTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/CourseUpdateTest.java index 0dd353f5529..822847e5edf 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/CourseUpdateTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/CourseUpdateTest.java @@ -6,6 +6,7 @@ import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; import java.io.IOException; import java.net.MalformedURLException; @@ -20,7 +21,6 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; import org.mockito.Mockito; -import static org.mockito.Mockito.mock; import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver; import com.sap.sailing.domain.base.Competitor; @@ -35,6 +35,7 @@ import com.sap.sailing.domain.base.impl.CourseImpl; import com.sap.sailing.domain.base.impl.MarkImpl; import com.sap.sailing.domain.base.impl.WaypointImpl; import com.sap.sailing.domain.common.PassingInstruction; +import com.sap.sailing.domain.leaderboard.LeaderboardGroupResolver; import com.sap.sailing.domain.racelog.impl.EmptyRaceLogStore; import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore; import com.sap.sailing.domain.tracking.DynamicRaceDefinitionSet; @@ -95,7 +96,7 @@ public class CourseUpdateTest extends AbstractTracTracLiveTest { }, /* delayToLiveInMillis */0l, /* millisecondsOverWhichToAverageWind */30000, /* simulator */null, /* courseDesignUpdateURI */ null, /* tracTracUsername */null, /* tracTracPassword */null, getEventSubscriber(), getRaceSubscriber(), - /*ignoreTracTracMarkPassings*/false, mock(RaceLogResolver.class), RaceTracker.TIMEOUT_FOR_RECEIVING_RACE_DEFINITION_IN_MILLISECONDS) { + /*ignoreTracTracMarkPassings*/false, mock(RaceLogResolver.class), mock(LeaderboardGroupResolver.class), RaceTracker.TIMEOUT_FOR_RECEIVING_RACE_DEFINITION_IN_MILLISECONDS) { @Override protected void handleEvent(Triple event) { super.handleEvent(event); diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/DistanceCacheInvalidationAfterLoadingFinishedTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/DistanceCacheInvalidationAfterLoadingFinishedTest.java index add821de9a9..64121bc1458 100644 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/DistanceCacheInvalidationAfterLoadingFinishedTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/DistanceCacheInvalidationAfterLoadingFinishedTest.java @@ -3,11 +3,14 @@ package com.sap.sailing.domain.test; import static org.junit.Assert.assertEquals; import java.util.Collections; +import java.util.Map; import org.junit.Before; import org.junit.Test; +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.common.Distance; import com.sap.sailing.domain.common.Position; import com.sap.sailing.domain.common.TrackedRaceStatusEnum; @@ -32,13 +35,14 @@ import com.sap.sse.common.impl.MillisecondsTimePoint; * */ public class DistanceCacheInvalidationAfterLoadingFinishedTest extends TrackBasedTest { - private Competitor competitor; + private CompetitorWithBoat competitor; private DynamicTrackedRace trackedRace; @Before public void setUp() { - competitor = createCompetitor("Test Competitor"); - trackedRace = createTestTrackedRace("Test Regatta", "Test Race", "505", Collections.singleton(competitor), MillisecondsTimePoint.now(), /* useMarkPassingCalculator */ false); + competitor = createCompetitorWithBoat("Test Competitor"); + Map competitorsAndBoats = TrackBasedTest.createCompetitorAndBoatsMap(competitor); + trackedRace = createTestTrackedRace("Test Regatta", "Test Race", "505", competitorsAndBoats, MillisecondsTimePoint.now(), /* useMarkPassingCalculator */ false); } @Test diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/FetchTracksAndStoreLocallyTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/FetchTracksAndStoreLocallyTest.java index 7b8fa85d92c..65128e5ee6a 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/FetchTracksAndStoreLocallyTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/FetchTracksAndStoreLocallyTest.java @@ -11,6 +11,7 @@ import java.util.Optional; import org.junit.Ignore; import org.junit.Test; +import com.sap.sailing.domain.base.Boat; import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.common.tracking.GPSFixMoving; import com.sap.sailing.domain.common.tracking.impl.GPSFixMovingImpl; @@ -160,8 +161,9 @@ public class FetchTracksAndStoreLocallyTest extends OnlineTracTracBasedTest { private void storeTracks() throws FileNotFoundException, IOException { for (Map.Entry> competitorAndTrack : tracks.entrySet()) { Competitor competitor = competitorAndTrack.getKey(); + Boat boatOfCompetitor = trackedRace.getRace().getBoatOfCompetitor(competitor); DynamicGPSFixTrack track = competitorAndTrack.getValue(); - storeTrack(competitor, track, getTracTracEvent().getName()+"-"+trackedRace.getRace().getName()); + storeTrack(competitor, boatOfCompetitor, track, getTracTracEvent().getName()+"-"+trackedRace.getRace().getName()); } } diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LeaderboardCourseChangeTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LeaderboardCourseChangeTest.java index 4566618d3a8..e5577eb132b 100644 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LeaderboardCourseChangeTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LeaderboardCourseChangeTest.java @@ -1,13 +1,16 @@ package com.sap.sailing.domain.test; import static org.junit.Assert.assertEquals; +import static org.mockito.Matchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; import java.util.Calendar; import java.util.Date; +import java.util.HashMap; import java.util.HashSet; +import java.util.Map; import java.util.Set; import java.util.UUID; import java.util.concurrent.ExecutionException; @@ -15,6 +18,7 @@ import java.util.concurrent.ExecutionException; import org.junit.Test; import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver; +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; @@ -29,10 +33,10 @@ import com.sap.sailing.domain.base.Series; import com.sap.sailing.domain.base.Sideline; import com.sap.sailing.domain.base.Waypoint; import com.sap.sailing.domain.base.impl.BoatClassImpl; +import com.sap.sailing.domain.base.impl.BoatImpl; import com.sap.sailing.domain.base.impl.CompetitorImpl; import com.sap.sailing.domain.base.impl.CourseImpl; import com.sap.sailing.domain.base.impl.DomainFactoryImpl; -import com.sap.sailing.domain.base.impl.DynamicBoat; import com.sap.sailing.domain.base.impl.DynamicTeam; import com.sap.sailing.domain.base.impl.FleetImpl; import com.sap.sailing.domain.base.impl.MarkImpl; @@ -87,7 +91,7 @@ public class LeaderboardCourseChangeTest { RaceColumn raceColumn = series.addRaceColumn(raceColumnName, trackedRegattaRegistry); ScoringScheme scoringScheme = new LowPoint(); Regatta mockedRegatta = new RegattaImpl(RegattaImpl.getDefaultName("TestRegatta", boatClass.getName()), boatClass, - /*startDate*/ null, /*endDate*/ null, seriesSet, false, scoringScheme, + /* canBoatsOfCompetitorsChangePerRace */ true, /*startDate*/ null, /*endDate*/ null, seriesSet, false, scoringScheme, UUID.randomUUID(), mock(CourseArea.class), OneDesignRankingMetric::new); ControlPoint start = new MarkImpl("Start"); ControlPoint m1 = new MarkImpl("M1"); @@ -153,21 +157,24 @@ public class LeaderboardCourseChangeTest { private RaceDefinition createMockedRace(Course course, BoatClass boatClass) { RaceDefinition mockedRaceDefinition = mock(RaceDefinition.class); - when(mockedRaceDefinition.getCompetitors()).thenReturn(new HashSet()); when(mockedRaceDefinition.getCourse()).thenReturn(course); when(mockedRaceDefinition.getBoatClass()).thenReturn(new BoatClassImpl("TestClass", true)); when(mockedRaceDefinition.getName()).thenReturn("TestRace"); - Iterable competitorSet = createCompetitorSet(boatClass); - when(mockedRaceDefinition.getCompetitors()).thenReturn(competitorSet); + Map competitorAndBoatMap = createCompetitorAndBoatMap(boatClass); + when(mockedRaceDefinition.getCompetitors()).thenReturn(competitorAndBoatMap.keySet()); + when(mockedRaceDefinition.getCompetitorsAndTheirBoats()).thenReturn(competitorAndBoatMap); + Boat boat = competitorAndBoatMap.get(competitorAndBoatMap.keySet().iterator().next()); + when(mockedRaceDefinition.getBoatOfCompetitor(any())).thenReturn(boat); return mockedRaceDefinition; } - private Iterable createCompetitorSet(BoatClass boatClass) { - Set competitors = new HashSet<>(); - DynamicBoat mockedBoat = mock(DynamicBoat.class); - when(mockedBoat.getBoatClass()).thenReturn(boatClass); - competitors.add(new CompetitorImpl(UUID.randomUUID(), "TestCompetitor", Color.BLACK, null, null, - mock(DynamicTeam.class), mockedBoat, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null)); + private Map createCompetitorAndBoatMap(BoatClass boatClass) { + Map competitors = new HashMap<>(); + Competitor c = new CompetitorImpl(UUID.randomUUID(), "TestCompetitor", "BYC", Color.BLACK, null, null, + mock(DynamicTeam.class), + /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null); + Boat b = new BoatImpl("Boot", "b", boatClass, null); + competitors.put(c, b); return competitors; } diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LeaderboardOfflineTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LeaderboardOfflineTest.java index 0593214c8ac..93f5583af5d 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LeaderboardOfflineTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LeaderboardOfflineTest.java @@ -24,6 +24,7 @@ import org.junit.Before; import org.junit.Test; 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; @@ -48,22 +49,22 @@ import com.sap.sse.common.impl.MillisecondsTimePoint; public class LeaderboardOfflineTest extends AbstractLeaderboardTest { private Set testRaces; private Map raceColumnsInLeaderboard; - private Competitor competitor; + private CompetitorWithBoat competitorWithBoat; @Before public void setUp() { - competitor = createCompetitor("Wolfgang Hunger"); + competitorWithBoat = createCompetitorWithBoat("Wolfgang Hunger"); } public void setupRaces(int numberOfStartedRaces, int numberOfNotStartedRaces) { testRaces = new HashSet(); raceColumnsInLeaderboard = new HashMap(); for (int i=0; i(); raceColumnsInLeaderboard = new HashMap(); - Competitor c2 = createCompetitor("Marcus Baur"); - Competitor c3 = createCompetitor("Robert Stanjek"); + CompetitorWithBoat c2 = createCompetitorWithBoat("Marcus Baur"); + CompetitorWithBoat c3 = createCompetitorWithBoat("Robert Stanjek"); for (int i=0; i<3; i++) { - MockedTrackedRaceWithFixedRankAndManyCompetitors r = new MockedTrackedRaceWithFixedRankAndManyCompetitors(competitor, i+1, /* started */ true); - r.addCompetitor(c2); - r.addCompetitor(c3); // this makes maxPoints==4 + MockedTrackedRaceWithFixedRankAndManyCompetitors r = new MockedTrackedRaceWithFixedRankAndManyCompetitors(competitorWithBoat, i+1, /* started */ true); + r.addCompetitorWithBoat(c2); + r.addCompetitorWithBoat(c3); // this makes maxPoints==4 testRaces.add(r); // hash set should take care of more or less randomly permuting the races } FlexibleLeaderboard leaderboard = new FlexibleLeaderboardImpl("Test Leaderboard", new ThresholdBasedResultDiscardingRuleImpl( @@ -185,80 +186,80 @@ public class LeaderboardOfflineTest extends AbstractLeaderboardTest { i++; RaceColumn raceColumn = leaderboard.addRace(race, "Test Race " + i, /* medalRace */false); raceColumnsInLeaderboard.put(race, raceColumn); - if (race.getRank(competitor) < bestScore) { - bestScore = race.getRank(competitor); + if (race.getRank(competitorWithBoat) < bestScore) { + bestScore = race.getRank(competitorWithBoat); bestScoringRaceColumn = raceColumn; } } - leaderboard.getScoreCorrection().setMaxPointsReason(competitor, bestScoringRaceColumn, MaxPointsReason.DSQ); + leaderboard.getScoreCorrection().setMaxPointsReason(competitorWithBoat, bestScoringRaceColumn, MaxPointsReason.DSQ); // assert that best scoring but now disqualified race gets max total points because of disqualification: assertEquals(Util.size(bestScoringRaceColumn.getTrackedRace(defaultFleet).getRace().getCompetitors())+1, - leaderboard.getEntry(competitor, bestScoringRaceColumn, MillisecondsTimePoint.now()).getTotalPoints(), 0.000000001); + leaderboard.getEntry(competitorWithBoat, bestScoringRaceColumn, MillisecondsTimePoint.now()).getTotalPoints(), 0.000000001); // now assert that it gets discarded because due to disqualification it scores worse than all others: - assertEquals(0, leaderboard.getEntry(competitor, bestScoringRaceColumn, MillisecondsTimePoint.now()).getNetPoints(), 0.000000001); + assertEquals(0, leaderboard.getEntry(competitorWithBoat, bestScoringRaceColumn, MillisecondsTimePoint.now()).getNetPoints(), 0.000000001); } @Test public void testCarriedPointsCountInSorting() throws NoWindException { raceColumnsInLeaderboard = new HashMap(); - Competitor c2 = createCompetitor("Marcus Baur"); - Competitor c3 = createCompetitor("Robert Stanjek"); + CompetitorWithBoat c2 = createCompetitorWithBoat("Marcus Baur"); + CompetitorWithBoat c3 = createCompetitorWithBoat("Robert Stanjek"); TimePoint now = MillisecondsTimePoint.now(); MockedTrackedRaceWithFixedRankAndManyCompetitors testRace = new MockedTrackedRaceWithFixedRankAndManyCompetitors( - competitor, /* rank */ 1, /* started */true); - testRace.addCompetitor(c2); - testRace.addCompetitor(c3); // this makes maxPoints==4 + competitorWithBoat, /* rank */ 1, /* started */true); + testRace.addCompetitorWithBoat(c2); + testRace.addCompetitorWithBoat(c3); // this makes maxPoints==4 FlexibleLeaderboard leaderboard = new FlexibleLeaderboardImpl("Test Leaderboard", new ThresholdBasedResultDiscardingRuleImpl( new int[] { 2 }), new LowPoint(), null); leaderboard.addRace(testRace, "R1", /* medalRace */false); - assertEquals(1., leaderboard.getNetPoints(competitor, now), 0.00000001); + assertEquals(1., leaderboard.getNetPoints(competitorWithBoat, now), 0.00000001); assertEquals(1., leaderboard.getNetPoints(c2, now), 0.00000001); assertEquals(1., leaderboard.getNetPoints(c3, now), 0.00000001); - leaderboard.setCarriedPoints(competitor, 100); + leaderboard.setCarriedPoints(competitorWithBoat, 100); leaderboard.setCarriedPoints(c2, 50); leaderboard.setCarriedPoints(c3, 25); - assertEquals(101., leaderboard.getNetPoints(competitor, now), 0.00000001); + assertEquals(101., leaderboard.getNetPoints(competitorWithBoat, now), 0.00000001); assertEquals(51., leaderboard.getNetPoints(c2, now), 0.00000001); assertEquals(26., leaderboard.getNetPoints(c3, now), 0.00000001); List sortedCompetitors = leaderboard.getCompetitorsFromBestToWorst(now); assertSame(c3, sortedCompetitors.get(0)); assertSame(c2, sortedCompetitors.get(1)); - assertSame(competitor, sortedCompetitors.get(2)); + assertSame(competitorWithBoat, sortedCompetitors.get(2)); } @Test public void testDNDNotDiscardedInUntrackedRace() throws NoWindException { raceColumnsInLeaderboard = new HashMap(); - Competitor c2 = createCompetitor("Marcus Baur"); - Competitor c3 = createCompetitor("Robert Stanjek"); + CompetitorWithBoat c2 = createCompetitorWithBoat("Marcus Baur"); + CompetitorWithBoat c3 = createCompetitorWithBoat("Robert Stanjek"); MockedTrackedRaceWithFixedRankAndManyCompetitors testRace = new MockedTrackedRaceWithFixedRankAndManyCompetitors( - competitor, /* rank */ 1, /* started */true); - testRace.addCompetitor(c2); - testRace.addCompetitor(c3); // this makes maxPoints==4 + competitorWithBoat, /* rank */ 1, /* started */true); + testRace.addCompetitorWithBoat(c2); + testRace.addCompetitorWithBoat(c3); // this makes maxPoints==4 FlexibleLeaderboard leaderboard = new FlexibleLeaderboardImpl("Test Leaderboard", new ThresholdBasedResultDiscardingRuleImpl( new int[] { 2 }), new LowPoint(), null); RaceColumn r1 = leaderboard.addRace(testRace, "R1", /* medalRace */false); raceColumnsInLeaderboard.put(testRace, r1); RaceColumn r2 = leaderboard.addRaceColumn("R2", /* medalRace */false); - leaderboard.getScoreCorrection().setMaxPointsReason(competitor, r2, MaxPointsReason.DND); // non-discardable disqualification + leaderboard.getScoreCorrection().setMaxPointsReason(competitorWithBoat, r2, MaxPointsReason.DND); // non-discardable disqualification // assert that max points were given before discarding... - assertEquals(4.0, leaderboard.getEntry(competitor, r2, MillisecondsTimePoint.now()).getTotalPoints(), 0.000000001); + assertEquals(4.0, leaderboard.getEntry(competitorWithBoat, r2, MillisecondsTimePoint.now()).getTotalPoints(), 0.000000001); // ...and after... - assertEquals(4.0, leaderboard.getEntry(competitor, r2, MillisecondsTimePoint.now()).getNetPoints(), 0.000000001); + assertEquals(4.0, leaderboard.getEntry(competitorWithBoat, r2, MillisecondsTimePoint.now()).getNetPoints(), 0.000000001); // ...because it's not discarded - assertFalse(leaderboard.getEntry(competitor, r2, MillisecondsTimePoint.now()).isDiscarded()); + assertFalse(leaderboard.getEntry(competitorWithBoat, r2, MillisecondsTimePoint.now()).isDiscarded()); } @Test public void testNoDNDDiscard() throws NoWindException { testRaces = new HashSet(); raceColumnsInLeaderboard = new HashMap(); - Competitor c2 = createCompetitor("Marcus Baur"); - Competitor c3 = createCompetitor("Robert Stanjek"); + CompetitorWithBoat c2 = createCompetitorWithBoat("Marcus Baur"); + CompetitorWithBoat c3 = createCompetitorWithBoat("Robert Stanjek"); for (int i=0; i<3; i++) { - MockedTrackedRaceWithFixedRankAndManyCompetitors r = new MockedTrackedRaceWithFixedRankAndManyCompetitors(competitor, i+1, /* started */ true); - r.addCompetitor(c2); - r.addCompetitor(c3); // this makes maxPoints==4 + MockedTrackedRaceWithFixedRankAndManyCompetitors r = new MockedTrackedRaceWithFixedRankAndManyCompetitors(competitorWithBoat, i+1, /* started */ true); + r.addCompetitorWithBoat(c2); + r.addCompetitorWithBoat(c3); // this makes maxPoints==4 testRaces.add(r); // hash set should take care of more or less randomly permuting the races } FlexibleLeaderboard leaderboard = new FlexibleLeaderboardImpl("Test Leaderboard", new ThresholdBasedResultDiscardingRuleImpl( @@ -271,18 +272,18 @@ public class LeaderboardOfflineTest extends AbstractLeaderboardTest { i++; RaceColumn raceColumn = leaderboard.addRace(race, "Test Race " + i, /* medalRace */false); raceColumnsInLeaderboard.put(race, raceColumn); - if (race.getRank(competitor) < bestScore) { - bestScore = race.getRank(competitor); + if (race.getRank(competitorWithBoat) < bestScore) { + bestScore = race.getRank(competitorWithBoat); bestScoringRaceColumn = raceColumn; } } - leaderboard.getScoreCorrection().setMaxPointsReason(competitor, bestScoringRaceColumn, MaxPointsReason.DND); + leaderboard.getScoreCorrection().setMaxPointsReason(competitorWithBoat, bestScoringRaceColumn, MaxPointsReason.DND); // assert that best scoring but now disqualified race gets max total points because of disqualification: assertEquals(Util.size(bestScoringRaceColumn.getTrackedRace(defaultFleet).getRace().getCompetitors())+1, - leaderboard.getEntry(competitor, bestScoringRaceColumn, MillisecondsTimePoint.now()).getTotalPoints(), 0.000000001); + leaderboard.getEntry(competitorWithBoat, bestScoringRaceColumn, MillisecondsTimePoint.now()).getTotalPoints(), 0.000000001); // now assert that it does not get discarded because it's a DND assertEquals(Util.size(bestScoringRaceColumn.getTrackedRace(defaultFleet).getRace().getCompetitors())+1, - leaderboard.getEntry(competitor, bestScoringRaceColumn, MillisecondsTimePoint.now()).getNetPoints(), 0.000000001); + leaderboard.getEntry(competitorWithBoat, bestScoringRaceColumn, MillisecondsTimePoint.now()).getNetPoints(), 0.000000001); } protected void testLeaderboard(int numberOfStartedRaces, int numberOfNotStartedRaces, int firstDiscardingThreshold, @@ -303,7 +304,7 @@ public class LeaderboardOfflineTest extends AbstractLeaderboardTest { leaderboard.addRaceColumn("Test Race "+i, /* medalRace */ addOneMedalRace && j == numberOfUntrackedRaces-1); } if (carry != null) { - leaderboard.setCarriedPoints(competitor, carry); + leaderboard.setCarriedPoints(competitorWithBoat, carry); } List ranksOfNonMedalStartedRaces = new ArrayList(); TimePoint now = MillisecondsTimePoint.now(); @@ -312,25 +313,25 @@ public class LeaderboardOfflineTest extends AbstractLeaderboardTest { if (column.getTrackedRace(defaultFleet) != null && column.getTrackedRace(defaultFleet).hasStarted(now)) { numberOfRacesToCompareToDiscardThresholds++; if (!column.isMedalRace()) { - ranksOfNonMedalStartedRaces.add(column.getTrackedRace(defaultFleet).getRank(competitor, now)); + ranksOfNonMedalStartedRaces.add(column.getTrackedRace(defaultFleet).getRank(competitorWithBoat, now)); } } } Collections.sort(ranksOfNonMedalStartedRaces); int carryInt = (carry == null ? 0 : carry); int netPoints = carryInt; - int medalRacePoints = getMedalRacePoints(competitor, now, defaultFleet); + int medalRacePoints = getMedalRacePoints(competitorWithBoat, now, defaultFleet); for (TrackedRace race : testRaces) { RaceColumn raceColumn = raceColumnsInLeaderboard.get(race); - com.sap.sse.common.Util.Pair key = new com.sap.sse.common.Util.Pair(competitor, raceColumn); + com.sap.sse.common.Util.Pair key = new com.sap.sse.common.Util.Pair(competitorWithBoat, raceColumn); if (race.hasStarted(now)) { - int rank = race.getRank(competitor, now); - assertEquals(rank, leaderboard.getTrackedRank(competitor, raceColumn, now)); + int rank = race.getRank(competitorWithBoat, now); + assertEquals(rank, leaderboard.getTrackedRank(competitorWithBoat, raceColumn, now)); assertEquals(rank, leaderboard.getContent(now).get(key).getTrackedRank()); - assertEquals(rank, leaderboard.getEntry(competitor, raceColumn, now).getTrackedRank()); - assertEquals(rank, leaderboard.getTotalPoints(competitor, raceColumn, now), 0.000000001); + assertEquals(rank, leaderboard.getEntry(competitorWithBoat, raceColumn, now).getTrackedRank()); + assertEquals(rank, leaderboard.getTotalPoints(competitorWithBoat, raceColumn, now), 0.000000001); assertEquals(rank, leaderboard.getContent(now).get(key).getTotalPoints(), 0.000000001); - assertEquals(rank, leaderboard.getEntry(competitor, raceColumn, now).getTotalPoints(), 0.000000001); + assertEquals(rank, leaderboard.getEntry(competitorWithBoat, raceColumn, now).getTotalPoints(), 0.000000001); // One race is discarded because four races were started, and for [3-6) one race can be discarded. // The discarded race is the worst of those started, so the one with rank 4. int expectedNumberOfDiscardedRaces = @@ -338,21 +339,21 @@ public class LeaderboardOfflineTest extends AbstractLeaderboardTest { numberOfRacesToCompareToDiscardThresholds < secondDiscardingThreshold ? 1 : 2; boolean discarded = ranksOfNonMedalStartedRaces.indexOf(rank) >= ranksOfNonMedalStartedRaces.size()-expectedNumberOfDiscardedRaces; int expected = discarded ? 0 : rank==medalRacePoints?2*rank:rank; - assertEquals(expected, leaderboard.getNetPoints(competitor, raceColumn, now), 0.000000001); + assertEquals(expected, leaderboard.getNetPoints(competitorWithBoat, raceColumn, now), 0.000000001); assertEquals(expected, leaderboard.getContent(now).get(key).getNetPoints(), 0.000000001); - assertEquals(expected, leaderboard.getEntry(competitor, raceColumn, now).getNetPoints(), 0.000000001); + assertEquals(expected, leaderboard.getEntry(competitorWithBoat, raceColumn, now).getNetPoints(), 0.000000001); netPoints += leaderboard.getContent(now).get(key).getNetPoints(); } else { - assertEquals(0, leaderboard.getTrackedRank(competitor, raceColumn, now)); - assertNull(leaderboard.getTotalPoints(competitor, raceColumn, now)); + assertEquals(0, leaderboard.getTrackedRank(competitorWithBoat, raceColumn, now)); + assertNull(leaderboard.getTotalPoints(competitorWithBoat, raceColumn, now)); assertEquals(0, leaderboard.getContent(now).get(key).getTrackedRank()); assertNull(leaderboard.getContent(now).get(key).getTotalPoints()); - assertEquals(0, leaderboard.getEntry(competitor, raceColumn, now).getTrackedRank()); - assertNull(leaderboard.getEntry(competitor, raceColumn, now).getTotalPoints()); + assertEquals(0, leaderboard.getEntry(competitorWithBoat, raceColumn, now).getTrackedRank()); + assertNull(leaderboard.getEntry(competitorWithBoat, raceColumn, now).getTotalPoints()); // no increment on netPoints } } - assertEquals(netPoints, leaderboard.getNetPoints(competitor, now), 0.000000001); + assertEquals(netPoints, leaderboard.getNetPoints(competitorWithBoat, now), 0.000000001); } private int getMedalRacePoints(Competitor competitor, TimePoint at, Fleet fleet) throws NoWindException { diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LeaderboardScoringAndRankingTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LeaderboardScoringAndRankingTest.java index a4ea30c26cc..282b3dc6838 100644 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LeaderboardScoringAndRankingTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LeaderboardScoringAndRankingTest.java @@ -1713,7 +1713,8 @@ public class LeaderboardScoringAndRankingTest extends LeaderboardScoringAndRanki public void testAdditionalScoreInformationLeadsToChangedScoreForOneColumn() throws NoWindException { TrackedRegattaRegistry trackedRegattaRegistry = mock(TrackedRegattaRegistry.class); Regatta dummyRegatta = new RegattaImpl(EmptyRaceLogStore.INSTANCE, EmptyRegattaLogStore.INSTANCE, "Dummy", - new BoatClassImpl("Extreme40", false), /*startDate*/ null, /*endDate*/ null, trackedRegattaRegistry, + new BoatClassImpl("Extreme40", false), + /* canBoatsOfCompetitorsChangePerRace */ true, /*startDate*/ null, /*endDate*/ null, trackedRegattaRegistry, new HighPointFirstGets10Or8AndLastBreaksTie(), "578876345345", new CourseAreaImpl("Humba", UUID.randomUUID())); trackedRegattaRegistry.getOrCreateTrackedRegatta(dummyRegatta); @@ -2324,7 +2325,8 @@ public class LeaderboardScoringAndRankingTest extends LeaderboardScoringAndRanki series.add(finalSeries); } final BoatClass boatClass = DomainFactory.INSTANCE.getOrCreateBoatClass("Extreme40", /* typicallyStartsUpwind */ false); - Regatta regatta = new RegattaImpl(RegattaImpl.getDefaultName("Test Regatta", boatClass.getName()), boatClass, /*startDate*/ null, /*endDate*/ null, + Regatta regatta = new RegattaImpl(RegattaImpl.getDefaultName("Test Regatta", boatClass.getName()), boatClass, + /* canBoatsOfCompetitorsChangePerRace */ true, /*startDate*/ null, /*endDate*/ null, series, /* persistent */false, DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.HIGH_POINT_FIRST_GETS_TEN), "123", /* course area */null, OneDesignRankingMetric::new); List competitors = createCompetitors(12); final int firstYellowCompetitorIndex = 3; @@ -2442,7 +2444,8 @@ public class LeaderboardScoringAndRankingTest extends LeaderboardScoringAndRanki series.add(medalSeries); } final BoatClass boatClass = DomainFactory.INSTANCE.getOrCreateBoatClass("470", /* typicallyStartsUpwind */ true); - Regatta regatta = new RegattaImpl(RegattaImpl.getDefaultName("Test Regatta", boatClass.getName()), boatClass, /*startDate*/ null, /*endDate*/ null, + Regatta regatta = new RegattaImpl(RegattaImpl.getDefaultName("Test Regatta", boatClass.getName()), boatClass, + /* canBoatsOfCompetitorsChangePerRace */ true, /*startDate*/ null, /*endDate*/ null, series, /* persistent */false, DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), "123", /* course area */null, OneDesignRankingMetric::new); List competitors = createCompetitors(12); final int firstYellowCompetitorIndex = 3; @@ -2554,7 +2557,8 @@ public class LeaderboardScoringAndRankingTest extends LeaderboardScoringAndRanki series.add(finalSeries); } final BoatClass boatClass = DomainFactory.INSTANCE.getOrCreateBoatClass("470", /* typicallyStartsUpwind */ true); - Regatta regatta = new RegattaImpl(RegattaImpl.getDefaultName("Test Regatta", boatClass.getName()), boatClass, /*startDate*/ null, /*endDate*/ null, + Regatta regatta = new RegattaImpl(RegattaImpl.getDefaultName("Test Regatta", boatClass.getName()), boatClass, + /* canBoatsOfCompetitorsChangePerRace */ true, /*startDate*/ null, /*endDate*/ null, series, /* persistent */false, DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), "123", /* course area */null, OneDesignRankingMetric::new); List competitors = createCompetitors(12); final int firstYellowCompetitorIndex = 3; @@ -2642,7 +2646,8 @@ public class LeaderboardScoringAndRankingTest extends LeaderboardScoringAndRanki series.add(finalSeries); } final BoatClass boatClass = DomainFactory.INSTANCE.getOrCreateBoatClass("470", /* typicallyStartsUpwind */ true); - Regatta regatta = new RegattaImpl(RegattaImpl.getDefaultName("Test Regatta", boatClass.getName()), boatClass, /*startDate*/ null, /*endDate*/ null, + Regatta regatta = new RegattaImpl(RegattaImpl.getDefaultName("Test Regatta", boatClass.getName()), boatClass, + /* canBoatsOfCompetitorsChangePerRace */ true, /*startDate*/ null, /*endDate*/ null, series, /* persistent */false, DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), "123", /* course area */null, OneDesignRankingMetric::new); final int TOTAL_NUMBER_OF_COMPETITORS = 100; List competitors = createCompetitors(TOTAL_NUMBER_OF_COMPETITORS); diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LeaderboardScoringAndRankingTestBase.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LeaderboardScoringAndRankingTestBase.java index 4352a704ceb..0f9f4b1a667 100644 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LeaderboardScoringAndRankingTestBase.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LeaderboardScoringAndRankingTestBase.java @@ -107,7 +107,7 @@ public class LeaderboardScoringAndRankingTestBase extends AbstractLeaderboardTes public static List createCompetitors(int numberOfCompetitorsToCreate) { List result = new ArrayList(); for (int i=1; i<=numberOfCompetitorsToCreate; i++) { - result.add(createCompetitor("C"+i)); + result.add(createCompetitorWithBoat("C"+i)); } return result; } @@ -126,8 +126,8 @@ public class LeaderboardScoringAndRankingTestBase extends AbstractLeaderboardTes series.add(defaultSeries); Regatta regatta = new RegattaImpl(RegattaImpl.getDefaultName(regattaName, boatClass.getName()), boatClass, - /* startDate */null, /* endDate */null, series, /* persistent */false, scoringScheme, "123", null, - OneDesignRankingMetric::new); + /* canBoatsOfCompetitorsChangePerRace */ true, /* startDate */null, /* endDate */null, series, + /* persistent */false, scoringScheme, "123", null, OneDesignRankingMetric::new); return regatta; } @@ -182,7 +182,8 @@ public class LeaderboardScoringAndRankingTestBase extends AbstractLeaderboardTes } Regatta regatta = new RegattaImpl(RegattaImpl.getDefaultName(regattaBaseName, boatClass.getName()), boatClass, - /*startDate*/ null, /*endDate*/ null, series, /* persistent */ false, scoringScheme, "123", null, OneDesignRankingMetric::new); + /* canBoatsOfCompetitorsChangePerRace */ true, /*startDate*/ null, /*endDate*/ null, series, + /* persistent */ false, scoringScheme, "123", null, OneDesignRankingMetric::new); return regatta; } @@ -227,8 +228,8 @@ public class LeaderboardScoringAndRankingTestBase extends AbstractLeaderboardTes } } Regatta regatta = new RegattaImpl(RegattaImpl.getDefaultName(regattaBaseName, boatClass.getName()), boatClass, - /*startDate*/ null, /*endDate*/ null, series, /* persistent */ false, scoringScheme, - /* ID */ "123", /* course area */ null, OneDesignRankingMetric::new); + /* canBoatsOfCompetitorsChangePerRace */ true, /*startDate*/ null, /*endDate*/ null, series, + /* persistent */ false, scoringScheme, /* ID */ "123", /* course area */ null, OneDesignRankingMetric::new); return regatta; } } diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LineAnalysisTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LineAnalysisTest.java index 5dc92520644..329ca0264bb 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LineAnalysisTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LineAnalysisTest.java @@ -2,12 +2,10 @@ package com.sap.sailing.domain.test; import static org.junit.Assert.assertEquals; -import java.util.Collections; - import org.junit.Before; import org.junit.Test; -import com.sap.sailing.domain.base.Competitor; +import com.sap.sailing.domain.base.CompetitorWithBoat; import com.sap.sailing.domain.base.Mark; import com.sap.sailing.domain.common.Bearing; import com.sap.sailing.domain.common.Distance; @@ -28,14 +26,13 @@ import com.sap.sse.common.impl.MillisecondsTimePoint; public class LineAnalysisTest extends TrackBasedTest { private TimePoint now; private DynamicTrackedRace trackedRace; - private Competitor competitor; + private CompetitorWithBoat competitor; @Before public void setUp() { - competitor = createCompetitor("Test"); - final Iterable competitorListWithOneCompetitor = Collections.singletonList(competitor); + competitor = createCompetitorWithBoat("Test"); now = MillisecondsTimePoint.now(); - trackedRace = createTestTrackedRace("Test Regatta", "Test Race", "505", competitorListWithOneCompetitor, now, /* useMarkPassingCalculator */ false); + trackedRace = createTestTrackedRace("Test Regatta", "Test Race", "505", createCompetitorAndBoatsMap(competitor), now, /* useMarkPassingCalculator */ false); } @Test diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/MultipleClassesInRegattaTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/MultipleClassesInRegattaTest.java index f48ae69da92..9e80d9ca295 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/MultipleClassesInRegattaTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/MultipleClassesInRegattaTest.java @@ -15,6 +15,7 @@ import org.junit.Before; import org.junit.Test; import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver; +import com.sap.sailing.domain.leaderboard.LeaderboardGroupResolver; import com.sap.sailing.domain.racelog.impl.EmptyRaceLogStore; import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore; import com.sap.sailing.domain.tracking.RaceTracker; @@ -55,25 +56,25 @@ public class MultipleClassesInRegattaTest { .createRaceTracker( EmptyRaceLogStore.INSTANCE, EmptyRegattaLogStore.INSTANCE, EmptyWindStore.INSTANCE, new DummyTrackedRegattaRegistry(), - mock(RaceLogResolver.class), createConnectivityParams(httpAndHost, liveURI, storedURI, courseDesignUpdateURI, + mock(RaceLogResolver.class), mock(LeaderboardGroupResolver.class), createConnectivityParams(httpAndHost, liveURI, storedURI, courseDesignUpdateURI, tracTracUsername, tracTracPassword, "cce678c8-97e6-11e0-9aed-406186cbf87c"), RaceTracker.TIMEOUT_FOR_RECEIVING_RACE_DEFINITION_IN_MILLISECONDS); kiwotest2 = domainFactory .createRaceTracker( EmptyRaceLogStore.INSTANCE, EmptyRegattaLogStore.INSTANCE, EmptyWindStore.INSTANCE, new DummyTrackedRegattaRegistry(), - mock(RaceLogResolver.class), createConnectivityParams(httpAndHost, liveURI, storedURI, courseDesignUpdateURI, + mock(RaceLogResolver.class), mock(LeaderboardGroupResolver.class), createConnectivityParams(httpAndHost, liveURI, storedURI, courseDesignUpdateURI, tracTracUsername, tracTracPassword, "11290bd6-97e7-11e0-9aed-406186cbf87c"), RaceTracker.TIMEOUT_FOR_RECEIVING_RACE_DEFINITION_IN_MILLISECONDS); kiwotest3 = domainFactory .createRaceTracker( EmptyRaceLogStore.INSTANCE, EmptyRegattaLogStore.INSTANCE, EmptyWindStore.INSTANCE, new DummyTrackedRegattaRegistry(), - mock(RaceLogResolver.class), createConnectivityParams(httpAndHost, liveURI, storedURI, courseDesignUpdateURI, + mock(RaceLogResolver.class), mock(LeaderboardGroupResolver.class), createConnectivityParams(httpAndHost, liveURI, storedURI, courseDesignUpdateURI, tracTracUsername, tracTracPassword, "39635b24-97e7-11e0-9aed-406186cbf87c"), RaceTracker.TIMEOUT_FOR_RECEIVING_RACE_DEFINITION_IN_MILLISECONDS); weym470may112014_2 = domainFactory .createRaceTracker( EmptyRaceLogStore.INSTANCE, EmptyRegattaLogStore.INSTANCE, EmptyWindStore.INSTANCE, new DummyTrackedRegattaRegistry(), - mock(RaceLogResolver.class), createConnectivityParams(httpAndHost, liveURI, storedURI, courseDesignUpdateURI, + mock(RaceLogResolver.class), mock(LeaderboardGroupResolver.class), createConnectivityParams(httpAndHost, liveURI, storedURI, courseDesignUpdateURI, tracTracUsername, tracTracPassword, "04498426-7dfd-11e0-8236-406186cbf87c"), RaceTracker.TIMEOUT_FOR_RECEIVING_RACE_DEFINITION_IN_MILLISECONDS); assertEquals("STG", kiwotest1.getRegatta().getBoatClass().getName()); diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/NonCompetingCompetitorTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/NonCompetingCompetitorTest.java index 8a7759ea7d2..70f144eda75 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/NonCompetingCompetitorTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/NonCompetingCompetitorTest.java @@ -8,10 +8,12 @@ import java.net.MalformedURLException; import java.net.URISyntaxException; import java.net.URL; import java.text.ParseException; +import java.util.Map.Entry; import org.junit.Before; import org.junit.Test; +import com.sap.sailing.domain.base.Boat; import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.common.NoWindException; import com.sap.sailing.domain.tractracadapter.ReceiverType; @@ -43,9 +45,9 @@ public class NonCompetingCompetitorTest extends AbstractManeuverDetectionTestCas */ @Test public void testNoNonCompetingCompetitors() throws ParseException, NoWindException { - for (final Competitor competitor : getTrackedRace().getRace().getCompetitors()) { - assertFalse(competitor.getName().startsWith("Ump")); - assertSame(getDomainFactory().getOrCreateBoatClass("GC32"), competitor.getBoat().getBoatClass()); + for (final Entry competitorAndBoatEntry : getTrackedRace().getRace().getCompetitorsAndTheirBoats().entrySet()) { + assertFalse(competitorAndBoatEntry.getKey().getName().startsWith("Ump")); + assertSame(getDomainFactory().getOrCreateBoatClass("GC32"), competitorAndBoatEntry.getValue().getBoatClass()); } } } diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/OfflineSerializationTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/OfflineSerializationTest.java index bac48cb4c0c..c9b8983bc9e 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/OfflineSerializationTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/OfflineSerializationTest.java @@ -29,7 +29,6 @@ import com.sap.sailing.domain.base.Event; import com.sap.sailing.domain.base.Mark; import com.sap.sailing.domain.base.Nationality; import com.sap.sailing.domain.base.Waypoint; -import com.sap.sailing.domain.base.impl.BoatImpl; import com.sap.sailing.domain.base.impl.CompetitorImpl; import com.sap.sailing.domain.base.impl.CourseAreaImpl; import com.sap.sailing.domain.base.impl.DomainFactoryImpl; @@ -188,12 +187,11 @@ public class OfflineSerializationTest extends AbstractSerializationTest { DomainFactory senderDomainFactory = new DomainFactoryImpl((srlid)->null); DomainFactory receiverDomainFactory = new DomainFactoryImpl((srlid)->null); String competitorName = "Tina Maximiliane Lutz"; - Competitor sendersCompetitor1 = new CompetitorImpl(123, competitorName, Color.RED, null, null, new TeamImpl("STG", Collections.singleton( + Competitor sendersCompetitor1 = new CompetitorImpl(123, competitorName, "KYC", Color.RED, null, null, new TeamImpl("STG", Collections.singleton( new PersonImpl(competitorName, senderDomainFactory.getOrCreateNationality("GER"), /* dateOfBirth */ null, "This is famous "+competitorName)), new PersonImpl("Rigo van Maas", senderDomainFactory.getOrCreateNationality("GER"), - /* dateOfBirth */null, "This is Rigo, the coach")), new BoatImpl(competitorName + "'s boat", - senderDomainFactory.getOrCreateBoatClass("470", /* typicallyStartsUpwind */ true), "GER 61"), /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null); + /* dateOfBirth */null, "This is Rigo, the coach")), /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null); Competitor receiversCompetitor1 = cloneBySerialization(sendersCompetitor1, receiverDomainFactory); Competitor receiversSecondCopyOfCompetitor1 = cloneBySerialization(sendersCompetitor1, receiverDomainFactory); assertSame(receiversCompetitor1, receiversSecondCopyOfCompetitor1); diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/OnlineTracTracBasedTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/OnlineTracTracBasedTest.java index 682a45ea3da..53d5c170ac0 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/OnlineTracTracBasedTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/OnlineTracTracBasedTest.java @@ -29,6 +29,7 @@ import com.sap.sailing.domain.common.Position; import com.sap.sailing.domain.common.TrackedRaceStatusEnum; import com.sap.sailing.domain.common.impl.DegreePosition; import com.sap.sailing.domain.common.tracking.impl.GPSFixImpl; +import com.sap.sailing.domain.leaderboard.LeaderboardGroupResolver; import com.sap.sailing.domain.racelog.impl.EmptyRaceLogStore; import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore; import com.sap.sailing.domain.tracking.DynamicRaceDefinitionSet; @@ -123,7 +124,7 @@ public abstract class OnlineTracTracBasedTest extends AbstractTracTracLiveTest i ArrayList receivers = new ArrayList(); for (Receiver r : domainFactory.getUpdateReceivers(trackedRegatta, getTracTracRace(), EmptyWindStore.INSTANCE, /* delayToLiveInMillis */0l, /* simulator */null, createRaceDefinitionSet(), /* trackedRegattaRegistry */null, - mock(RaceLogResolver.class), /* courseDesignUpdateURI */null, /* tracTracUsername */null, /* tracTracPassword */ + mock(RaceLogResolver.class), mock(LeaderboardGroupResolver.class), /* courseDesignUpdateURI */null, /* tracTracUsername */null, /* tracTracPassword */ null, getEventSubscriber(), getRaceSubscriber(), /*ignoreTracTracMarkPassings*/ false, RaceTracker.TIMEOUT_FOR_RECEIVING_RACE_DEFINITION_IN_MILLISECONDS, receiverTypes)) { receivers.add(r); } diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/RaceExecutionOrderProvdiderAttachDetachTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/RaceExecutionOrderProvdiderAttachDetachTest.java index 1d178327e1f..c9939994336 100644 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/RaceExecutionOrderProvdiderAttachDetachTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/RaceExecutionOrderProvdiderAttachDetachTest.java @@ -13,6 +13,7 @@ import java.util.UUID; import org.junit.Test; import com.sap.sailing.domain.abstractlog.race.RaceLog; +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.Fleet; @@ -75,7 +76,7 @@ public class RaceExecutionOrderProvdiderAttachDetachTest extends TrackBasedTest @Test public void testRaceExecutionOrderProviderAttachDetachWithRaceColumn() { - trackedRace = createTestTrackedRace(REGATTA, RACE, BOATCLASS, Collections. emptyList(), + trackedRace = createTestTrackedRace(REGATTA, RACE, BOATCLASS, Collections. emptyMap(), MillisecondsTimePoint.now(), /* useMarkPassingCalculator */ false); flexibleLeaderboard = new FlexibleLeaderboardImpl(FLEXIBLELEADERBOARD, new ThresholdBasedResultDiscardingRuleImpl(new int[] { 3, 6 }), new LowPoint(), null); @@ -89,9 +90,9 @@ public class RaceExecutionOrderProvdiderAttachDetachTest extends TrackBasedTest public void testWindInRegularIntervalWithPreviousRaceStillTracking() { final TimePoint startOfFirstRace = MillisecondsTimePoint.now(); final TimePoint startOfSecondRace = startOfFirstRace.plus(Duration.ONE_MINUTE.times(5)); - DynamicTrackedRaceImpl previousTrackedRace = createTestTrackedRace(REGATTA, RACE, BOATCLASS, Collections. emptyList(), startOfFirstRace, /* useMarkPassingCalculator */ false); + DynamicTrackedRaceImpl previousTrackedRace = createTestTrackedRace(REGATTA, RACE, BOATCLASS, Collections. emptyMap(), startOfFirstRace, /* useMarkPassingCalculator */ false); previousTrackedRace.setStartOfTrackingReceived(startOfFirstRace); - trackedRace = createTestTrackedRace(REGATTA, "TestRace2", BOATCLASS, Collections. emptyList(), startOfSecondRace, /* useMarkPassingCalculator */ false); + trackedRace = createTestTrackedRace(REGATTA, "TestRace2", BOATCLASS, Collections. emptyMap(), startOfSecondRace, /* useMarkPassingCalculator */ false); flexibleLeaderboard = new FlexibleLeaderboardImpl(FLEXIBLELEADERBOARD, new ThresholdBasedResultDiscardingRuleImpl(new int[] { 3, 6 }), new LowPoint(), null); flexibleLeaderboard.addRace(previousTrackedRace, RACECOLUMN_FLEXIBLELEADERBOARD+"1", false); @@ -107,10 +108,10 @@ public class RaceExecutionOrderProvdiderAttachDetachTest extends TrackBasedTest final TimePoint startOfFirstRace = MillisecondsTimePoint.now(); final TimePoint endOfFirstRace = startOfFirstRace.plus(Duration.ONE_SECOND); final TimePoint startOfSecondRace = startOfFirstRace.plus(Duration.ONE_HOUR); - DynamicTrackedRaceImpl previousTrackedRace = createTestTrackedRace(REGATTA, RACE, BOATCLASS, Collections. emptyList(), startOfFirstRace, /* useMarkPassingCalculator */ false); + DynamicTrackedRaceImpl previousTrackedRace = createTestTrackedRace(REGATTA, RACE, BOATCLASS, Collections. emptyMap(), startOfFirstRace, /* useMarkPassingCalculator */ false); previousTrackedRace.setStartOfTrackingReceived(startOfFirstRace); previousTrackedRace.setEndOfTrackingReceived(endOfFirstRace); // a very short race... - trackedRace = createTestTrackedRace(REGATTA, "TestRace2", BOATCLASS, Collections. emptyList(), startOfSecondRace, /* useMarkPassingCalculator */ false); + trackedRace = createTestTrackedRace(REGATTA, "TestRace2", BOATCLASS, Collections. emptyMap(), startOfSecondRace, /* useMarkPassingCalculator */ false); flexibleLeaderboard = new FlexibleLeaderboardImpl(FLEXIBLELEADERBOARD, new ThresholdBasedResultDiscardingRuleImpl(new int[] { 3, 6 }), new LowPoint(), null); flexibleLeaderboard.addRace(previousTrackedRace, RACECOLUMN_FLEXIBLELEADERBOARD+"1", false); @@ -129,7 +130,7 @@ public class RaceExecutionOrderProvdiderAttachDetachTest extends TrackBasedTest final TimePoint startOfFirstRace = MillisecondsTimePoint.now(); final TimePoint endOfFirstRace = startOfFirstRace.plus(Duration.ONE_SECOND); final TimePoint startOfSecondRace = startOfFirstRace.plus(Duration.ONE_HOUR); - trackedRace = createTestTrackedRace(REGATTA, "TestRace2", BOATCLASS, Collections. emptyList(), startOfSecondRace, /* useMarkPassingCalculator */ false); + trackedRace = createTestTrackedRace(REGATTA, "TestRace2", BOATCLASS, Collections. emptyMap(), startOfSecondRace, /* useMarkPassingCalculator */ false); flexibleLeaderboard = new FlexibleLeaderboardImpl(FLEXIBLELEADERBOARD, new ThresholdBasedResultDiscardingRuleImpl(new int[] { 3, 6 }), new LowPoint(), null); flexibleLeaderboard.addRace(trackedRace, RACECOLUMN_FLEXIBLELEADERBOARD, false); @@ -145,10 +146,10 @@ public class RaceExecutionOrderProvdiderAttachDetachTest extends TrackBasedTest final TimePoint startOfFirstRace = MillisecondsTimePoint.now(); final TimePoint endOfFirstRace = startOfFirstRace.plus(Duration.ONE_SECOND); final TimePoint startOfSecondRace = startOfFirstRace.plus(TrackedRaceImpl.EXTRA_LONG_TIME_BEFORE_START_TO_TRACK_WIND_MILLIS.times(2)); - DynamicTrackedRaceImpl previousTrackedRace = createTestTrackedRace(REGATTA, RACE, BOATCLASS, Collections. emptyList(), startOfFirstRace, /* useMarkPassingCalculator */ false); + DynamicTrackedRaceImpl previousTrackedRace = createTestTrackedRace(REGATTA, RACE, BOATCLASS, Collections. emptyMap(), startOfFirstRace, /* useMarkPassingCalculator */ false); previousTrackedRace.setStartOfTrackingReceived(startOfFirstRace); previousTrackedRace.setEndOfTrackingReceived(endOfFirstRace); // a very short race... - trackedRace = createTestTrackedRace(REGATTA, "TestRace2", BOATCLASS, Collections. emptyList(), startOfSecondRace, /* useMarkPassingCalculator */ false); + trackedRace = createTestTrackedRace(REGATTA, "TestRace2", BOATCLASS, Collections. emptyMap(), startOfSecondRace, /* useMarkPassingCalculator */ false); flexibleLeaderboard = new FlexibleLeaderboardImpl(FLEXIBLELEADERBOARD, new ThresholdBasedResultDiscardingRuleImpl(new int[] { 3, 6 }), new LowPoint(), null); flexibleLeaderboard.addRace(previousTrackedRace, RACECOLUMN_FLEXIBLELEADERBOARD+"1", false); @@ -166,10 +167,10 @@ public class RaceExecutionOrderProvdiderAttachDetachTest extends TrackBasedTest final TimePoint startOfFirstRace = MillisecondsTimePoint.now(); final TimePoint endOfFirstRace = startOfFirstRace.plus(Duration.ONE_SECOND); final TimePoint startOfSecondRace = startOfFirstRace.plus(TrackedRaceImpl.EXTRA_LONG_TIME_BEFORE_START_TO_TRACK_WIND_MILLIS.divide(2)); - DynamicTrackedRaceImpl previousTrackedRace = createTestTrackedRace(REGATTA, RACE, BOATCLASS, Collections. emptyList(), startOfFirstRace, /* useMarkPassingCalculator */ false); + DynamicTrackedRaceImpl previousTrackedRace = createTestTrackedRace(REGATTA, RACE, BOATCLASS, Collections. emptyMap(), startOfFirstRace, /* useMarkPassingCalculator */ false); previousTrackedRace.setStartOfTrackingReceived(startOfFirstRace); previousTrackedRace.setEndOfTrackingReceived(endOfFirstRace); // a very short race... - trackedRace = createTestTrackedRace(REGATTA, "TestRace2", BOATCLASS, Collections. emptyList(), startOfSecondRace, /* useMarkPassingCalculator */ false); + trackedRace = createTestTrackedRace(REGATTA, "TestRace2", BOATCLASS, Collections. emptyMap(), startOfSecondRace, /* useMarkPassingCalculator */ false); flexibleLeaderboard = new FlexibleLeaderboardImpl(FLEXIBLELEADERBOARD, new ThresholdBasedResultDiscardingRuleImpl(new int[] { 3, 6 }), new LowPoint(), null); flexibleLeaderboard.addRace(previousTrackedRace, RACECOLUMN_FLEXIBLELEADERBOARD+"1", false); @@ -214,7 +215,7 @@ public class RaceExecutionOrderProvdiderAttachDetachTest extends TrackBasedTest } private void createTestSetupWithRegattaAndSeries(boolean linkSeriesToRegatta) { - trackedRace = createTestTrackedRace(REGATTA, RACE, BOATCLASS, Collections. emptyList(), + trackedRace = createTestTrackedRace(REGATTA, RACE, BOATCLASS, Collections. emptyMap(), MillisecondsTimePoint.now(), /* useMarkPassingCalculator */ false); fleet = new FleetImpl(FLEET); Set fleets = new HashSet<>(); @@ -228,8 +229,8 @@ public class RaceExecutionOrderProvdiderAttachDetachTest extends TrackBasedTest raceColumnInSeries = series.addRaceColumn(RACECOLUMN_SERIES, null); ScoringScheme scoringScheme = new LowPoint(); regatta = new RegattaImpl(RegattaImpl.getDefaultName(REGATTA, boatClass.getName()), boatClass, - /* startDate */null, /* endDate */null, seriesSet, false, scoringScheme, UUID.randomUUID(), null, - OneDesignRankingMetric::new); + /* canBoatsOfCompetitorsChangePerRace */ true, /* startDate */null, /* endDate */null, + seriesSet, false, scoringScheme, UUID.randomUUID(), null, OneDesignRankingMetric::new); } /** @@ -285,7 +286,7 @@ public class RaceExecutionOrderProvdiderAttachDetachTest extends TrackBasedTest } private DynamicTrackedRace createTrackedRace(final String name, final TimePoint startOfRace, final TimePoint endOfRace) { - DynamicTrackedRace trackedRace = createTestTrackedRace(REGATTA, name, BOATCLASS, Collections. emptyList(), startOfRace, /* useMarkPassingCalculator */ false); + DynamicTrackedRace trackedRace = createTestTrackedRace(REGATTA, name, BOATCLASS, Collections. emptyMap(), startOfRace, /* useMarkPassingCalculator */ false); trackedRace.setStartOfTrackingReceived(startOfRace); trackedRace.setEndOfTrackingReceived(endOfRace); return trackedRace; diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ReachingLegTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ReachingLegTest.java index 0ee40a3a632..10485470d91 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ReachingLegTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ReachingLegTest.java @@ -8,13 +8,17 @@ import java.util.ArrayList; import java.util.Collections; import java.util.GregorianCalendar; import java.util.Iterator; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import org.junit.Before; import org.junit.Test; import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver; +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.Course; import com.sap.sailing.domain.base.DomainFactory; @@ -24,7 +28,6 @@ import com.sap.sailing.domain.base.Regatta; import com.sap.sailing.domain.base.Sideline; import com.sap.sailing.domain.base.Waypoint; import com.sap.sailing.domain.base.impl.BoatClassImpl; -import com.sap.sailing.domain.base.impl.CompetitorImpl; import com.sap.sailing.domain.base.impl.ControlPointWithTwoMarksImpl; import com.sap.sailing.domain.base.impl.CourseImpl; import com.sap.sailing.domain.base.impl.MarkImpl; @@ -59,11 +62,11 @@ import com.sap.sse.common.TimePoint; import com.sap.sse.common.impl.MillisecondsTimePoint; public class ReachingLegTest extends TrackBasedTest { - private List competitors; - private CompetitorImpl plattner; - private CompetitorImpl hunger; + private Map competitorsAndBoats; + private CompetitorWithBoat plattner; + private CompetitorWithBoat hunger; private MillisecondsTimePoint start; - private Competitor schomaeker; + private CompetitorWithBoat schomaeker; /** * Creates the race and two competitors ({@link #plattner} and {@link #hunger}) and sets the start line passing for both of them @@ -71,15 +74,15 @@ public class ReachingLegTest extends TrackBasedTest { */ @Before public void setUp() { - competitors = new ArrayList(); - hunger = createCompetitor("Wolfgang Hunger"); - competitors.add(hunger); - plattner = createCompetitor("Dr. Hasso Plattner"); - competitors.add(plattner); - schomaeker = createCompetitor("Meike Schom�ker"); - competitors.add(schomaeker); + competitorsAndBoats = new LinkedHashMap<>(); + hunger = createCompetitorWithBoat("Wolfgang Hunger"); + competitorsAndBoats.put(hunger, hunger.getBoat()); + plattner = createCompetitorWithBoat("Dr. Hasso Plattner"); + competitorsAndBoats.put(plattner, plattner.getBoat()); + schomaeker = createCompetitorWithBoat("Meike Schom�ker"); + competitorsAndBoats.put(schomaeker, schomaeker.getBoat()); start = new MillisecondsTimePoint(new GregorianCalendar(2011, 05, 23).getTime()); - setTrackedRace(createTrackedRace("Kieler Woche", "505 Race 2", "505", competitors, start)); + setTrackedRace(createTrackedRace("Kieler Woche", "505 Race 2", "505", competitorsAndBoats, start)); List hungersMarkPassings = createMarkPassings(hunger, start); getTrackedRace().updateMarkPassings(hunger, hungersMarkPassings); List plattnersMarkPassings = createMarkPassings(plattner, start); @@ -87,10 +90,11 @@ public class ReachingLegTest extends TrackBasedTest { } protected DynamicTrackedRaceImpl createTrackedRace(String regattaName, String raceName, String boatClassName, - Iterable competitors, TimePoint timePointForFixes) { + Map competitorsAndBoats, TimePoint timePointForFixes) { BoatClassImpl boatClass = new BoatClassImpl(boatClassName, /* typicallyStartsUpwind */ true); Regatta regatta = new RegattaImpl(EmptyRaceLogStore.INSTANCE, EmptyRegattaLogStore.INSTANCE, - RegattaImpl.getDefaultName(regattaName, boatClass.getName()), boatClass, /*startDate*/ null, /*endDate*/ null, /* trackedRegattaRegistry */ null, + RegattaImpl.getDefaultName(regattaName, boatClass.getName()), boatClass, + /* canBoatsOfCompetitorsChangePerRace */ true, /*startDate*/ null, /*endDate*/ null, /* trackedRegattaRegistry */ null, DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), "123", null); TrackedRegatta trackedRegatta = new DynamicTrackedRegattaImpl(regatta); List waypoints = new ArrayList(); @@ -105,7 +109,7 @@ public class ReachingLegTest extends TrackBasedTest { waypoints.add(new WaypointImpl(offsetMark)); waypoints.add(new WaypointImpl(leeGate)); Course course = new CourseImpl(raceName, waypoints); - RaceDefinition race = new RaceDefinitionImpl(raceName, course, boatClass, competitors); + RaceDefinition race = new RaceDefinitionImpl(raceName, course, boatClass, competitorsAndBoats); DynamicTrackedRaceImpl trackedRace = new DynamicTrackedRaceImpl(trackedRegatta, race, Collections. emptyList(), EmptyWindStore.INSTANCE, /* delayToLiveInMillis */ 0, /* millisecondsOverWhichToAverageWind */ 30000, /* millisecondsOverWhichToAverageSpeed */ 30000, diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ReceiveMarkPassingDataTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ReceiveMarkPassingDataTest.java index d2ae8048d0f..a69fdcdbbea 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ReceiveMarkPassingDataTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ReceiveMarkPassingDataTest.java @@ -16,6 +16,7 @@ import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver; import com.sap.sailing.domain.base.RaceDefinition; import com.sap.sailing.domain.base.Waypoint; import com.sap.sailing.domain.common.tracking.impl.GPSFixMovingImpl; +import com.sap.sailing.domain.leaderboard.LeaderboardGroupResolver; import com.sap.sailing.domain.racelog.impl.EmptyRaceLogStore; import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore; import com.sap.sailing.domain.tracking.DynamicRaceDefinitionSet; @@ -109,7 +110,7 @@ public class ReceiveMarkPassingDataTest extends AbstractTracTracLiveTest { public void addRaceDefinition(RaceDefinition race, DynamicTrackedRace trackedRace) { } }, /* trackedRegattaRegistry */null, - mock(RaceLogResolver.class), /* courseDesignUpdateURI */null, /* tracTracUsername */null, /* tracTracPassword */ + mock(RaceLogResolver.class), mock(LeaderboardGroupResolver.class), /* courseDesignUpdateURI */null, /* tracTracUsername */null, /* tracTracPassword */ null, getEventSubscriber(), getRaceSubscriber(), /*ignoreTracTracMarkPassings*/ false, RaceTracker.TIMEOUT_FOR_RECEIVING_RACE_DEFINITION_IN_MILLISECONDS, ReceiverType.RACECOURSE, ReceiverType.MARKPOSITIONS, ReceiverType.RACESTARTFINISH, ReceiverType.RAWPOSITIONS)) { receivers.add(r); diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ReceiveTrackingDataTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ReceiveTrackingDataTest.java index 855b29f764a..2f67d006701 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ReceiveTrackingDataTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ReceiveTrackingDataTest.java @@ -17,6 +17,7 @@ import com.sap.sailing.domain.base.Regatta; import com.sap.sailing.domain.common.tracking.GPSFix; import com.sap.sailing.domain.common.tracking.GPSFixMoving; import com.sap.sailing.domain.common.tracking.impl.GPSFixMovingImpl; +import com.sap.sailing.domain.leaderboard.LeaderboardGroupResolver; import com.sap.sailing.domain.racelog.impl.EmptyRaceLogStore; import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore; import com.sap.sailing.domain.tracking.DynamicRaceDefinitionSet; @@ -86,7 +87,7 @@ public class ReceiveTrackingDataTest extends AbstractTracTracLiveTest { public void addRaceDefinition(RaceDefinition race, DynamicTrackedRace trackedRace) { } }, - /* trackedRegattaRegistry */null, mock(RaceLogResolver.class), getTracTracRace(), /* courseDesignUpdateURI */ + /* trackedRegattaRegistry */null, mock(RaceLogResolver.class), mock(LeaderboardGroupResolver.class), getTracTracRace(), /* courseDesignUpdateURI */ null, /* tracTracUsername */null, /* tracTracPassword */null, getEventSubscriber(), getRaceSubscriber(), /*ignoreTracTracMarkPassings*/ false, RaceTracker.TIMEOUT_FOR_RECEIVING_RACE_DEFINITION_IN_MILLISECONDS)) { receiver.subscribe(); getRaceSubscriber().start(); @@ -99,7 +100,7 @@ public class ReceiveTrackingDataTest extends AbstractTracTracLiveTest { @Override public void addRaceDefinition(RaceDefinition race, DynamicTrackedRace trackedRace) { } - }, /* trackedRegattaRegistry */null, mock(RaceLogResolver.class), getTracTracRace(), /* courseDesignUpdateURI */ + }, /* trackedRegattaRegistry */null, mock(RaceLogResolver.class), mock(LeaderboardGroupResolver.class), getTracTracRace(), /* courseDesignUpdateURI */ null, /* tracTracUsername */null, /* tracTracPassword */null, getEventSubscriber(), getRaceSubscriber(), /*ignoreTracTracMarkPassings*/ false, RaceTracker.TIMEOUT_FOR_RECEIVING_RACE_DEFINITION_IN_MILLISECONDS)); } diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/RegattaLogEventNotificationForwardingTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/RegattaLogEventNotificationForwardingTest.java index 50f21133084..724ca4f9615 100644 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/RegattaLogEventNotificationForwardingTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/RegattaLogEventNotificationForwardingTest.java @@ -74,7 +74,7 @@ public class RegattaLogEventNotificationForwardingTest extends AbstractSerializa receivedRegattaLogEvent[0] = event; } }); - final RegattaLogRegisterCompetitorEventImpl event = createRegattaLogEvent(); + final RegattaLogRegisterCompetitorEvent event = createRegattaLogEvent(); leaderboard.getRegattaLog().add(event); assertSame(event, receivedRegattaLogEvent[0]); } @@ -96,14 +96,14 @@ public class RegattaLogEventNotificationForwardingTest extends AbstractSerializa receivedRegattaLogEvent[0] = event; } }); - final RegattaLogRegisterCompetitorEventImpl event = createRegattaLogEvent(); + final RegattaLogRegisterCompetitorEvent event = createRegattaLogEvent(); deserializedLeaderboard.getRegattaLog().add(event); assertSame(event, receivedRegattaLogEvent[0]); } - private RegattaLogRegisterCompetitorEventImpl createRegattaLogEvent() { - final RegattaLogRegisterCompetitorEventImpl event = new RegattaLogRegisterCompetitorEventImpl(MillisecondsTimePoint.now(), /* author */ null, - AbstractTracTracLiveTest.createCompetitor("Someone")); + private RegattaLogRegisterCompetitorEvent createRegattaLogEvent() { + final RegattaLogRegisterCompetitorEvent event = new RegattaLogRegisterCompetitorEventImpl(MillisecondsTimePoint.now(), /* author */ null, + AbstractLeaderboardTest.createCompetitor("Someone")); return event; } @@ -115,7 +115,7 @@ public class RegattaLogEventNotificationForwardingTest extends AbstractSerializa @Test public void testRegattaLeaderboardRegattaLogEventForwarding() { - Regatta regatta = new RegattaImpl("test", null, null, null, new HashSet(), false, null, "test", null, OneDesignRankingMetric::new); + Regatta regatta = new RegattaImpl("test", null, true, null, null, new HashSet(), false, null, "test", null, OneDesignRankingMetric::new); final RegattaLogEvent[] receivedRegattaLogEvent = new RegattaLogEvent[1]; regatta.addRaceColumnListener(new RaceColumnListenerWithDefaultAction() { private static final long serialVersionUID = -3835439531940986851L; @@ -129,14 +129,14 @@ public class RegattaLogEventNotificationForwardingTest extends AbstractSerializa receivedRegattaLogEvent[0] = event; } }); - final RegattaLogRegisterCompetitorEventImpl event = createRegattaLogEvent(); + final RegattaLogRegisterCompetitorEvent event = createRegattaLogEvent(); regatta.getRegattaLog().add(event); assertSame(event, receivedRegattaLogEvent[0]); } @Test public void testRegattaLeaderboardRegattaLogEventForwardingAfterRegattaDeserialization() throws ClassNotFoundException, IOException { - Regatta regatta = new RegattaImpl("test", null, null, null, new HashSet(), false, null, "test", null, OneDesignRankingMetric::new); + Regatta regatta = new RegattaImpl("test", null, true, null, null, new HashSet(), false, null, "test", null, OneDesignRankingMetric::new); Regatta deserializedRegatta = cloneBySerialization(regatta, DomainFactory.INSTANCE); final RegattaLogEvent[] receivedRegattaLogEvent = new RegattaLogEvent[1]; deserializedRegatta.addRaceColumnListener(new RaceColumnListenerWithDefaultAction() { @@ -151,7 +151,7 @@ public class RegattaLogEventNotificationForwardingTest extends AbstractSerializa receivedRegattaLogEvent[0] = event; } }); - final RegattaLogRegisterCompetitorEventImpl event = createRegattaLogEvent(); + final RegattaLogRegisterCompetitorEvent event = createRegattaLogEvent(); deserializedRegatta.getRegattaLog().add(event); assertSame(event, receivedRegattaLogEvent[0]); } @@ -181,7 +181,7 @@ public class RegattaLogEventNotificationForwardingTest extends AbstractSerializa Series series = new SeriesImpl("Test Series", /* isMedal */ false, /* isFleetsCanRunInParallel */ true, /* fleets */ Collections.singleton(new FleetImpl("Default")), Collections.singleton("R1"), /* trackedRegattaRegistry */ null); - Regatta regatta = new RegattaImpl("test", null, null, null, Collections.singleton(series), false, + Regatta regatta = new RegattaImpl("test", null, true, null, null, Collections.singleton(series), false, new LowPoint(), "test", null, OneDesignRankingMetric::new); return regatta; } diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/SerializeRankingMetricTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/SerializeRankingMetricTest.java index 642fb9a173e..23e70896311 100644 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/SerializeRankingMetricTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/SerializeRankingMetricTest.java @@ -9,6 +9,7 @@ import java.util.UUID; import org.junit.Test; +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; @@ -33,10 +34,10 @@ public class SerializeRankingMetricTest extends AbstractSerializationTest { final BoatClass _49er = DomainFactory.INSTANCE.getOrCreateBoatClass("49er"); final TrackedRace trackedRace = new DynamicTrackedRaceImpl(new DynamicTrackedRegattaImpl( new RegattaImpl(EmptyRaceLogStore.INSTANCE, EmptyRegattaLogStore.INSTANCE, "Regatta", /* boatClass */ _49er, - /* startDate */ null, /* endDate */ null, /* trackedRegattaRegistry */ null, - new LowPoint(), UUID.randomUUID(), /* courseArea */ null)), + /* canBoatsOfCompetitorsChangePerRace */ true, /* startDate */ null, /* endDate */ null, + /* trackedRegattaRegistry */ null, new LowPoint(), UUID.randomUUID(), /* courseArea */ null)), new RaceDefinitionImpl("Race", new CourseImpl("Course", Collections.emptyList()) , - /* boatClass */ _49er, Collections.emptySet()), + /* boatClass */ _49er, Collections.emptyMap()), Collections. emptyList(), EmptyWindStore.INSTANCE, 0l, 0l, 0l, false, TimeOnTimeAndDistanceRankingMetric::new, null); RankingMetric clone = cloneBySerialization(trackedRace.getRankingMetric(), DomainFactory.INSTANCE); diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/StoredTrackBasedTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/StoredTrackBasedTest.java index 0ad2f62f9d6..177c7b75e5a 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/StoredTrackBasedTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/StoredTrackBasedTest.java @@ -16,6 +16,8 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; +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.impl.BoatClassImpl; import com.sap.sailing.domain.base.impl.BoatImpl; @@ -40,10 +42,10 @@ import com.sap.sse.common.impl.MillisecondsTimePoint; public abstract class StoredTrackBasedTest extends TrackBasedTest { private static final String RESOURCES = "resources/"; - protected DynamicGPSFixTrack readTrack(Competitor competitor, String regattaName) throws FileNotFoundException, IOException { + protected DynamicGPSFixTrack readTrack(Competitor competitor, Boat boat, String regattaName) throws FileNotFoundException, IOException { DynamicGPSFixTrack track = null; - if (getFile(competitor, regattaName).exists()) { - ObjectInput oi = getInputStream(competitor, regattaName); + if (getFile(competitor, boat, regattaName).exists()) { + ObjectInput oi = getInputStream(competitor, boat, regattaName); track = new DynamicGPSFixMovingTrackImpl(competitor, /* millisecondsOverWhichToAverage */ 40000); try { @@ -58,17 +60,17 @@ public abstract class StoredTrackBasedTest extends TrackBasedTest { return track; } - ObjectInput getInputStream(Competitor competitor, String regattaName) throws FileNotFoundException, IOException { - return new ObjectInputStream(new FileInputStream(getFile(competitor, regattaName))); + ObjectInput getInputStream(Competitor competitor, Boat boat, String regattaName) throws FileNotFoundException, IOException { + return new ObjectInputStream(new FileInputStream(getFile(competitor, boat, regattaName))); } - ObjectOutput getOutputStream(Competitor competitor, String regattaName) throws FileNotFoundException, IOException { - return new ObjectOutputStream(new FileOutputStream(getFile(competitor, regattaName))); + ObjectOutput getOutputStream(Competitor competitor, Boat boat, String regattaName) throws FileNotFoundException, IOException { + return new ObjectOutputStream(new FileOutputStream(getFile(competitor, boat, regattaName))); } - private File getFile(Competitor competitor, String regattaName) { + private File getFile(Competitor competitor, Boat boat, String regattaName) { return new File(RESOURCES + regattaName + "-" + competitor.getName() - + (competitor.getBoat().getSailID() == null ? "" : "-" + competitor.getBoat().getSailID())); + + (boat.getSailID() == null ? "" : "-" + boat.getSailID())); } public static ObjectInputStream getObjectInputStream(String fileNameWithinResources) throws FileNotFoundException, IOException { @@ -102,9 +104,9 @@ public abstract class StoredTrackBasedTest extends TrackBasedTest { return new GPSFixMovingImpl(position, timePoint, speedWithBearing); } - protected void storeTrack(Competitor competitor, DynamicGPSFixTrack track, String regattaName) + protected void storeTrack(Competitor competitor, Boat boat, DynamicGPSFixTrack track, String regattaName) throws FileNotFoundException, IOException { - ObjectOutput oo = getOutputStream(competitor, regattaName); + ObjectOutput oo = getOutputStream(competitor, boat, regattaName); for (GPSFixMoving fix : track.getRawFixes()) { writeGPSFixMoving(fix, oo); } @@ -117,9 +119,11 @@ public abstract class StoredTrackBasedTest extends TrackBasedTest { for (String competitorName : getCompetitorNamesOfStoredTracks(KIELER_WOCHE)) { DynamicPerson p = new PersonImpl(competitorName, /* nationality */ null, /* dateOfBirth */ null, /* description */ null); DynamicTeam t = new TeamImpl(competitorName, Collections.singleton(p), /* coach */ null); - Competitor c = new CompetitorImpl(competitorName, competitorName, Color.RED, null, null, t, new BoatImpl(competitorName, - new BoatClassImpl("505", /* typicallyStartsUpwind */ true), null), /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null); - DynamicGPSFixTrack track = readTrack(c, KIELER_WOCHE); + Competitor c = new CompetitorImpl(competitorName, competitorName, "KYC", Color.RED, null, null, t, + /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null); + BoatClass boatClass = new BoatClassImpl("505", /* typicallyStartsUpwind */ true); + Boat b = new BoatImpl("123", competitorName, boatClass, null); + DynamicGPSFixTrack track = readTrack(c, b, KIELER_WOCHE); if (track != null) { tracks.put(c, track); } diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/StoredTrackBasedTestWithTrackedRace.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/StoredTrackBasedTestWithTrackedRace.java index 366463a5700..0d9e084ca88 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/StoredTrackBasedTestWithTrackedRace.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/StoredTrackBasedTestWithTrackedRace.java @@ -4,12 +4,17 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.util.ArrayList; import java.util.GregorianCalendar; +import java.util.HashMap; import java.util.List; import java.util.Map; import org.junit.Before; +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.impl.BoatClassImpl; +import com.sap.sailing.domain.base.impl.BoatImpl; import com.sap.sailing.domain.common.tracking.GPSFixMoving; import com.sap.sailing.domain.tracking.DynamicGPSFixTrack; import com.sap.sailing.domain.tracking.MarkPassing; @@ -20,7 +25,15 @@ public class StoredTrackBasedTestWithTrackedRace extends StoredTrackBasedTest { @Before public void setUp() throws FileNotFoundException, IOException { Map> tracks = loadTracks(); - setTrackedRace(createTestTrackedRace("Kieler Woche", "505 Race 2", "505", tracks.keySet(), + + final BoatClass boatClass = new BoatClassImpl("505", /* typicallyStartsUpwind */ true); + Map competitorsAndBoats = new HashMap<>(); + int i = 1; + for(Competitor c: tracks.keySet()) { + Boat b = new BoatImpl("Boat" + i++, c.getName(), boatClass, null); + competitorsAndBoats.put(c, b); + } + setTrackedRace(createTestTrackedRace("Kieler Woche", "505 Race 2", "505", competitorsAndBoats, new MillisecondsTimePoint(new GregorianCalendar(2011, 05, 23).getTime()), /* useMarkPassingCalculator */ false)); copyTracks(tracks); } diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/TackTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/TackTest.java index fb571d09ef7..8809eb0126b 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/TackTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/TackTest.java @@ -4,10 +4,14 @@ import static org.junit.Assert.assertEquals; import java.util.Collections; import java.util.GregorianCalendar; +import java.util.HashMap; +import java.util.Map; import org.junit.Before; import org.junit.Test; +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.impl.BoatClassImpl; import com.sap.sailing.domain.base.impl.BoatImpl; @@ -35,13 +39,17 @@ public class TackTest extends StoredTrackBasedTestWithTrackedRace { @Override @Before public void setUp() { - competitor = new CompetitorImpl(123, "Wolfgang Hunger", Color.RED, null, null, new TeamImpl("STG", Collections.singleton( + BoatClass boatClass = new BoatClassImpl("505", /* typicallyStartsUpwind */ true); + competitor = new CompetitorImpl(123, "Wolfgang Hunger", "KYC", Color.RED, null, null, new TeamImpl("STG", Collections.singleton( new PersonImpl("Wolfgang Hunger", new NationalityImpl("GER"), /* dateOfBirth */null, "This is famous Wolfgang Hunger")), new PersonImpl("Rigo van Maas", new NationalityImpl("NED"), - /* dateOfBirth */null, "This is Rigo, the coach")), new BoatImpl("Wolfgang Hunger's boat", - new BoatClassImpl("505", /* typicallyStartsUpwind */ true), null), /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null); - setTrackedRace(createTestTrackedRace("Kieler Woche", "505 Race 2", "505", Collections.singleton(competitor), + /* dateOfBirth */null, "This is Rigo, the coach")), + /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null); + Boat boat = new BoatImpl(competitor.getId(), "Wolfgang Hunger's boat", boatClass, "123", null); + Map competitorsAndBoats = new HashMap<>(); + competitorsAndBoats.put(competitor, boat); + setTrackedRace(createTestTrackedRace("Kieler Woche", "505 Race 2", "505", competitorsAndBoats, new MillisecondsTimePoint(new GregorianCalendar(2011, 05, 23).getTime()), /* useMarkPassingCalculator */ false)); } diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/TestDeadlockInRegattaListener.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/TestDeadlockInRegattaListener.java index c7020e2d91e..3256751f6e4 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/TestDeadlockInRegattaListener.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/TestDeadlockInRegattaListener.java @@ -50,7 +50,7 @@ public class TestDeadlockInRegattaListener { final String regattaName = "Test Regatta"; final RegattaImpl regatta = new RegattaImpl( /* raceLogStore */ null, EmptyRegattaLogStore.INSTANCE, regattaName, - new BoatClassImpl("49er", true), /* startDate */ null, /* endDate */ null, + new BoatClassImpl("49er", true), /* can boats change */ false, /* startDate */ null, /* endDate */ null, /* trackedRegattaRegistry */ null, new LowPoint(), UUID.randomUUID(), /* courseArea */ null, /* controlTrackingFromStartAndFinishTimes */ true, OneDesignRankingMetric::new); diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/TrackBasedTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/TrackBasedTest.java index c8825ab386e..5733057968d 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/TrackBasedTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/TrackBasedTest.java @@ -7,12 +7,16 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Iterator; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import java.util.UUID; import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver; +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.ControlPoint; import com.sap.sailing.domain.base.Course; import com.sap.sailing.domain.base.CourseArea; @@ -28,9 +32,11 @@ import com.sap.sailing.domain.base.Waypoint; import com.sap.sailing.domain.base.impl.BoatClassImpl; import com.sap.sailing.domain.base.impl.BoatImpl; import com.sap.sailing.domain.base.impl.CompetitorImpl; +import com.sap.sailing.domain.base.impl.CompetitorWithBoatImpl; import com.sap.sailing.domain.base.impl.ControlPointWithTwoMarksImpl; import com.sap.sailing.domain.base.impl.CourseAreaImpl; import com.sap.sailing.domain.base.impl.CourseImpl; +import com.sap.sailing.domain.base.impl.DynamicBoat; import com.sap.sailing.domain.base.impl.FleetImpl; import com.sap.sailing.domain.base.impl.MarkImpl; import com.sap.sailing.domain.base.impl.NationalityImpl; @@ -72,6 +78,7 @@ public abstract class TrackBasedTest { private DynamicTrackedRaceImpl trackedRace; final static Fleet regattaFleet = new FleetImpl("fleet name"); + final static BoatClass boatClass = new BoatClassImpl("505", /* typicallyStartsUpwind */true); protected DynamicTrackedRaceImpl getTrackedRace() { return trackedRace; @@ -81,15 +88,26 @@ public abstract class TrackBasedTest { this.trackedRace = trackedRace; } - public static CompetitorImpl createCompetitor(String competitorName) { - return new CompetitorImpl(UUID.randomUUID(), competitorName, Color.RED, null, null, new TeamImpl("STG", Collections.singleton( + @SafeVarargs + public static Map createCompetitorAndBoatsMap(CompetitorWithBoat... competitorsWithBoats) { + Map result = new LinkedHashMap<>(); + for (CompetitorWithBoat competitorWithBoat: competitorsWithBoats) { + result.put(competitorWithBoat, competitorWithBoat.getBoat()); + } + return result; + } + + public static CompetitorWithBoat createCompetitorWithBoat(String competitorName) { + Competitor c = new CompetitorImpl(UUID.randomUUID(), competitorName, "HP", Color.RED, null, null, new TeamImpl("STG", Collections.singleton( new PersonImpl(competitorName, new NationalityImpl("GER"), /* dateOfBirth */null, "This is famous " + competitorName)), new PersonImpl("Rigo van Maas", new NationalityImpl("NED"), - /* dateOfBirth */null, "This is Rigo, the coach")), new BoatImpl(competitorName + "'s boat", - new BoatClassImpl("505", /* typicallyStartsUpwind */true), null), /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null); + /* dateOfBirth */null, "This is Rigo, the coach")), + /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null); + DynamicBoat b = new BoatImpl(c.getId(), competitorName + "'s boat", boatClass, null, null); + return new CompetitorWithBoatImpl(c, b); } - + /** * For {@link #trackedRace}'s race course, creates a list of mark passings using the time points specified, in order * for the waypoints. @@ -119,8 +137,8 @@ public abstract class TrackBasedTest { * @param useMarkPassingCalculator whether or not to use the internal mark passing calculator */ public static DynamicTrackedRaceImpl createTestTrackedRace(String regattaName, String raceName, String boatClassName, - Iterable competitors, TimePoint timePointForFixes, boolean useMarkPassingCalculator) { - return createTestTrackedRace(regattaName, raceName, boatClassName, competitors, timePointForFixes, useMarkPassingCalculator, + Map competitorsAndBoats, TimePoint timePointForFixes, boolean useMarkPassingCalculator) { + return createTestTrackedRace(regattaName, raceName, boatClassName, competitorsAndBoats, timePointForFixes, useMarkPassingCalculator, mock(RaceLogResolver.class)); } /** @@ -139,10 +157,11 @@ public abstract class TrackBasedTest { * @param useMarkPassingCalculator whether or not to use the internal mark passing calculator */ public static DynamicTrackedRaceImpl createTestTrackedRace(String regattaName, String raceName, String boatClassName, - Iterable competitors, TimePoint timePointForFixes, boolean useMarkPassingCalculator, RaceLogResolver raceLogResolver) { + Map competitorsAndBoats, TimePoint timePointForFixes, boolean useMarkPassingCalculator, RaceLogResolver raceLogResolver) { BoatClassImpl boatClass = new BoatClassImpl(boatClassName, /* typicallyStartsUpwind */ true); Regatta regatta = new RegattaImpl(EmptyRaceLogStore.INSTANCE, EmptyRegattaLogStore.INSTANCE, - RegattaImpl.getDefaultName(regattaName, boatClass.getName()), boatClass, /*startDate*/ null, /*endDate*/ null, /* trackedRegattaRegistry */ null, + RegattaImpl.getDefaultName(regattaName, boatClass.getName()), boatClass, + /* canBoatsOfCompetitorsChangePerRace */ true, /*startDate*/ null, /*endDate*/ null, /* trackedRegattaRegistry */ null, DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), "123", null); TrackedRegatta trackedRegatta = new DynamicTrackedRegattaImpl(regatta); List waypoints = new ArrayList(); @@ -157,7 +176,7 @@ public abstract class TrackBasedTest { waypoints.add(new WaypointImpl(windwardMark)); waypoints.add(new WaypointImpl(leeGate)); Course course = new CourseImpl(raceName, waypoints); - RaceDefinition race = new RaceDefinitionImpl(raceName, course, boatClass, competitors); + RaceDefinition race = new RaceDefinitionImpl(raceName, course, boatClass, competitorsAndBoats); DynamicTrackedRaceImpl trackedRace = new DynamicTrackedRaceImpl(trackedRegatta, race, Collections. emptyList(), EmptyWindStore.INSTANCE, /* delayToLiveInMillis */ 0, /* millisecondsOverWhichToAverageWind */ 30000, /* millisecondsOverWhichToAverageSpeed */ 30000, @@ -192,7 +211,8 @@ public abstract class TrackBasedTest { TrackedRegattaRegistry trackedRegattaRegistry = mock(TrackedRegattaRegistry.class); Series series = new SeriesImpl("series name", isMedal, /* isFleetsCanRunInParallel */ true, regattaFleets, raceColumnNames, trackedRegattaRegistry); Iterable regattaSeries = Collections.singleton(series); - return new RegattaImpl(regattaName, boatClass, startDate, endDate, regattaSeries, persistent, scoringScheme, regatteId , courseArea, OneDesignRankingMetric::new); + return new RegattaImpl(regattaName, boatClass, /* canBoatsOfCompetitorsChangePerRace */ true, + startDate, endDate, regattaSeries, persistent, scoringScheme, regatteId , courseArea, OneDesignRankingMetric::new); } public static void assignRacesToRegattaLeaderboardColumns(RegattaLeaderboard leaderboard, Collection raceIdentifiers) { diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/TrackTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/TrackTest.java index f0ce8699455..ea12bede9ae 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/TrackTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/TrackTest.java @@ -76,7 +76,7 @@ public class TrackTest { @Before public void setUp() throws InterruptedException { - track = new DynamicGPSFixMovingTrackImpl(new BoatImpl("MyFirstBoat", new BoatClassImpl("505", /* typicallyStartsUpwind */ + track = new DynamicGPSFixMovingTrackImpl(new BoatImpl("123", "MyFirstBoat", new BoatClassImpl("505", /* typicallyStartsUpwind */ true), null), /* millisecondsOverWhichToAverage */5000, /* no smoothening */null); TimePoint now1 = MillisecondsTimePoint.now(); TimePoint now2 = addMillisToTimepoint(now1, MILLIS_BETWEEN_FIXES); @@ -131,7 +131,7 @@ public class TrackTest { @Test public void positionEstimationTest() { track = new DynamicGPSFixMovingTrackImpl( - new BoatImpl("MyFirstBoat", new BoatClassImpl("505", true), null), 5000, null); + new BoatImpl("123", "MyFirstBoat", new BoatClassImpl("505", true), null), 5000, null); DegreePosition p1 = new DegreePosition(0, 0); DegreePosition p2 = new DegreePosition(90, 0); TimePoint t1 = MillisecondsTimePoint.now(); @@ -1077,8 +1077,8 @@ public class TrackTest { @Test public void testValidCheckForFixThatSaysItsAsFastAsItWas() { - DynamicGPSFixMovingTrackImpl myTrack = new DynamicGPSFixMovingTrackImpl(new BoatImpl("MyFirstBoat", new BoatClassImpl("505", /* typicallyStartsUpwind */ - true), null), /* millisecondsOverWhichToAverage */5000, /* no smoothening */null); + DynamicGPSFixMovingTrackImpl myTrack = new DynamicGPSFixMovingTrackImpl(new BoatImpl("123", "MyFirstBoat", new BoatClassImpl("505", /* typicallyStartsUpwind */ + true), null), /* millisecondsOverWhichToAverage */5000, /* no smoothening */null); TimePoint now1 = MillisecondsTimePoint.now(); TimePoint now2 = addMillisToTimepoint(now1, 1000); // 1s AbstractBearing bearing = new DegreeBearingImpl(90); @@ -1098,8 +1098,8 @@ public class TrackTest { @Test public void testValidCheckForFixThatSaysItsFasterThanItActuallyWas() { - DynamicGPSFixMovingTrackImpl myTrack = new DynamicGPSFixMovingTrackImpl(new BoatImpl("MyFirstBoat", new BoatClassImpl("505", /* typicallyStartsUpwind */ - true), null), /* millisecondsOverWhichToAverage */5000, /* no smoothening */null); + DynamicGPSFixMovingTrackImpl myTrack = new DynamicGPSFixMovingTrackImpl(new BoatImpl("123", "MyFirstBoat", new BoatClassImpl("505", /* typicallyStartsUpwind */ + true), null), /* millisecondsOverWhichToAverage */5000, /* no smoothening */null); TimePoint now1 = MillisecondsTimePoint.now(); TimePoint now2 = addMillisToTimepoint(now1, 1000); // 1s AbstractBearing bearing = new DegreeBearingImpl(90); @@ -1119,8 +1119,8 @@ public class TrackTest { @Test public void testValidCheckForFixThatSaysItsSlowerThanItActuallyWas() { - DynamicGPSFixMovingTrackImpl myTrack = new DynamicGPSFixMovingTrackImpl(new BoatImpl("MyFirstBoat", new BoatClassImpl("505", /* typicallyStartsUpwind */ - true), null), /* millisecondsOverWhichToAverage */5000, /* no smoothening */null); + DynamicGPSFixMovingTrackImpl myTrack = new DynamicGPSFixMovingTrackImpl(new BoatImpl("123", "MyFirstBoat", new BoatClassImpl("505", /* typicallyStartsUpwind */ + true), null), /* millisecondsOverWhichToAverage */5000, /* no smoothening */null); TimePoint now1 = MillisecondsTimePoint.now(); TimePoint now2 = addMillisToTimepoint(now1, 1000); // 1s AbstractBearing bearing = new DegreeBearingImpl(90); diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/TrackedRaceStartTimeUpdateForDependentStartTimeTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/TrackedRaceStartTimeUpdateForDependentStartTimeTest.java index 7d564ab60a9..4de312c268a 100644 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/TrackedRaceStartTimeUpdateForDependentStartTimeTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/TrackedRaceStartTimeUpdateForDependentStartTimeTest.java @@ -6,7 +6,6 @@ import static org.junit.Assert.assertNull; import java.io.IOException; import java.net.MalformedURLException; -import java.util.Collections; import org.junit.Test; @@ -19,7 +18,7 @@ import com.sap.sailing.domain.abstractlog.race.impl.RaceLogDependentStartTimeEve import com.sap.sailing.domain.abstractlog.race.impl.RaceLogImpl; import com.sap.sailing.domain.abstractlog.race.impl.RaceLogStartTimeEventImpl; import com.sap.sailing.domain.abstractlog.race.impl.SimpleRaceLogIdentifierImpl; -import com.sap.sailing.domain.base.Competitor; +import com.sap.sailing.domain.base.CompetitorWithBoat; import com.sap.sailing.domain.tracking.StartTimeChangedListener; import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sse.common.Duration; @@ -29,7 +28,7 @@ import com.sap.sse.common.impl.MillisecondsTimePoint; public class TrackedRaceStartTimeUpdateForDependentStartTimeTest extends TrackBasedTest { @Test public void testStartTimeUpdateForRaceWithDependentStartTime() { - final Competitor hasso = createCompetitor("Hasso"); + final CompetitorWithBoat hasso = createCompetitorWithBoat("Hasso"); final RaceLog r1RaceLog = new RaceLogImpl("r1RaceLog"); final RaceLog r2RaceLog = new RaceLogImpl("r2RaceLog"); final RaceLogResolver raceLogResolver = new RaceLogResolver() { @@ -43,9 +42,9 @@ public class TrackedRaceStartTimeUpdateForDependentStartTimeTest extends TrackBa return null; } }; - final TrackedRace r1 = createTestTrackedRace("Regatta", "R1", "J/70", Collections.singleton(hasso), + final TrackedRace r1 = createTestTrackedRace("Regatta", "R1", "J/70", createCompetitorAndBoatsMap(hasso), MillisecondsTimePoint.now(), /* useMarkPassingCalculator */ false, raceLogResolver); - final TrackedRace r2 = createTestTrackedRace("Regatta", "R2", "J/70", Collections.singleton(hasso), + final TrackedRace r2 = createTestTrackedRace("Regatta", "R2", "J/70", createCompetitorAndBoatsMap(hasso), MillisecondsTimePoint.now(), /* useMarkPassingCalculator */ false, raceLogResolver); r1.attachRaceLog(r1RaceLog); r2.attachRaceLog(r2RaceLog); diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/TrackedRaceStatusUpdateTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/TrackedRaceStatusUpdateTest.java index 1bb230d7ddb..64b4b411bb0 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/TrackedRaceStatusUpdateTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/TrackedRaceStatusUpdateTest.java @@ -2,12 +2,10 @@ package com.sap.sailing.domain.test; import static org.junit.Assert.assertSame; -import java.util.Collections; - import org.junit.Before; import org.junit.Test; -import com.sap.sailing.domain.base.Competitor; +import com.sap.sailing.domain.base.CompetitorWithBoat; import com.sap.sailing.domain.common.TrackedRaceStatusEnum; import com.sap.sailing.domain.tracking.DynamicTrackedRace; import com.sap.sailing.domain.tracking.TrackingDataLoader; @@ -21,13 +19,13 @@ import com.sap.sse.common.impl.MillisecondsTimePoint; * */ public class TrackedRaceStatusUpdateTest extends TrackBasedTest { - private Competitor competitor; + private CompetitorWithBoat competitor; private DynamicTrackedRace trackedRace; @Before public void setUp() { - competitor = createCompetitor("Test Competitor"); - trackedRace = createTestTrackedRace("Test Regatta", "Test Race", "505", Collections.singleton(competitor), MillisecondsTimePoint.now(), /* useMarkPassingCalculator */ false); + competitor = createCompetitorWithBoat("Test Competitor"); + trackedRace = createTestTrackedRace("Test Regatta", "Test Race", "505", createCompetitorAndBoatsMap(competitor), MillisecondsTimePoint.now(), /* useMarkPassingCalculator */ false); } @Test diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/UnicodeCharactersInCompetitorNamesTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/UnicodeCharactersInCompetitorNamesTest.java index ce8d59043ef..e56eda93e06 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/UnicodeCharactersInCompetitorNamesTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/UnicodeCharactersInCompetitorNamesTest.java @@ -17,6 +17,7 @@ import org.junit.rules.Timeout; import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver; import com.sap.sailing.domain.base.Competitor; +import com.sap.sailing.domain.leaderboard.LeaderboardGroupResolver; import com.sap.sailing.domain.racelog.impl.EmptyRaceLogStore; import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore; import com.sap.sailing.domain.tracking.RaceTracker; @@ -53,7 +54,7 @@ public class UnicodeCharactersInCompetitorNamesTest { EmptyRaceLogStore.INSTANCE, EmptyRegattaLogStore.INSTANCE, EmptyWindStore.INSTANCE, new DummyTrackedRegattaRegistry(), - mock(RaceLogResolver.class), new RaceTrackingConnectivityParametersImpl(new URL( + mock(RaceLogResolver.class), mock(LeaderboardGroupResolver.class), new RaceTrackingConnectivityParametersImpl(new URL( "http://" + TracTracConnectionConstants.HOST_NAME + "/events/event_20110609_KielerWoch/clientparams.php?event=event_20110609_KielerWoch&race=5b08a9ee-9933-11e0-85be-406186cbf87c"), diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/UpdateMarkPassingTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/UpdateMarkPassingTest.java index 4179b4b4f3c..a9a968c5b42 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/UpdateMarkPassingTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/UpdateMarkPassingTest.java @@ -11,7 +11,7 @@ import java.util.HashSet; import org.junit.Test; import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver; -import com.sap.sailing.domain.base.Competitor; +import com.sap.sailing.domain.base.CompetitorWithBoat; import com.sap.sailing.domain.base.Course; import com.sap.sailing.domain.base.RaceDefinition; import com.sap.sailing.domain.base.Series; @@ -40,11 +40,11 @@ public class UpdateMarkPassingTest { when(race.getName()).thenReturn("Test Race"); Course c = new CourseImpl("Test Course", Collections.singleton(waypoint)); when(race.getCourse()).thenReturn(c); - Competitor competitor = TrackBasedTest.createCompetitor("Test Competitor"); + CompetitorWithBoat competitor = TrackBasedTest.createCompetitorWithBoat("Test Competitor"); when(race.getBoatClass()).thenReturn(new BoatClassImpl("49er", /* typicallyStartsUpwind */ true)); when(race.getCompetitors()).thenReturn(Collections.singleton(competitor)); DynamicTrackedRaceImpl trackedRace = new DynamicTrackedRaceImpl( - /* trackedRegatta */new DynamicTrackedRegattaImpl(new RegattaImpl("test", null, null, null, new HashSet(), false, null, + /* trackedRegatta */new DynamicTrackedRegattaImpl(new RegattaImpl("test", null, true, null, null, new HashSet(), false, null, "test", null, OneDesignRankingMetric::new)), race, Collections. emptyList(), EmptyWindStore.INSTANCE, /* delayToLiveInMillis */1000, /* millisecondsOverWhichToAverageWind */30000, diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/WindEstimationOnConstructedTracksTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/WindEstimationOnConstructedTracksTest.java index fb391a50113..fae597619ba 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/WindEstimationOnConstructedTracksTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/WindEstimationOnConstructedTracksTest.java @@ -18,7 +18,9 @@ import java.util.Set; import org.junit.Before; import org.junit.Test; +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.Waypoint; import com.sap.sailing.domain.common.Distance; import com.sap.sailing.domain.common.LegType; @@ -47,20 +49,26 @@ import com.sap.sse.common.Util; import com.sap.sse.common.impl.MillisecondsTimePoint; public class WindEstimationOnConstructedTracksTest extends StoredTrackBasedTest { - private List competitors; + private List competitors; private static final String[] competitorNames = new String[] { "Wolfgang Hunger", "Dr. Hasso Plattner", "Robert Stanjek", "Simon Grotelueschen" }; @Before public void setUp() { - competitors = new ArrayList(); + competitors = new ArrayList<>(); for (String name : competitorNames) { - competitors.add(createCompetitor(name)); + CompetitorWithBoat competitor = createCompetitorWithBoat(name); + competitors.add(competitor); } } private void initRace(int numberOfCompetitorsToUse, int[] numberOfMarksPassed, TimePoint timePointForFixes) { + List subList = competitors.subList(0, numberOfCompetitorsToUse); + Map competitorsAndBoats = new HashMap<>(); + for (CompetitorWithBoat competitor: subList) { + competitorsAndBoats.put(competitor, competitor.getBoat()); + } setTrackedRace(createTestTrackedRace("Kieler Woche", "505 Race 2", "505", - competitors.subList(0, numberOfCompetitorsToUse), timePointForFixes, /* useMarkPassingCalculator */ false)); + competitorsAndBoats, timePointForFixes, /* useMarkPassingCalculator */ false)); for (int i=0; i competitorsAndBoats = new HashMap<>(); + competitorsAndBoats.put(competitor, boat); DynamicTrackedRace trackedRace = new DynamicTrackedRaceImpl(new DynamicTrackedRegattaImpl( new RegattaImpl(EmptyRaceLogStore.INSTANCE, EmptyRegattaLogStore.INSTANCE, - RegattaImpl.getDefaultName("Test Regatta", boatClass.getName()), boatClass, /*startDate*/ null, /*endDate*/ null, + RegattaImpl.getDefaultName("Test Regatta", boatClass.getName()), boatClass, + /* canBoatsOfCompetitorsChangePerRace */ true, /*startDate*/ null, /*endDate*/ null, /* trackedRegattaRegistry */ null, domainFactory.createScoringScheme(ScoringSchemeType.LOW_POINT), "123", null)), new RaceDefinitionImpl("Test Race", new CourseImpl("Test Course", Arrays.asList(new Waypoint[] { w1, w2, w3 })), - boatClass, Collections.singleton(competitor)), Collections. emptyList(), + boatClass, competitorsAndBoats), Collections. emptyList(), EmptyWindStore.INSTANCE, /* delayToLiveInMillis */ 1000, /* millisecondsOverWhichToAverageWind */ 30000, /* millisecondsOverWhichToAverageSpeed */ 30000, /*useMarkPassingCalculator*/ false, OneDesignRankingMetric::new, diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/markpassing/AbstractMockedRaceMarkPassingTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/markpassing/AbstractMockedRaceMarkPassingTest.java index 33866c17476..3bb5c0ecdd8 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/markpassing/AbstractMockedRaceMarkPassingTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/markpassing/AbstractMockedRaceMarkPassingTest.java @@ -4,10 +4,13 @@ import static org.mockito.Mockito.mock; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.UUID; import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver; +import com.sap.sailing.domain.base.Boat; import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.ControlPointWithTwoMarks; import com.sap.sailing.domain.base.Course; @@ -45,10 +48,13 @@ import com.sap.sse.common.impl.MillisecondsTimePoint; public class AbstractMockedRaceMarkPassingTest { final BoatClassImpl boatClass = new BoatClassImpl("boat", true, "boat", new MeterDistance(10), new MeterDistance(5), null); - protected Competitor ron = new CompetitorImpl("Ron", "Ron", null, null, null, null, new BoatImpl("boat", boatClass, "boot"), /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null); - protected Competitor tom = new CompetitorImpl("Tom", "Tom", null, null, null, null, new BoatImpl("boat", boatClass, "boot"), /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null); - protected Competitor ben = new CompetitorImpl("Ben", "Ben", null, null, null, null, new BoatImpl("boat", boatClass, "boot"), /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null); - + protected Competitor ron = new CompetitorImpl("Ron", "Ron", "KYC", null, null, null, null, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null); + protected Competitor tom = new CompetitorImpl("Tom", "Tom", "KYC", null, null, null, null, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null); + protected Competitor ben = new CompetitorImpl("Ben", "Ben", "KYC", null, null, null, null, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null); + protected Boat boatRon = new BoatImpl("123", "boat1", boatClass, "GER 123"); + protected Boat boatTom = new BoatImpl("456", "boat2", boatClass, "GER 456"); + protected Boat boatBen = new BoatImpl("789", "boat3", boatClass, "GER 789"); + protected Mark m = new MarkImpl("Mark"); protected Mark gate1 = new MarkImpl("Gate1"); protected Mark gate2 = new MarkImpl("Gate2"); @@ -66,11 +72,15 @@ public class AbstractMockedRaceMarkPassingTest { Waypoint w4 = new WaypointImpl(m, PassingInstruction.Starboard); Waypoint w5 = new WaypointImpl(cp, PassingInstruction.Line); waypoints = Arrays.asList(w1, w2, w3, w4, w5); - Regatta r = new RegattaImpl(RegattaImpl.getDefaultName("regatta", boatClass.getName()), boatClass, + Regatta r = new RegattaImpl(RegattaImpl.getDefaultName("regatta", boatClass.getName()), boatClass, /* canBoatsOfCompetitorsChangePerRace */ true, /*startDate*/ null, /*endDate*/ null, Arrays.asList(new SeriesImpl("Series", true, /* isFleetsCanRunInParallel */ true, Arrays.asList(new FleetImpl("fleet")), new ArrayList(), null)), true, new HighPoint(), "ID", new CourseAreaImpl("area", new UUID(5, 5)), OneDesignRankingMetric::new); Course course = new CourseImpl("course", waypoints); - RaceDefinition raceDef = new RaceDefinitionImpl("Performance Race", course, boatClass, Arrays.asList(ron, tom, ben)); + Map competitorsAndBoats = new HashMap<>(); + competitorsAndBoats.put(ron, boatRon); + competitorsAndBoats.put(tom, boatTom); + competitorsAndBoats.put(ben, boatBen); + RaceDefinition raceDef = new RaceDefinitionImpl("Performance Race", course, boatClass, competitorsAndBoats); race = new DynamicTrackedRaceImpl(new DynamicTrackedRegattaImpl(r), raceDef, new ArrayList(), new EmptyWindStore(), 0, 10000, 10000, /* useMarkPassingCalculator */ false, OneDesignRankingMetric::new, diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRace.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRace.java index 578c9aec53a..9c6478a6fe5 100644 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRace.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRace.java @@ -12,6 +12,7 @@ import java.util.concurrent.Future; import com.sap.sailing.domain.abstractlog.race.RaceLog; import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver; import com.sap.sailing.domain.abstractlog.regatta.RegattaLog; +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.CourseArea; @@ -333,6 +334,16 @@ public class MockedTrackedRace implements DynamicTrackedRace { return null; } + @Override + public Iterable getAllBoats() { + return null; + } + + @Override + public boolean canBoatsOfCompetitorsChangePerRace() { + return false; + } + @Override public void addRace(RaceDefinition race) { } @@ -529,7 +540,7 @@ public class MockedTrackedRace implements DynamicTrackedRace { } @Override - public void registerCompetitors(Iterable competitor) { + public void registerCompetitors(Iterable competitors) { } @Override @@ -545,6 +556,27 @@ public class MockedTrackedRace implements DynamicTrackedRace { return null; } + @Override + public Iterable getBoatsRegisteredInRegattaLog() { + return null; + } + + @Override + public void registerBoat(Boat boat) { + } + + @Override + public void registerBoats(Iterable boat) { + } + + @Override + public void deregisterBoat(Boat boat) { + } + + @Override + public void deregisterBoats(Iterable boat) { + } + @Override public boolean isControlTrackingFromStartAndFinishTimes() { return false; @@ -1161,6 +1193,15 @@ public class MockedTrackedRace implements DynamicTrackedRace { } @Override + public Boat getBoatOfCompetitor(Competitor competitor) { + return null; + } + + @Override + public Competitor getCompetitorOfBoat(Boat boat) { + return null; + } + public Distance getEstimatedDistanceToComplete(TimePoint now) { return null; } diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRaceWithFixedRank.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRaceWithFixedRank.java index a4a68cee96b..d361cbf4808 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRaceWithFixedRank.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRaceWithFixedRank.java @@ -3,11 +3,14 @@ package com.sap.sailing.domain.test.mock; import java.io.Serializable; import java.util.Arrays; import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; 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.Course; import com.sap.sailing.domain.base.RaceDefinition; import com.sap.sailing.domain.base.Waypoint; @@ -19,20 +22,21 @@ public class MockedTrackedRaceWithFixedRank extends MockedTrackedRace { private static final long serialVersionUID = -8587203762630194172L; private final int rank; private final boolean started; - private final RaceDefinition raceDefinition; - private final Competitor competitor; + protected final Map competitorsAndBoats; private final BoatClass boatClass; + protected RaceDefinition raceDefinition; - public MockedTrackedRaceWithFixedRank(Competitor competitor, int rank, boolean started, BoatClass boatClass) { + public MockedTrackedRaceWithFixedRank(CompetitorWithBoat competitorWithBoat, int rank, boolean started, BoatClass boatClass) { this.rank = rank; this.started = started; - this.competitor = competitor; + this.competitorsAndBoats = new HashMap<>(); this.raceDefinition = new MockedRaceDefinition(); this.boatClass = boatClass; + this.competitorsAndBoats.put(competitorWithBoat, competitorWithBoat.getBoat()); } - public MockedTrackedRaceWithFixedRank(Competitor competitor, int rank, boolean started) { - this(competitor, rank, started, /* boatClass */ null); + public MockedTrackedRaceWithFixedRank(CompetitorWithBoat competitorWithBoat,int rank, boolean started) { + this(competitorWithBoat, rank, started, /* boatClass */ null); } private class MockedRaceDefinition implements RaceDefinition { @@ -55,7 +59,17 @@ public class MockedTrackedRaceWithFixedRank extends MockedTrackedRace { @Override public Iterable getCompetitors() { - return Collections.singleton(MockedTrackedRaceWithFixedRank.this.competitor); + return competitorsAndBoats.keySet(); + } + + @Override + public Iterable getBoats() { + return competitorsAndBoats.values(); + } + + @Override + public Map getCompetitorsAndTheirBoats() { + return competitorsAndBoats; } @Override @@ -70,8 +84,9 @@ public class MockedTrackedRaceWithFixedRank extends MockedTrackedRace { @Override public Competitor getCompetitorById(Serializable competitorID) { - if (competitorID.equals(MockedTrackedRaceWithFixedRank.this.competitor.getId())) { - return MockedTrackedRaceWithFixedRank.this.competitor; + Competitor c = competitorsAndBoats.keySet().iterator().next(); + if (competitorID.equals(c.getId())) { + return competitorsAndBoats.keySet().iterator().next(); } else { return null; } @@ -79,7 +94,7 @@ public class MockedTrackedRaceWithFixedRank extends MockedTrackedRace { @Override public Boat getBoatOfCompetitor(Competitor competitor) { - return null; + return competitorsAndBoats.get(competitor); } @Override @@ -90,7 +105,7 @@ public class MockedTrackedRaceWithFixedRank extends MockedTrackedRace { @Override public List getCompetitorsFromBestToWorst(TimePoint timePoint) { - return Collections.singletonList(competitor); + return Collections.singletonList(competitorsAndBoats.keySet().iterator().next()); } @Override @@ -112,4 +127,9 @@ public class MockedTrackedRaceWithFixedRank extends MockedTrackedRace { public RaceDefinition getRace() { return raceDefinition; } + + @Override + public Boat getBoatOfCompetitor(Competitor competitor) { + return competitorsAndBoats.get(competitor); + } } diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRaceWithFixedRankAndManyCompetitors.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRaceWithFixedRankAndManyCompetitors.java index e1091b53505..a239cccb823 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRaceWithFixedRankAndManyCompetitors.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRaceWithFixedRankAndManyCompetitors.java @@ -1,24 +1,20 @@ package com.sap.sailing.domain.test.mock; import java.io.Serializable; -import java.util.HashSet; -import java.util.Set; +import java.util.Map; 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.Course; import com.sap.sailing.domain.base.RaceDefinition; public class MockedTrackedRaceWithFixedRankAndManyCompetitors extends MockedTrackedRaceWithFixedRank { private static final long serialVersionUID = -6189437610363552577L; - private final Set competitors; - private final RaceDefinition raceDefinition; - public MockedTrackedRaceWithFixedRankAndManyCompetitors(Competitor competitor, int rank, boolean started) { - super(competitor, rank, started); - competitors = new HashSet(); - competitors.add(competitor); + public MockedTrackedRaceWithFixedRankAndManyCompetitors(CompetitorWithBoat competitorWithBoat, int rank, boolean started) { + super(competitorWithBoat, rank, started); this.raceDefinition = new RaceDefinition() { private static final long serialVersionUID = 8878878939443845646L; @Override @@ -31,11 +27,19 @@ public class MockedTrackedRaceWithFixedRankAndManyCompetitors extends MockedTrac } @Override public Iterable getCompetitors() { - return competitors; + return competitorsAndBoats.keySet(); + } + @Override + public Iterable getBoats() { + return competitorsAndBoats.values(); + } + @Override + public Map getCompetitorsAndTheirBoats() { + return competitorsAndBoats; } @Override public Competitor getCompetitorById(Serializable competitorID) { - for (Competitor competitor : competitors) { + for (Competitor competitor : competitorsAndBoats.keySet()) { if (competitorID.equals(competitor.getId())) { return competitor; } @@ -53,7 +57,7 @@ public class MockedTrackedRaceWithFixedRankAndManyCompetitors extends MockedTrac } @Override public Boat getBoatOfCompetitor(Competitor competitor) { - return null; + return competitorsAndBoats.get(competitor); } @Override public byte[] getCompetitorMD5() { @@ -62,8 +66,8 @@ public class MockedTrackedRaceWithFixedRankAndManyCompetitors extends MockedTrac }; } - public void addCompetitor(Competitor competitor) { - competitors.add(competitor); + public void addCompetitorWithBoat(CompetitorWithBoat competitorWithBoat) { + competitorsAndBoats.put(competitorWithBoat, competitorWithBoat.getBoat()); } @Override diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRaceWithStartTimeAndRanks.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRaceWithStartTimeAndRanks.java index ad955c44b9a..f83fb7a88dc 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRaceWithStartTimeAndRanks.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRaceWithStartTimeAndRanks.java @@ -3,12 +3,16 @@ package com.sap.sailing.domain.test.mock; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.NavigableSet; import java.util.Set; 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.base.BoatClass; import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.Leg; import com.sap.sailing.domain.base.Mark; @@ -18,6 +22,8 @@ import com.sap.sailing.domain.base.SharedDomainFactory; import com.sap.sailing.domain.base.Sideline; import com.sap.sailing.domain.base.SpeedWithConfidence; import com.sap.sailing.domain.base.Waypoint; +import com.sap.sailing.domain.base.impl.BoatClassImpl; +import com.sap.sailing.domain.base.impl.BoatImpl; import com.sap.sailing.domain.base.impl.CourseImpl; import com.sap.sailing.domain.base.impl.RaceDefinitionImpl; import com.sap.sailing.domain.common.Bearing; @@ -78,6 +84,7 @@ public class MockedTrackedRaceWithStartTimeAndRanks implements TrackedRace { private static final long serialVersionUID = 2708044935347796930L; private final TimePoint startTime; private final List competitorsFromBestToWorst; + private final Map competitorsAndBoats; private final Regatta regatta; private RaceDefinition race; @@ -95,9 +102,15 @@ public class MockedTrackedRaceWithStartTimeAndRanks implements TrackedRace { this.startTime = startTime; // copies the list to make sure that later modifications to the list passed to this constructor don't affect the ranking produced by this race this.competitorsFromBestToWorst = new ArrayList(competitorsFromBestToWorst); - final List waypoints = Collections.emptyList(); - this.race = new RaceDefinitionImpl("Mocked Race", new CourseImpl("Mock Course", waypoints), /* boat class */ null, - competitorsFromBestToWorst); + BoatClass boatClass = new BoatClassImpl("49er", /* upwind start */ true); + competitorsAndBoats = new HashMap<>(); + int i = 1; + for (Competitor c: competitorsFromBestToWorst) { + Boat b = new BoatImpl("Boat" + i++, c.getName(), boatClass, c.getName(), null); + competitorsAndBoats.put(c, b); + } + this.race = new RaceDefinitionImpl("Mocked Race", new CourseImpl("Mock Course", Collections.emptyList()), boatClass, + competitorsAndBoats); } @Override @@ -710,6 +723,23 @@ public class MockedTrackedRaceWithStartTimeAndRanks implements TrackedRace { } @Override + public Boat getBoatOfCompetitor(Competitor competitor) { + return competitorsAndBoats.get(competitor); + } + + @Override + public Competitor getCompetitorOfBoat(Boat boat) { + if (boat == null) { + return null; + } + for (Map.Entry competitorWithBoat : competitorsAndBoats.entrySet()) { + if (boat.equals(competitorWithBoat.getValue())) { + return competitorWithBoat.getKey(); + } + } + return null; + } + public Distance getEstimatedDistanceToComplete(TimePoint now) { return null; } diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/tracking/impl/BravoFixTrackSerializationTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/tracking/impl/BravoFixTrackSerializationTest.java index 772b6d59bc6..062d795244e 100644 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/tracking/impl/BravoFixTrackSerializationTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/tracking/impl/BravoFixTrackSerializationTest.java @@ -15,6 +15,7 @@ import org.junit.Before; import org.junit.Test; import com.sap.sailing.domain.base.Competitor; +import com.sap.sailing.domain.base.CompetitorWithBoat; import com.sap.sailing.domain.common.sensordata.BravoSensorDataMetadata; import com.sap.sailing.domain.common.tracking.impl.BravoFixImpl; import com.sap.sailing.domain.common.tracking.impl.DoubleVectorFixImpl; @@ -30,7 +31,7 @@ public class BravoFixTrackSerializationTest { @Before public void setUp() { - Competitor competitor = TrackBasedTest.createCompetitor("SAP Extreme Sailing Team"); + CompetitorWithBoat competitor = TrackBasedTest.createCompetitorWithBoat("SAP Extreme Sailing Team"); track = new BravoFixTrackImpl(competitor, BravoFixTrack.TRACK_NAME, false); } diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/tracking/impl/TrackedRegattaTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/tracking/impl/TrackedRegattaTest.java index 3980819bac9..d9a97c5d1d2 100644 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/tracking/impl/TrackedRegattaTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/tracking/impl/TrackedRegattaTest.java @@ -48,7 +48,7 @@ public class TrackedRegattaTest { public void setUp() { regatta = new DynamicTrackedRegattaImpl(new RegattaImpl(EmptyRaceLogStore.INSTANCE, EmptyRegattaLogStore.INSTANCE, RegattaImpl.getDefaultName("regatta", boatClass.getName()), boatClass, - /* startDate */ null, /* endDate */null, null, null, "a", null)); + /* canBoatsOfCompetitorsChangePerRace */ false, /* startDate */ null, /* endDate */null, null, null, "a", null)); } @Test(expected = TimeoutException.class) @@ -115,7 +115,7 @@ public class TrackedRegattaTest { } private DynamicTrackedRace createRace(String name) { - RaceDefinition race1 = new RaceDefinitionImpl(name, course, boatClass, Collections.emptyList()); + RaceDefinition race1 = new RaceDefinitionImpl(name, course, boatClass, Collections.emptyMap()); return new DynamicTrackedRaceImpl(regatta, race1, Collections. emptyList(), EmptyWindStore.INSTANCE, 0, 0, 0, /* useMarkPassingCalculator */ false, OneDesignRankingMetric::new, mock(RaceLogResolver.class)); diff --git a/java/com.sap.sailing.domain.tractracadapter/META-INF/MANIFEST.MF b/java/com.sap.sailing.domain.tractracadapter/META-INF/MANIFEST.MF index bd6ff98b0b6..a9d85b03706 100644 --- a/java/com.sap.sailing.domain.tractracadapter/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.domain.tractracadapter/META-INF/MANIFEST.MF @@ -20,7 +20,8 @@ Export-Package: com.sap.sailing.domain.tractracadapter, com.sap.sailing.domain.swisstimingadapter.test, com.sap.sailing.server.test, com.sap.sailing.mongodb.test, - com.sap.sailing.domain.tractracadapter.persistence" + com.sap.sailing.domain.tractracadapter.persistence, + com.sap.sailing.server.replication.test" Bundle-ActivationPolicy: lazy Import-Package: org.apache.http, org.apache.http.client;version="[4.2.5,4.3.0)", diff --git a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/DomainFactory.java b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/DomainFactory.java index e47a7f8254a..93b2aae54e7 100755 --- a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/DomainFactory.java +++ b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/DomainFactory.java @@ -28,6 +28,7 @@ import com.sap.sailing.domain.base.Sideline; import com.sap.sailing.domain.base.Waypoint; import com.sap.sailing.domain.common.PassingInstruction; import com.sap.sailing.domain.common.tracking.GPSFixMoving; +import com.sap.sailing.domain.leaderboard.LeaderboardGroupResolver; import com.sap.sailing.domain.racelog.RaceLogStore; import com.sap.sailing.domain.regattalog.RegattaLogStore; import com.sap.sailing.domain.tracking.DynamicRaceDefinitionSet; @@ -46,6 +47,7 @@ import com.sap.sailing.domain.tractracadapter.impl.DomainFactoryImpl; import com.sap.sailing.domain.tractracadapter.impl.RaceCourseReceiver; import com.sap.sailing.domain.tractracadapter.impl.RaceTrackingConnectivityParametersImpl; import com.sap.sailing.domain.tractracadapter.impl.Simulator; +import com.sap.sse.common.Color; import com.sap.sse.common.Duration; import com.sap.sse.common.TimePoint; import com.sap.sse.common.Util; @@ -80,11 +82,12 @@ public interface DomainFactory { Sideline createSideline(String name, Iterable controlPoints); - com.sap.sailing.domain.base.Competitor getOrCreateCompetitor(ICompetitor competitor); + com.sap.sailing.domain.base.Boat getOrCreateBoat(Serializable boatId, String boatName, BoatClass boatClass, String sailId, Color boatColor); - com.sap.sailing.domain.base.Competitor getOrCreateCompetitor(UUID competitorId, String competitorClassName, - String nationalityAsString, String name, String shortName, float timeOnTimeFactor, float timeOnDistanceAllowanceInSecondsPerNauticalMile, String searchTag); + com.sap.sailing.domain.base.Competitor resolveCompetitor(ICompetitor competitor); + void updateCompetitor(ICompetitor competitor); + /** * Looks up or, if not found, creates a {@link Nationality} object and re-uses threeLetterIOCCode also as the * nationality's name. @@ -133,16 +136,18 @@ public interface DomainFactory { * implementation is {@link EmptyWindStore} which simply provides new, empty tracks. This is always * available but loses track of the wind, e.g., during server restarts. */ - TracTracRaceTracker createRaceTracker(RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, WindStore windStore, TrackedRegattaRegistry trackedRegattaRegistry, - RaceLogResolver raceLogResolver, RaceTrackingConnectivityParametersImpl connectivityParams, long timeoutInMilliseconds) - throws MalformedURLException, FileNotFoundException, URISyntaxException, CreateModelException, SubscriberInitializationException, IOException, InterruptedException; + TracTracRaceTracker createRaceTracker(RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, + WindStore windStore, TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver, + LeaderboardGroupResolver leaderboardGroupResolver, + RaceTrackingConnectivityParametersImpl connectivityParams, long timeoutInMilliseconds) + throws URISyntaxException, SubscriberInitializationException, IOException, InterruptedException; /** * Same as {@link #createRaceTracker(URL, URI, URI, URI, TimePoint, TimePoint, WindStore, TrackedRegattaRegistry)}, * only that a predefined {@link Regatta} is used to hold the resulting races. */ RaceTracker createRaceTracker(Regatta regatta, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, WindStore windStore, TrackedRegattaRegistry trackedRegattaRegistry, - RaceLogResolver raceLogResolver, RaceTrackingConnectivityParametersImpl connectivityParams, long timeoutInMilliseconds) + RaceLogResolver raceLogResolver, LeaderboardGroupResolver leaderboardGroupResolver, RaceTrackingConnectivityParametersImpl connectivityParams, long timeoutInMilliseconds) throws MalformedURLException, FileNotFoundException, URISyntaxException, CreateModelException, SubscriberInitializationException, IOException, InterruptedException; @@ -165,7 +170,7 @@ public interface DomainFactory { */ Iterable getUpdateReceivers(DynamicTrackedRegatta trackedRegatta, long delayToLiveInMillis, Simulator simulator, WindStore windStore, DynamicRaceDefinitionSet raceDefinitionSetToUpdate, TrackedRegattaRegistry trackedRegattaRegistry, - RaceLogResolver raceLogResolver, IRace tractracRace, + RaceLogResolver raceLogResolver, LeaderboardGroupResolver leaderboardGroupResolver, IRace tractracRace, URI courseDesignUpdateURI, String tracTracUsername, String tracTracPassword, IEventSubscriber eventSubscriber, IRaceSubscriber raceSubscriber, boolean useInternalMarkPassingAlgorithm, long timeoutInMilliseconds); @@ -193,8 +198,8 @@ public interface DomainFactory { * the race. */ DynamicTrackedRace getOrCreateRaceDefinitionAndTrackedRace(DynamicTrackedRegatta trackedRegatta, UUID raceId, - String raceName, Iterable competitors, BoatClass boatClass, - Map competitorBoats, Course course, Iterable sidelines, WindStore windStore, + String raceName, BoatClass boatClass, Map competitorBoats, + Course course, Iterable sidelines, WindStore windStore, long delayToLiveInMillis, long millisecondsOverWhichToAverageWind, DynamicRaceDefinitionSet raceDefinitionSetToUpdate, URI courseDesignUpdateURI, UUID tracTracEventUuid, String tracTracUsername, String tracTracPassword, boolean ignoreTracTracMarkPassings, @@ -215,7 +220,7 @@ public interface DomainFactory { */ Iterable getUpdateReceivers(DynamicTrackedRegatta trackedRegatta, IRace tractracRace, WindStore windStore, long delayToLiveInMillis, Simulator simulator, DynamicRaceDefinitionSet raceDefinitionSetToUpdate, TrackedRegattaRegistry trackedRegattaRegistry, - RaceLogResolver raceLogResolver, URI courseDesignUpdateURI, + RaceLogResolver raceLogResolver, LeaderboardGroupResolver leaderboardGroupResolver, URI courseDesignUpdateURI, String tracTracUsername, String tracTracPassword, IEventSubscriber eventSubscriber, IRaceSubscriber raceSubscriber, boolean ignoreTracTracMarkPassings, long timeoutInMilliseconds, ReceiverType... types); JSONService parseJSONURLWithRaceRecords(URL jsonURL, boolean loadClientParams) throws IOException, ParseException, org.json.simple.parser.ParseException, URISyntaxException; @@ -248,9 +253,10 @@ public interface DomainFactory { */ RaceDefinition getAndWaitForRaceDefinition(UUID raceId, long timeoutInMilliseconds); - Map getBoatsInfoForCompetitors(IRace race, BoatClass defaultBoatClass); + Map getOrCreateCompetitorsAndTheirBoats(DynamicTrackedRegatta trackedRegatta, LeaderboardGroupResolver LeaderboardGroupResolver, + IRace race, BoatClass defaultBoatClass); - Util.Pair, BoatClass> getCompetitorsAndDominantBoatClass(IRace race); + BoatClass resolveDominantBoatClassOfRace(IRace race); /** * @param offsetToStartTimeOfSimulatedRace diff --git a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/MetadataParser.java b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/MetadataParser.java index ca6ac5041a5..7c720fda45d 100755 --- a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/MetadataParser.java +++ b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/MetadataParser.java @@ -2,13 +2,13 @@ package com.sap.sailing.domain.tractracadapter; import java.io.Serializable; import java.util.Map; +import java.util.UUID; import com.sap.sailing.domain.common.MarkType; import com.sap.sailing.domain.common.PassingInstruction; import com.sap.sailing.domain.tractracadapter.impl.RaceCourseReceiver; import com.sap.sse.common.Color; import com.sap.sse.common.Named; -import com.sap.sse.common.Util; import com.tractrac.model.lib.api.event.IRaceCompetitor; /** @@ -34,7 +34,13 @@ public interface MetadataParser { String getPattern(); Serializable getId(); } - + + public interface BoatMetaData extends Named { + String getId(); + String getColor(); + UUID getUuid(); + } + Map parsePassingInstructionData(String routeMetadataString, Iterable controlPoints); /** @@ -59,7 +65,7 @@ public interface MetadataParser { /** * Parses the boat name, boad id and the boat color for a competitor (entry) of a race. * @param competitor - * @return a Triple containing the boat name, boat id and boat color or null if no metadata is availalble + * @return the boat metadata or null if no metadata is availalble */ - Util.Triple parseCompetitorBoat(IRaceCompetitor competitor); + BoatMetaData parseCompetitorBoat(IRaceCompetitor competitor); } diff --git a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/AbstractReceiverWithQueue.java b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/AbstractReceiverWithQueue.java index 1894f85c08d..ea0e6dea2a5 100755 --- a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/AbstractReceiverWithQueue.java +++ b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/AbstractReceiverWithQueue.java @@ -194,6 +194,10 @@ public abstract class AbstractReceiverWithQueue implements Runnable, Re } } catch (InterruptedException e) { logger.log(Level.INFO, "Interrupted while taking element from queue", e); + } catch (Exception e) { + // before this thread terminates abnormally, at least log it: + logger.log(Level.SEVERE, ""+this+" is terminating abnormally; the race will probably be left at LOADING (100%).", e); + throw e; } } unsubscribe(); @@ -262,4 +266,10 @@ public abstract class AbstractReceiverWithQueue implements Runnable, Re } } } + + @Override + public String toString() { + return "Receiver "+getClass().getName()+" for regatta "+getTrackedRegatta().getRegatta().getName()+ + " in event "+getTracTracEvent().getName()+" with ID "+getTracTracEvent().getId(); + } } diff --git a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/DomainFactoryImpl.java b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/DomainFactoryImpl.java index a2cc640340e..9892d333e1f 100755 --- a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/DomainFactoryImpl.java +++ b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/DomainFactoryImpl.java @@ -8,6 +8,7 @@ import java.net.URL; import java.text.ParseException; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -18,6 +19,7 @@ import java.util.Map.Entry; import java.util.Set; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Consumer; import java.util.logging.Level; import java.util.logging.Logger; @@ -28,15 +30,17 @@ 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.CompetitorStore; +import com.sap.sailing.domain.base.CompetitorWithBoat; import com.sap.sailing.domain.base.ControlPoint; import com.sap.sailing.domain.base.Course; +import com.sap.sailing.domain.base.LeaderboardGroupBase; import com.sap.sailing.domain.base.Mark; +import com.sap.sailing.domain.base.MigratableRegatta; import com.sap.sailing.domain.base.Nationality; import com.sap.sailing.domain.base.RaceDefinition; import com.sap.sailing.domain.base.Regatta; import com.sap.sailing.domain.base.Sideline; import com.sap.sailing.domain.base.Waypoint; -import com.sap.sailing.domain.base.impl.BoatImpl; import com.sap.sailing.domain.base.impl.CourseImpl; import com.sap.sailing.domain.base.impl.DynamicBoat; import com.sap.sailing.domain.base.impl.DynamicPerson; @@ -54,6 +58,10 @@ import com.sap.sailing.domain.common.impl.DegreeBearingImpl; import com.sap.sailing.domain.common.impl.DegreePosition; import com.sap.sailing.domain.common.tracking.GPSFixMoving; import com.sap.sailing.domain.common.tracking.impl.GPSFixMovingImpl; +import com.sap.sailing.domain.leaderboard.Leaderboard; +import com.sap.sailing.domain.leaderboard.LeaderboardGroup; +import com.sap.sailing.domain.leaderboard.LeaderboardGroupResolver; +import com.sap.sailing.domain.leaderboard.RegattaLeaderboard; import com.sap.sailing.domain.racelog.RaceLogStore; import com.sap.sailing.domain.regattalog.RegattaLogStore; import com.sap.sailing.domain.tracking.DynamicRaceDefinitionSet; @@ -69,12 +77,14 @@ import com.sap.sailing.domain.tracking.WindTrack; import com.sap.sailing.domain.tractracadapter.DomainFactory; import com.sap.sailing.domain.tractracadapter.JSONService; import com.sap.sailing.domain.tractracadapter.MetadataParser; +import com.sap.sailing.domain.tractracadapter.MetadataParser.BoatMetaData; import com.sap.sailing.domain.tractracadapter.MetadataParser.ControlPointMetaData; import com.sap.sailing.domain.tractracadapter.Receiver; import com.sap.sailing.domain.tractracadapter.ReceiverType; import com.sap.sailing.domain.tractracadapter.TracTracConfiguration; import com.sap.sailing.domain.tractracadapter.TracTracControlPoint; import com.sap.sailing.domain.tractracadapter.TracTracRaceTracker; +import com.sap.sse.common.Color; import com.sap.sse.common.Duration; import com.sap.sse.common.TimePoint; import com.sap.sse.common.Util; @@ -127,10 +137,22 @@ public class DomainFactoryImpl implements DomainFactory { private final WeakValueCache raceCache = new WeakValueCache<>(new ConcurrentHashMap<>()); private final MetadataParser metadataParser; + + /** + * A synchronized set that holds those competitors that are currently being migrated from having a boat directly + * assigned to having a boat assignment per race (see bug 2822). The migration procedure ensures that it enters + * a competitor into this set before starting to check whether migration is required; it keeps the competitor + * in this set until either the test shows migration is not necessary or until migration has finished in case + * it was necessary. Checking whether a competitor is in this set, deciding about the migration need and adding + * the competitor to this set all have to happen in a single {@code synchronized} block using this set as + * monitor. + */ + private final Set competitorsCurrentlyBeingMigrated; public DomainFactoryImpl(com.sap.sailing.domain.base.DomainFactory baseDomainFactory) { this.baseDomainFactory = baseDomainFactory; this.metadataParser = new MetadataParserImpl(); + competitorsCurrentlyBeingMigrated = Collections.synchronizedSet(new HashSet<>()); } @Override @@ -235,24 +257,48 @@ public class DomainFactoryImpl implements DomainFactory { } @Override - public Competitor getOrCreateCompetitor(ICompetitor competitor) { - final UUID competitorId = competitor.getId(); - final String competitorClassName = competitor.getCompetitorClass()==null?null:competitor.getCompetitorClass().getName(); - final String nationalityAsString = competitor.getNationality(); - final String name = competitor.getName(); - final String shortName = competitor.getShortName(); - Competitor result = getOrCreateCompetitor(competitorId, competitorClassName, nationalityAsString, name, - shortName, competitor.getHandicapToT(), competitor.getHandicapToD(), null); - return result; + public Competitor resolveCompetitor(ICompetitor competitor) { + return baseDomainFactory.getCompetitorStore().getExistingCompetitorById(competitor.getId()); } @Override - public Competitor getOrCreateCompetitor(final UUID competitorId, final String competitorClassName, + public void updateCompetitor(ICompetitor competitor) { + Competitor domainCompetitor = this.resolveCompetitor(competitor); + if (domainCompetitor != null) { + if (domainCompetitor.hasBoat()) { + getOrCreateCompetitorWithBoat(competitor); + } else { + getOrCreateCompetitor(competitor); + } + logger.info("Competitor " + competitor + + " was updated on TracTrac side. Maybe consider updating in competitor store as well. " + + "TracTrac competitor maps to " + domainCompetitor.getName() + " with ID " + + domainCompetitor.getId().toString()); + } else { + logger.info("Could not find competitor "+competitor+" in competitor store."); + } + } + + private Competitor getOrCreateCompetitor(ICompetitor competitor) { + Competitor result = getOrCreateCompetitor(competitor.getId(), competitor.getNationality(), competitor.getName(), + competitor.getShortName(), competitor.getHandicapToT(), competitor.getHandicapToD(), null); + return result; + } + + private CompetitorWithBoat getOrCreateCompetitorWithBoat(ICompetitor competitor) { + final String sailId = competitor.getShortName(); // we take the sailId from the shortName attribute + final String competitorClassName = competitor.getCompetitorClass()==null?null:competitor.getCompetitorClass().getName(); + CompetitorWithBoat result = getOrCreateCompetitorWithBoat(competitor.getId(), competitor.getNationality(), competitor.getName(), + /* shortName */ null, competitor.getHandicapToT(), competitor.getHandicapToD(), null, competitorClassName, sailId); + return result; + } + + private CompetitorWithBoat getOrCreateCompetitorWithBoat(final UUID competitorId, final String nationalityAsString, final String name, final String shortName, float timeOnTimeFactor, - float timeOnDistanceAllowanceInSecondsPerNauticalMile, String searchTag) { + float timeOnDistanceAllowanceInSecondsPerNauticalMile, String searchTag, String competitorClassName, String sailId) { CompetitorStore competitorStore = baseDomainFactory.getCompetitorStore(); - Competitor result = competitorStore.getExistingCompetitorById(competitorId); - if (result == null || competitorStore.isCompetitorToUpdateDuringGetOrCreate(result)) { + CompetitorWithBoat domainCompetitor = competitorStore.getExistingCompetitorWithBoatById(competitorId); + if (domainCompetitor == null || competitorStore.isCompetitorToUpdateDuringGetOrCreate(domainCompetitor)) { BoatClass boatClass = getOrCreateBoatClass(competitorClassName); Nationality nationality; try { @@ -262,13 +308,45 @@ public class DomainFactoryImpl implements DomainFactory { nationality = null; logger.log(Level.SEVERE, "Unknown nationality "+nationalityAsString+" for competitor "+name+"; leaving null", iae); } + // TODO bug2822: if only boat is to be updated then this would currently fail here; consider competitorStore.isBoatToUpdateDuringGetOrCreate(...) DynamicTeam team = createTeam(name, nationality, competitorId); - DynamicBoat boat = new BoatImpl(shortName, boatClass, shortName); - result = competitorStore.getOrCreateCompetitor(competitorId, name, null /* displayColor */, - null /* email */, null /* flagImag */, team, boat, (double) timeOnTimeFactor, + DynamicBoat boat = (DynamicBoat) competitorStore.getOrCreateBoat(UUID.randomUUID(), null /* no boat name available */, boatClass, sailId, /* color */ null); + domainCompetitor = competitorStore.getOrCreateCompetitorWithBoat(competitorId, name, shortName, null /* displayColor */, + null /* email */, null /* flagImag */, team, (double) timeOnTimeFactor, + new MillisecondsDurationImpl((long) (timeOnDistanceAllowanceInSecondsPerNauticalMile*1000)), searchTag, (DynamicBoat) boat); + } + return domainCompetitor; + } + + private Competitor getOrCreateCompetitor(final UUID competitorId, final String nationalityAsString, + final String name, final String shortName, float timeOnTimeFactor, + float timeOnDistanceAllowanceInSecondsPerNauticalMile, String searchTag) { + CompetitorStore competitorStore = baseDomainFactory.getCompetitorStore(); + Competitor domainCompetitor = competitorStore.getExistingCompetitorById(competitorId); + if (domainCompetitor == null || competitorStore.isCompetitorToUpdateDuringGetOrCreate(domainCompetitor)) { + Nationality nationality; + try { + nationality = getOrCreateNationality(nationalityAsString); + } catch (IllegalArgumentException iae) { + // the country code was probably not a legal IOC country code + nationality = null; + logger.log(Level.SEVERE, "Unknown nationality "+nationalityAsString+" for competitor "+name+"; leaving null", iae); + } + DynamicTeam team = createTeam(name, nationality, competitorId); + domainCompetitor = competitorStore.getOrCreateCompetitor(competitorId, name, shortName, null /* displayColor */, + null /* email */, null /* flagImag */, team, (double) timeOnTimeFactor, new MillisecondsDurationImpl((long) (timeOnDistanceAllowanceInSecondsPerNauticalMile*1000)), searchTag); } - return result; + return domainCompetitor; + } + + public Boat getOrCreateBoat(Serializable boatId, String boatName, BoatClass boatClass, String sailId, Color boatColor) { + CompetitorStore competitorStore = baseDomainFactory.getCompetitorStore(); + Boat domainBoat = competitorStore.getExistingBoatById(boatId); + if (domainBoat == null) { + domainBoat = baseDomainFactory.getCompetitorStore().getOrCreateBoat(boatId, boatName, boatClass, sailId, boatColor); + } + return domainBoat; } private DynamicTeam createTeam(String name, Nationality nationality, UUID competitorId) { @@ -373,7 +451,8 @@ public class DomainFactoryImpl implements DomainFactory { // Then, there is no way but restart the server to get rid of this stale cache entry here. if (result == null) { result = new RegattaImpl(raceLogStore, regattaLogStore, RegattaImpl.getDefaultName( - defaultRegattaNameAndBoatClass.getA(), boatClass.getName()), boatClass, /*startDate*/ null, /*endDate*/ null, + defaultRegattaNameAndBoatClass.getA(), boatClass.getName()), boatClass, + /* canBoatsOfCompetitorsChangePerRace */ false, /*startDate*/ null, /*endDate*/ null, trackedRegattaRegistry, // use the low-point system as the default scoring scheme getBaseDomainFactory().createScoringScheme(ScoringSchemeType.LOW_POINT), race.getId(), null); @@ -399,7 +478,7 @@ public class DomainFactoryImpl implements DomainFactory { @Override public Iterable getUpdateReceivers(DynamicTrackedRegatta trackedRegatta, IRace tractracRace, WindStore windStore, long delayToLiveInMillis, Simulator simulator, DynamicRaceDefinitionSet raceDefinitionSetToUpdate, - TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver, + TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver, LeaderboardGroupResolver leaderboardGroupResolver, URI courseDesignUpdateURI, String tracTracUsername, String tracTracPassword, IEventSubscriber eventSubscriber, IRaceSubscriber raceSubscriber, boolean useInternalMarkPassingAlgorithm, long timeoutInMilliseconds, ReceiverType... types) { @@ -412,7 +491,7 @@ public class DomainFactoryImpl implements DomainFactory { raceDefinitionSetToUpdate, delayToLiveInMillis, WindTrack.DEFAULT_MILLISECONDS_OVER_WHICH_TO_AVERAGE_WIND, simulator, courseDesignUpdateURI, tracTracUsername, tracTracPassword, eventSubscriber, raceSubscriber, - useInternalMarkPassingAlgorithm, raceLogResolver, timeoutInMilliseconds)); + useInternalMarkPassingAlgorithm, raceLogResolver, leaderboardGroupResolver, timeoutInMilliseconds)); break; case MARKPOSITIONS: result.add(new MarkPositionReceiver( @@ -444,11 +523,11 @@ public class DomainFactoryImpl implements DomainFactory { public Iterable getUpdateReceivers(DynamicTrackedRegatta trackedRegatta, long delayToLiveInMillis, Simulator simulator, WindStore windStore, DynamicRaceDefinitionSet raceDefinitionSetToUpdate, TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver, - IRace tractracRace, URI courseDesignUpdateURI, + LeaderboardGroupResolver leaderboardGroupResolver, IRace tractracRace, URI courseDesignUpdateURI, String tracTracUsername, String tracTracPassword, IEventSubscriber eventSubscriber, IRaceSubscriber raceSubscriber, boolean useInternalMarkPassingAlgorithm, long timeoutInMilliseconds) { return getUpdateReceivers(trackedRegatta, tractracRace, windStore, delayToLiveInMillis, simulator, - raceDefinitionSetToUpdate, trackedRegattaRegistry, raceLogResolver, courseDesignUpdateURI, + raceDefinitionSetToUpdate, trackedRegattaRegistry, raceLogResolver, leaderboardGroupResolver, courseDesignUpdateURI, tracTracUsername, tracTracPassword, eventSubscriber, raceSubscriber, useInternalMarkPassingAlgorithm, timeoutInMilliseconds, ReceiverType.RACECOURSE, ReceiverType.MARKPASSINGS, ReceiverType.MARKPOSITIONS, ReceiverType.RACESTARTFINISH, ReceiverType.RAWPOSITIONS, @@ -501,9 +580,8 @@ public class DomainFactoryImpl implements DomainFactory { } @Override - public DynamicTrackedRace getOrCreateRaceDefinitionAndTrackedRace(DynamicTrackedRegatta trackedRegatta, UUID raceId, - String raceName, Iterable competitors, BoatClass boatClass, - Map competitorBoats, Course course, Iterable sidelines, WindStore windStore, + public DynamicTrackedRace getOrCreateRaceDefinitionAndTrackedRace(DynamicTrackedRegatta trackedRegatta, UUID raceId, + String raceName, BoatClass boatClass, Map competitorsAndBoats, Course course, Iterable sidelines, WindStore windStore, long delayToLiveInMillis, long millisecondsOverWhichToAverageWind, DynamicRaceDefinitionSet raceDefinitionSetToUpdate, URI tracTracUpdateURI, UUID tracTracEventUuid, String tracTracUsername, String tracTracPassword, boolean ignoreTracTracMarkPassings, RaceLogResolver raceLogResolver, @@ -512,7 +590,7 @@ public class DomainFactoryImpl implements DomainFactory { RaceDefinition raceDefinition = raceCache.get(raceId); if (raceDefinition == null) { logger.info("Creating RaceDefinitionImpl for race "+raceName); - raceDefinition = new RaceDefinitionImpl(raceName, course, boatClass, competitors, competitorBoats, raceId); + raceDefinition = new RaceDefinitionImpl(raceName, course, boatClass, competitorsAndBoats, raceId); } else { logger.info("Already found RaceDefinitionImpl for race "+raceName); } @@ -620,19 +698,187 @@ public class DomainFactoryImpl implements DomainFactory { raceDefinitionSetToUpdate, useMarkPassingCalculator, raceLogResolver, Optional.empty()); } + /** + * Obtains the boats for the given competitors. + * There are 2 cases to distinguish:

    + *
  1. Races with boat information in the competitor metadata field
  2. + *
  3. Races without any boat information
  4. + *
+ */ @Override - public Map getBoatsInfoForCompetitors(IRace race, BoatClass defaultBoatClass) { - final Map competitorBoatInfos = new HashMap<>(); + public Map getOrCreateCompetitorsAndTheirBoats(DynamicTrackedRegatta trackedRegatta, LeaderboardGroupResolver leaderboardGroupResolver, + IRace race, BoatClass defaultBoatClass) { + final CompetitorStore competitorAndBoatStore = baseDomainFactory.getCompetitorStore(); + final Map competitorsAndBoats = new HashMap<>(); + Regatta regatta = trackedRegatta.getRegatta(); + LeaderboardGroup leaderboardGroup = leaderboardGroupResolver.resolveLeaderboardGroupByRegattaName(regatta.getName()); + AtomicBoolean leaderboardGroupConsistencyChecked = new AtomicBoolean(false); getCompetingCompetitors(race).forEach(rc->{ - Util.Triple competitorBoatInfo = getMetadataParser().parseCompetitorBoat(rc); - Competitor existingCompetitor = getOrCreateCompetitor(rc.getCompetitor()); - if (existingCompetitor != null && competitorBoatInfo != null) { - Boat boatOfCompetitor = new BoatImpl(competitorBoatInfo.getA(), defaultBoatClass, - competitorBoatInfo.getB(), AbstractColor.getCssColor(competitorBoatInfo.getC())); - competitorBoatInfos.put(existingCompetitor, boatOfCompetitor); + Serializable competitorId = rc.getCompetitor().getId(); + BoatMetaData competitorBoatInfo = getMetadataParser().parseCompetitorBoat(rc); + // If the TracTrac race contains boat metadata we assume the regatta can have changing boats per race. + // As the attribute 'canBoatsOfCompetitorsChangePerRace' is new and 'false' is the default value + // we need to set it's value to true for the regatta, but only if the regatta is of type MigratableRegattaImpl. + // For the check for migration we obtain the monitor on the regatta here; the + // migrateCanBoatsOfCompetitorsChangePerRace() method is "synchronized" and this way we guarantee that + // the regatta will not be migrated from anywhere else while we're checking for its migration here. + synchronized (regatta) { + if (competitorBoatInfo != null && regatta.canBoatsOfCompetitorsChangePerRace() == false) { + // we need to set this to true for the regatta to make it possible to create the boat/competitor mappings + if (regatta instanceof MigratableRegatta) { + MigratableRegatta migratableRegatta = (MigratableRegatta) regatta; + migratableRegatta.migrateCanBoatsOfCompetitorsChangePerRace(); + if (!leaderboardGroupConsistencyChecked.get()) { + checkConsistencyOfRegattaTypeInSeries(leaderboardGroup, regatta); + leaderboardGroupConsistencyChecked.set(true); + } + } else { + logger.log(Level.SEVERE, "Bug2822 DB-Migration: Regatta " + regatta.getName() + + " has wrong type 'canBoatsOfCompetitorsChangePerRace' but can't be migrated because it is not of type MigratableRegattaImpl"); + } + } else { + if (!leaderboardGroupConsistencyChecked.get()) { + checkConsistencyOfRegattaTypeInSeries(leaderboardGroup, regatta); + leaderboardGroupConsistencyChecked.set(true); + } + } + } + // Case 1 + if (regatta.canBoatsOfCompetitorsChangePerRace()) { + // create an unique identifier for the boat and try to find it in the boatStore + Serializable boatId; + String sailId; + if (competitorBoatInfo != null) { + boatId = createUniqueBoatIdentifierFromBoatMetadata(regatta, leaderboardGroup, competitorBoatInfo); + sailId = competitorBoatInfo.getId(); // we take here the boatId as sailID which is a number like 1, 2, 3 + } else { + boatId = createUniqueBoatIdentifierFromCompetitor(rc.getCompetitor()); + sailId = rc.getCompetitor().getShortName(); + } + Boat existingBoat = competitorAndBoatStore.getExistingBoatById(boatId); + Competitor existingCompetitor = competitorAndBoatStore.getExistingCompetitorById(competitorId); + Competitor competitorToUse; + if (existingCompetitor != null) { + // ensure that check for and execution of migration for existingCompetitor happens at most once: + final boolean needToMigrate; + synchronized (competitorsCurrentlyBeingMigrated) { + if (!competitorsCurrentlyBeingMigrated.contains(existingCompetitor)) { + // we need to check if we need to migrate the competitor to have a separate boat + needToMigrate = existingCompetitor.hasBoat(); + if (needToMigrate) { + competitorsCurrentlyBeingMigrated.add(existingCompetitor); + } + } else { + // migration is already underway + needToMigrate = false; + logger.fine("Bug2822 DB-Migration: Not migrating competitor "+existingCompetitor.getName()+" because a migration for it is already ongoing"); + } + } + if (needToMigrate) { + competitorToUse = competitorAndBoatStore.migrateToCompetitorWithoutBoat((CompetitorWithBoat) existingCompetitor); + // we might also want to update the shortName field of the competitor during migration (instead of using sailID) + if (competitorToUse.getShortName() != rc.getCompetitor().getShortName()) { + boolean savedIsCompetitorToUpdateDuringGetOrCreate = competitorAndBoatStore.isCompetitorToUpdateDuringGetOrCreate(competitorToUse); + competitorAndBoatStore.allowCompetitorResetToDefaults(competitorToUse); + competitorAndBoatStore.updateCompetitor(competitorToUse.getId().toString(), competitorToUse.getName(), + rc.getCompetitor().getShortName(), competitorToUse.getColor(), + competitorToUse.getEmail(), competitorToUse.getNationality(), competitorToUse.getTeam().getImage(), + competitorToUse.getFlagImage(), competitorToUse.getTimeOnTimeFactor(), + competitorToUse.getTimeOnDistanceAllowancePerNauticalMile(), competitorToUse.getSearchTag()); + if (savedIsCompetitorToUpdateDuringGetOrCreate) { + competitorAndBoatStore.allowCompetitorResetToDefaults(competitorToUse); + } + } + // It's safe to modify the competitor contents between addition and removal to a HashSet + // because CompetitorImpl.hashCode/equals are based solely on Java object identity + competitorsCurrentlyBeingMigrated.remove(existingCompetitor); + } else { + competitorToUse = existingCompetitor; + } + } else { + competitorToUse = getOrCreateCompetitor(rc.getCompetitor()); + } + Boat boatToUse; + if (existingBoat != null) { + boatToUse = existingBoat; + } else { + if (competitorBoatInfo != null) { + boatToUse = getOrCreateBoat(boatId, competitorBoatInfo.getName(), defaultBoatClass, sailId, AbstractColor.getCssColor(competitorBoatInfo.getColor())); + } else { + boatToUse = getOrCreateBoat(boatId, /* boat name*/ null, defaultBoatClass, sailId, null); + } + } + competitorsAndBoats.put(competitorToUse, boatToUse); + } else { + // Case 2 we assume here that the boat is contained in competitor as it's always the same + CompetitorWithBoat competitorWithBoat = getOrCreateCompetitorWithBoat(rc.getCompetitor()); + competitorsAndBoats.put(competitorWithBoat, competitorWithBoat.getBoat()); } }); - return competitorBoatInfos; + return competitorsAndBoats; + } + + private boolean checkConsistencyOfRegattaTypeInSeries(LeaderboardGroup leaderboardGroup, Regatta regatta) { + boolean result = true; + final boolean canBoatsOfCompetitorsChangePerRace = regatta.canBoatsOfCompetitorsChangePerRace(); + String boatsCanChangelogMessage = ""; + if (leaderboardGroup != null) { + for (Leaderboard leaderboard : leaderboardGroup.getLeaderboards()) { + if (leaderboard instanceof RegattaLeaderboard) { + RegattaLeaderboard regattaLeaderboard = (RegattaLeaderboard) leaderboard; + boolean regattaHasTrackedRaces = Util.size(regattaLeaderboard.getTrackedRaces()) > 0; + boolean regattaCanBoatsChange = regattaLeaderboard.getRegatta().canBoatsOfCompetitorsChangePerRace(); + boatsCanChangelogMessage += regattaLeaderboard.getName(); + boatsCanChangelogMessage += ": trackedRaces=" + regattaHasTrackedRaces; + boatsCanChangelogMessage += ": canBoatsChange=" + regattaCanBoatsChange + "; "; + if (regattaHasTrackedRaces && canBoatsOfCompetitorsChangePerRace != regattaCanBoatsChange) { + result = false; + break; + } + } + } + } + if (result == false) { + logger.log(Level.SEVERE, "Bug2822 DB-Migration: Regatta " + regatta.getName() + + " has different value of 'canBoatsOfCompetitorsChangePerRace' than other regattas in leaderboardGroup " + leaderboardGroup.getName()); + logger.log(Level.SEVERE, "Bug2822 DB-Migration: leaderboardGroup state: " + boatsCanChangelogMessage); + } + return result; + } + + /** + * Create an unique key for a boat derived from the regatta, a leaderboardGroup (can be null) and the boat metadata + * @return a unique boat key + */ + private Serializable createUniqueBoatIdentifierFromBoatMetadata(Regatta regatta, LeaderboardGroupBase leaderboardGroup, BoatMetaData boatMetadata) { + Serializable boatIdentifier = null; + if (boatMetadata.getUuid() != null) { + boatIdentifier = boatMetadata.getUuid(); + } else { + if (leaderboardGroup != null) { + boatIdentifier = buildEscapedCompositeIdentifier(leaderboardGroup.getId().toString(), boatMetadata.getId()); + } else { + boatIdentifier = buildEscapedCompositeIdentifier(regatta.getId().toString(), boatMetadata.getId()); + } + } + return boatIdentifier; + } + + /** + * Create an unique key for a boat derived from the Id of the competitor + * @return the unique key (per tractrac event) + */ + private String createUniqueBoatIdentifierFromCompetitor(ICompetitor competitor) { + String boatIdentifier = competitor.getId().toString(); + return boatIdentifier; + } + + private String buildEscapedCompositeIdentifier(String id1, String id2) { + return String.format("%s#%s", escapeIdentifierFragment(id1), escapeIdentifierFragment(id2)); + } + + private String escapeIdentifierFragment(String fragment) { + return fragment.replace("\\", "\\\\").replace("#", "\\#"); } /** @@ -642,23 +888,18 @@ public class DomainFactoryImpl implements DomainFactory { private Stream getCompetingCompetitors(IRace race) { return race.getRaceCompetitors().stream().filter(rc->!rc.getCompetitor().isNonCompeting()); } - + @Override - public Util.Pair, BoatClass> getCompetitorsAndDominantBoatClass(IRace race) { + public BoatClass resolveDominantBoatClassOfRace(IRace race) { List competitorClasses = new ArrayList(); - final List competitors = new ArrayList(); getCompetingCompetitors(race).forEach(rc->{ // also add those whose race class doesn't match the dominant one (such as camera boats) // because they may still send data that we would like to record in some tracks - competitors.add(getOrCreateCompetitor(rc.getCompetitor())); competitorClasses.add(rc.getCompetitor().getCompetitorClass()); }); - BoatClass dominantBoatClass = getDominantBoatClass(competitorClasses); - Util.Pair, BoatClass> competitorsAndDominantBoatClass = new com.sap.sse.common.Util.Pair, BoatClass>( - competitors, dominantBoatClass); - return competitorsAndDominantBoatClass; + return getDominantBoatClass(competitorClasses); } - + private BoatClass getDominantBoatClass(Collection competitorClasses) { List competitorClassNames = new ArrayList<>(); for (ICompetitorClass competitorClass : competitorClasses) { @@ -710,19 +951,22 @@ public class DomainFactoryImpl implements DomainFactory { @Override public TracTracRaceTracker createRaceTracker(RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, WindStore windStore, TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver, + LeaderboardGroupResolver leaderboardGroupResolver, RaceTrackingConnectivityParametersImpl connectivityParams, long timeoutInMilliseconds) - throws URISyntaxException, CreateModelException, SubscriberInitializationException, IOException, - InterruptedException { - return new TracTracRaceTrackerImpl(this, raceLogStore, regattaLogStore, windStore, trackedRegattaRegistry, raceLogResolver, connectivityParams, timeoutInMilliseconds); + throws URISyntaxException, SubscriberInitializationException, IOException, InterruptedException { + return new TracTracRaceTrackerImpl(this, raceLogStore, regattaLogStore, windStore, trackedRegattaRegistry, + raceLogResolver, leaderboardGroupResolver, connectivityParams, timeoutInMilliseconds); } @Override - public RaceTracker createRaceTracker(Regatta regatta, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, WindStore windStore, - TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver, RaceTrackingConnectivityParametersImpl connectivityParams, long timeoutInMilliseconds) - throws URISyntaxException, CreateModelException, - SubscriberInitializationException, IOException, InterruptedException { + public RaceTracker createRaceTracker(Regatta regatta, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, + WindStore windStore, TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver, + LeaderboardGroupResolver leaderboardGroupResolver, + RaceTrackingConnectivityParametersImpl connectivityParams, long timeoutInMilliseconds) + throws URISyntaxException, CreateModelException, SubscriberInitializationException, IOException, + InterruptedException { return new TracTracRaceTrackerImpl(regatta, this, raceLogStore, regattaLogStore, windStore, trackedRegattaRegistry, - raceLogResolver, connectivityParams, timeoutInMilliseconds); + raceLogResolver, leaderboardGroupResolver, connectivityParams, timeoutInMilliseconds); } @Override diff --git a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/MarkPassingReceiver.java b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/MarkPassingReceiver.java index 12ed394fde0..3b64fa85b4d 100755 --- a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/MarkPassingReceiver.java +++ b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/MarkPassingReceiver.java @@ -7,6 +7,7 @@ import java.util.List; import java.util.Map; import java.util.logging.Logger; +import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.Course; import com.sap.sailing.domain.base.Waypoint; import com.sap.sailing.domain.tracking.DynamicTrackedRace; @@ -56,47 +57,49 @@ public class MarkPassingReceiver extends AbstractReceiverWithQueue event) { if (!event.getA().getCompetitor().isNonCompeting()) { - System.out.print("L"); // as in "Leg" - DynamicTrackedRace trackedRace = getTrackedRace(event.getA().getRace()); - if (trackedRace != null) { - Course course = trackedRace.getRace().getCourse(); - Iterator waypointsIter = course.getWaypoints().iterator(); - Map passingsByWaypoint = new HashMap(); - // Note: the entries always describe all mark passings for the competitor so far in the current race in - // order - for (IControlPassing passing : event.getB().getPassings()) { - IControl controlPointPassed = passing.getControl(); - com.sap.sailing.domain.base.ControlPoint domainControlPoint = getDomainFactory() - .getOrCreateControlPoint(new ControlPointAdapter(controlPointPassed)); - Waypoint passed = findWaypointForControlPoint(trackedRace, waypointsIter, domainControlPoint); - if (passed != null) { - TimePoint time = new MillisecondsTimePoint(passing.getTimestamp()); - MarkPassing markPassing = getDomainFactory().createMarkPassing(time, passed, - getDomainFactory().getOrCreateCompetitor(event.getA().getCompetitor())); - passingsByWaypoint.put(passed, markPassing); - } else { - logger.warning("Didn't find waypoint in course " + course + " for mark passing around " - + passing.getControl()); - } - } - List markPassings = new ArrayList(); - for (Waypoint waypoint : course.getWaypoints()) { - MarkPassing passing = passingsByWaypoint.get(waypoint); - if (passing != null) { - markPassings.add(passing); - } - } - logger.fine("Received mark passings in race "+trackedRace.getRace().getName()+": "+markPassings); - if (getSimulator() != null) { - getSimulator().delayMarkPassings(getDomainFactory().getOrCreateCompetitor(event.getA().getCompetitor()), markPassings); - } else { - trackedRace.updateMarkPassings(getDomainFactory().getOrCreateCompetitor(event.getA().getCompetitor()), - markPassings); - } - } else { - logger.warning("Couldn't find tracked race for race " + event.getA().getRace().getName() - + ". Dropping mark passing event " + event); - } + System.out.print("L"); // as in "Leg" + DynamicTrackedRace trackedRace = getTrackedRace(event.getA().getRace()); + if (trackedRace != null) { + Course course = trackedRace.getRace().getCourse(); + Iterator waypointsIter = course.getWaypoints().iterator(); + Map passingsByWaypoint = new HashMap(); + // Note: the entries always describe all mark passings for the competitor so far in the current race in order + for (IControlPassing passing : event.getB().getPassings()) { + IControl controlPointPassed = passing.getControl(); + com.sap.sailing.domain.base.ControlPoint domainControlPoint = getDomainFactory() + .getOrCreateControlPoint(new ControlPointAdapter(controlPointPassed)); + Waypoint passed = findWaypointForControlPoint(trackedRace, waypointsIter, domainControlPoint); + if (passed != null) { + TimePoint time = new MillisecondsTimePoint(passing.getTimestamp()); + Competitor competitor = getDomainFactory().resolveCompetitor(event.getA().getCompetitor()); + MarkPassing markPassing = getDomainFactory().createMarkPassing(time, passed, competitor); + passingsByWaypoint.put(passed, markPassing); + } else { + logger.warning("Didn't find waypoint in course " + course + " for mark passing around " + passing.getControl()); + } + } + List markPassings = new ArrayList(); + for (Waypoint waypoint : course.getWaypoints()) { + MarkPassing passing = passingsByWaypoint.get(waypoint); + if (passing != null) { + markPassings.add(passing); + } + } + logger.fine("Received mark passings in race " + trackedRace.getRace().getName() + ": " + markPassings); + Competitor competitor = getDomainFactory().resolveCompetitor(event.getA().getCompetitor()); + if (competitor != null) { + if (getSimulator() != null) { + getSimulator().delayMarkPassings(competitor, markPassings); + } else { + trackedRace.updateMarkPassings(competitor, markPassings); + } + } else { + logger.warning("Didn't find competitor for mark passings"); + } + } else { + logger.warning("Couldn't find tracked race for race " + event.getA().getRace().getName() + + ". Dropping mark passing event " + event); + } } } diff --git a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/MetadataParserImpl.java b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/MetadataParserImpl.java index 904fd172ede..bbb6bbb0f70 100755 --- a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/MetadataParserImpl.java +++ b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/MetadataParserImpl.java @@ -81,7 +81,43 @@ public class MetadataParserImpl implements MetadataParser { return id; } } - + + public class BoatMetaDataImpl extends NamedImpl implements BoatMetaData { + private static final long serialVersionUID = 1L; + private String id; + private UUID uuid; + private final String color; + + public BoatMetaDataImpl(String boatUuid, String boatId, String boatName, String boatColor) { + super(boatName); + this.id = boatId; + this.uuid = null; + this.color = boatColor; + if (boatUuid != null) { + try { + this.uuid = UUID.fromString(boatUuid); + } catch (IllegalArgumentException e) { + // fallback, at least id is set to the provided string + if (id == null) { + id = boatUuid; + } + } + } + } + + public UUID getUuid() { + return uuid; + } + + public String getId() { + return id; + } + + public String getColor() { + return color; + } + } + /** * Parses the route metadata for additional course information * The 'passing side' for each course waypoint is encoded like this... @@ -212,10 +248,11 @@ public class MetadataParserImpl implements MetadataParser { } @Override - public Util.Triple parseCompetitorBoat(IRaceCompetitor competitor) { - Util.Triple result = null; + public BoatMetaData parseCompetitorBoat(IRaceCompetitor competitor) { + BoatMetaData result = null; String parsedBoatName = null; String parsedBoatId = null; + String parsedBoatUuid = null; String parsedColor = null; String raceCompetitorMetadataString = competitor.getMetadata() != null ? competitor.getMetadata().getText() : null; if (raceCompetitorMetadataString != null) { @@ -225,12 +262,14 @@ public class MetadataParserImpl implements MetadataParser { parsedBoatName = entry.getValue(); } else if (entry.getKey().equals("boatId")) { parsedBoatId = entry.getValue(); + } else if (entry.getKey().equals("boatUuid")) { + parsedBoatUuid = entry.getValue(); } else if (entry.getKey().equals("boatColor")) { parsedColor = entry.getValue(); } } - if (parsedBoatName != null && parsedBoatId != null && parsedColor != null) { - result = new Util.Triple(parsedBoatName, parsedBoatId, parsedColor); + if (parsedBoatName != null && (parsedBoatId != null || parsedBoatUuid != null) && parsedColor != null) { + result = new BoatMetaDataImpl(parsedBoatUuid, parsedBoatId, parsedBoatName, parsedColor); } } return result; diff --git a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/RaceCourseReceiver.java b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/RaceCourseReceiver.java index 1ae14c249a3..c3e67d7c98f 100755 --- a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/RaceCourseReceiver.java +++ b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/RaceCourseReceiver.java @@ -21,9 +21,11 @@ import com.sap.sailing.domain.base.RaceDefinition; import com.sap.sailing.domain.base.Regatta; import com.sap.sailing.domain.base.Sideline; import com.sap.sailing.domain.common.PassingInstruction; +import com.sap.sailing.domain.leaderboard.LeaderboardGroupResolver; import com.sap.sailing.domain.tracking.DynamicRaceDefinitionSet; import com.sap.sailing.domain.tracking.DynamicTrackedRace; import com.sap.sailing.domain.tracking.DynamicTrackedRegatta; +import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.domain.tracking.WindStore; import com.sap.sailing.domain.tractracadapter.DomainFactory; import com.sap.sailing.domain.tractracadapter.TracTracControlPoint; @@ -66,16 +68,18 @@ public class RaceCourseReceiver extends AbstractReceiverWithQueue, BoatClass> competitorsAndDominantBoatClass = getDomainFactory().getCompetitorsAndDominantBoatClass(tractracRace); - Map competitorBoats = getDomainFactory().getBoatsInfoForCompetitors(tractracRace, competitorsAndDominantBoatClass.getB()); + BoatClass dominantBoatClass = getDomainFactory().resolveDominantBoatClassOfRace(tractracRace); + Map competitorAndBoats = getDomainFactory().getOrCreateCompetitorsAndTheirBoats(getTrackedRegatta(), leaderboardGroupResolver, tractracRace, dominantBoatClass); trackedRace = getDomainFactory().getOrCreateRaceDefinitionAndTrackedRace( - getTrackedRegatta(), tractracRace.getId(), tractracRace.getName(), competitorsAndDominantBoatClass.getA(), - competitorsAndDominantBoatClass.getB(), competitorBoats, course, sidelines, windStore, delayToLiveInMillis, + getTrackedRegatta(), tractracRace.getId(), tractracRace.getName(), + dominantBoatClass, competitorAndBoats, course, sidelines, windStore, delayToLiveInMillis, millisecondsOverWhichToAverageWind, raceDefinitionSetToUpdate, tracTracUpdateURI, getTracTracEvent().getId(), tracTracUsername, tracTracPassword, useInternalMarkPassingAlgorithm, raceLogResolver, tr->updateRaceTimes(tractracRace, tr), tractracRace); addAllMarksFromCourseArea(trackedRace); @@ -252,4 +256,8 @@ public class RaceCourseReceiver extends AbstractReceiverWithQueue bravoFixTrack = trackedRace.getOrCreateSensorTrack(competitor, BravoFixTrack.TRACK_NAME, () -> new CompetitorBravoFixTrackImpl(competitor, BravoFixTrack.TRACK_NAME, /* hasExtendedFixes */ false, diff --git a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/TracTracRaceTrackerImpl.java b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/TracTracRaceTrackerImpl.java index 704a24e08aa..15e969eb026 100755 --- a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/TracTracRaceTrackerImpl.java +++ b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/TracTracRaceTrackerImpl.java @@ -21,13 +21,13 @@ import java.util.logging.Level; import java.util.logging.Logger; import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver; -import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.Course; import com.sap.sailing.domain.base.Fleet; import com.sap.sailing.domain.base.RaceDefinition; import com.sap.sailing.domain.base.Regatta; import com.sap.sailing.domain.base.Series; import com.sap.sailing.domain.common.TrackedRaceStatusEnum; +import com.sap.sailing.domain.leaderboard.LeaderboardGroupResolver; import com.sap.sailing.domain.racelog.RaceLogStore; import com.sap.sailing.domain.regattalog.RegattaLogStore; import com.sap.sailing.domain.tracking.AbstractRaceTrackerImpl; @@ -261,10 +261,10 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl */ TracTracRaceTrackerImpl(DomainFactory domainFactory, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, WindStore windStore, TrackedRegattaRegistry trackedRegattaRegistry, - RaceLogResolver raceLogResolver, RaceTrackingConnectivityParametersImpl connectivityParams, long timeoutInMilliseconds) + RaceLogResolver raceLogResolver, LeaderboardGroupResolver leaderboardGroupResolver, RaceTrackingConnectivityParametersImpl connectivityParams, long timeoutInMilliseconds) throws URISyntaxException, SubscriberInitializationException, IOException, InterruptedException { this(/* regatta */ null, domainFactory, raceLogStore, regattaLogStore, windStore, trackedRegattaRegistry, - raceLogResolver, connectivityParams, timeoutInMilliseconds); + raceLogResolver, leaderboardGroupResolver, connectivityParams, timeoutInMilliseconds); } /** @@ -283,7 +283,7 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl */ TracTracRaceTrackerImpl(final Regatta regatta, DomainFactory domainFactory, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, WindStore windStore, TrackedRegattaRegistry trackedRegattaRegistry, - RaceLogResolver raceLogResolver, RaceTrackingConnectivityParametersImpl connectivityParams, long timeoutInMilliseconds) + RaceLogResolver raceLogResolver, LeaderboardGroupResolver leaderboardGroupResolver, RaceTrackingConnectivityParametersImpl connectivityParams, long timeoutInMilliseconds) throws URISyntaxException, SubscriberInitializationException, IOException, InterruptedException { super(connectivityParams); final URL paramURL = connectivityParams.getParamURL(); @@ -336,10 +336,7 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl @Override public void updateCompetitor(ICompetitor competitor) { if (!competitor.isNonCompeting()) { - final Competitor domainCompetitor = TracTracRaceTrackerImpl.this.domainFactory.getOrCreateCompetitor(competitor); - logger.info("Competitor "+competitor+" was updated on TracTrac side. Maybe consider updating in competitor store as well. "+ - "TracTrac competitor maps to "+domainCompetitor.getName()+" with sail ID "+domainCompetitor.getBoat().getSailID()+ - " and boat class "+domainCompetitor.getBoat().getBoatClass().getName()); + TracTracRaceTrackerImpl.this.domainFactory.updateCompetitor(competitor); } } @@ -399,7 +396,7 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl } receivers = new HashSet(); for (Receiver receiver : domainFactory.getUpdateReceivers(getTrackedRegatta(), delayToLiveInMillis, - simulator, windStore, this, trackedRegattaRegistry, raceLogResolver, tractracRace, + simulator, windStore, this, trackedRegattaRegistry, raceLogResolver, leaderboardGroupResolver, tractracRace, tracTracUpdateURI, tracTracUsername, tracTracPassword, eventSubscriber, raceSubscriber, useInternalMarkPassingAlgorithm, timeoutInMilliseconds)) { receivers.add(receiver); diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/DomainFactory.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/DomainFactory.java index 685a59f5ad0..b39824fead1 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/DomainFactory.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/DomainFactory.java @@ -3,12 +3,15 @@ package com.sap.sailing.domain.base; import java.io.IOException; import java.io.InputStream; import java.util.List; +import java.util.Map; import com.sap.sailing.domain.base.impl.DomainFactoryImpl; import com.sap.sailing.domain.common.Placemark; import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; import com.sap.sailing.domain.common.ScoringSchemeType; +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.sailing.domain.common.dto.FleetDTO; import com.sap.sailing.domain.common.dto.PlacemarkDTO; import com.sap.sailing.domain.common.dto.RaceDTO; @@ -22,6 +25,7 @@ import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.domain.tracking.TrackedRegattaRegistry; import com.sap.sse.common.IsManagedByCache; import com.sap.sse.common.TimePoint; +import com.sap.sse.common.Util.Pair; import com.sap.sse.util.ObjectInputStreamResolvingAgainstCache; public interface DomainFactory extends SharedDomainFactory { @@ -33,7 +37,7 @@ public interface DomainFactory extends SharedDomainFactory { static DomainFactory INSTANCE = new DomainFactoryImpl((srlid)->null); MarkPassing createMarkPassing(TimePoint timePoint, Waypoint waypoint, Competitor competitor); - + /** * When de-serializing objects of types whose instances that are managed and cached by this domain factory, * de-serialized instances need to be replaced by / resolved to the counterparts already known by this factory. The @@ -55,7 +59,15 @@ public interface DomainFactory extends SharedDomainFactory { ScoringScheme createScoringScheme(ScoringSchemeType scoringSchemeType); - CompetitorDTO convertToCompetitorDTO(Competitor c); + CompetitorWithoutBoatDTO convertToCompetitorDTO(Competitor competitor); + + CompetitorDTO convertToCompetitorWithOptionalBoatDTO(Competitor competitor); + + CompetitorDTO convertToCompetitorDTO(Competitor competitor, Boat boat); + + Map convertToCompetitorAndBoatDTOs(Map competitorsAndBoats); + + BoatDTO convertToBoatDTO(Boat boat); FleetDTO convertToFleetDTO(Fleet fleet); @@ -66,8 +78,12 @@ public interface DomainFactory extends SharedDomainFactory { PlacemarkDTO convertToPlacemarkDTO(Placemark placemark); + List getCompetitorDTOList(Map competitors); + List getCompetitorDTOList(Iterable competitors); + List getCompetitorDTOList(List> competitors); + TrackedRaceDTO createTrackedRaceDTO(TrackedRace trackedRace); TrackedRaceStatisticsDTO createTrackedRaceStatisticsDTO(TrackedRace trackedRace, Leaderboard leaderboard, RaceColumn raceColumn, diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/MigratableRegatta.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/MigratableRegatta.java new file mode 100755 index 00000000000..a82e0e9ac3f --- /dev/null +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/MigratableRegatta.java @@ -0,0 +1,5 @@ +package com.sap.sailing.domain.base; + +public interface MigratableRegatta extends Regatta { + void migrateCanBoatsOfCompetitorsChangePerRace(); +} diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/PairingListLeaderboardAdapter.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/PairingListLeaderboardAdapter.java index 61bddac315a..b3d113e8029 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/PairingListLeaderboardAdapter.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/PairingListLeaderboardAdapter.java @@ -2,9 +2,8 @@ package com.sap.sailing.domain.base; import java.util.ArrayList; import java.util.List; +import java.util.Map.Entry; -import com.sap.sailing.domain.base.impl.BoatClassImpl; -import com.sap.sailing.domain.base.impl.BoatImpl; import com.sap.sse.common.Util.Pair; import com.sap.sse.pairinglist.PairingList; @@ -12,13 +11,9 @@ public class PairingListLeaderboardAdapter implements PairingList> getCompetitors(RaceColumn raceColumn, Fleet fleet) { - // TODO add boat to return value (bug4403) List> result = new ArrayList<>(); - int boatIndex = 0; - for (Competitor competitor : raceColumn.getCompetitorsRegisteredInRacelog(fleet)) { - result.add(new Pair(competitor, - new BoatImpl("Boat " + (boatIndex + 1), new BoatClassImpl("49er", true), "DE" + boatIndex))); - boatIndex++; + for (Entry competitorAndBoat : raceColumn.getAllCompetitorsAndTheirBoats(fleet).entrySet()) { + result.add(new Pair(competitorAndBoat.getKey(), competitorAndBoat.getValue())); } return result; } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/RaceColumn.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/RaceColumn.java index d1651c7bf44..57ad108b937 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/RaceColumn.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/RaceColumn.java @@ -1,5 +1,7 @@ package com.sap.sailing.domain.base; +import java.util.Map; + import com.sap.sailing.domain.abstractlog.race.RaceLog; import com.sap.sailing.domain.abstractlog.race.RaceLogCourseDesignChangedEvent; import com.sap.sailing.domain.abstractlog.regatta.RegattaLog; @@ -263,6 +265,21 @@ public interface RaceColumn extends Named { * Same as {@link #getAllCompetitors()}, but restricted to the single race identified by the fleet parameter. */ Iterable getAllCompetitors(Fleet fleet); + + /** + * Provides the combined set of competitors and their boats from all {@link #getTrackedRace(Fleet) tracked races attached to this + * column} or, in case a fleet does not have a tracked race attached, the competitors registered through the + * respective {@link RaceLog} {@link #getRaceLog(Fleet)} attached to this column for that fleet (if this is + * {@link #enableCompetitorRegistrationOnRaceLog(Fleet) enabled) or registered through the {@link #getRegattaLog() + * regatta log}. + */ + Map getAllCompetitorsAndTheirBoats(); + + /** + * Same as {@link #getAllCompetitorsAndTheirBoats()}, but restricted to the single race identified by the fleet parameter. + */ + Map getAllCompetitorsAndTheirBoats(Fleet fleet); + /** * Provides the combined set of marks from the courses of all {@link #getTrackedRace(Fleet) tracked races attached @@ -299,27 +316,27 @@ public interface RaceColumn extends Named { Iterable getAvailableMarks(Fleet fleet); /** - * Returns the competitor set registered in the race column's race log associated to the passed fleet. If competitor + * Returns the competitor and their boats registered in the race column's race log associated to the passed fleet. If competitor * registration in RaceLog is {@link #disableCompetitorRegistrationOnRaceLog(Fleet) disabled} or in case of a * MetaLeaderboardColumn an empty set is returned. * - * @return competitors in RaceLog if registration {@link #enableCompetitorRegistrationOnRaceLog(Fleet) enabled} or + * @return competitors and boats in RaceLog if registration {@link #enableCompetitorRegistrationOnRaceLog(Fleet) enabled} or * empty set in case registration is {@link #disableCompetitorRegistrationOnRaceLog(Fleet) disabled} or this * column belongs to a {@link MetaLeaderboard} */ - Iterable getCompetitorsRegisteredInRacelog(Fleet fleet); + Map getCompetitorsRegisteredInRacelog(Fleet fleet); /** * Checks whether competitor registration on RaceLog is enabled. * - * @return boolean if competitor registration on the RaceLog is enabled, false in case this column belongs to a + * @return boolean if competitor and boat registration on the RaceLog is enabled, false in case this column belongs to a * {@link MetaLeaderboard} * * @see #enableCompetitorRegistrationOnRaceLog(Fleet) * @see #disableCompetitorRegistrationOnRaceLog(Fleet) */ boolean isCompetitorRegistrationInRacelogEnabled(Fleet fleet); - + /** * Activates competitor registration on the race column's race log associated to the passed fleet. As a result, * competitor registrations that were added to the race log before this was disabled by @@ -331,7 +348,7 @@ public interface RaceColumn extends Named { /** * Disables competitor registration on the race column's race log associated to the passed fleet. Performs nothing - * in case this column belongs to a {@link MetaLeaderboard}. If there are already competitor registrations on the + * in case this column belongs to a {@link MetaLeaderboard}. If there are already competitor and boat registrations on the * race log, those will not be removed from the log but they will be ignored, and the regatta log's competitor * registrations will be used instead. Re-{@link #enableCompetitorRegistrationOnRaceLog(Fleet) enabling} competitor * registrations in the race log will cause such existing registrations to be honored again. @@ -340,25 +357,44 @@ public interface RaceColumn extends Named { * Performs nothing in case this column belongs to a {@link MetaLeaderboard}. */ void disableCompetitorRegistrationOnRaceLog(Fleet fleet) throws NotRevokableException; - + /** - * Registers a competitor on the the race column's race log associated to the passed fleet. + * Registers a competitor and his boat on the the race column's race log associated to the passed fleet. * * @throws CompetitorRegistrationOnRaceLogDisabledException * thrown if competitor registration is {@link #disableCompetitorRegistrationOnRaceLog(Fleet) disabled} * on racelog as well as if RaceColumn belongs to a {@link MetaLeaderboard} */ - void registerCompetitor(Competitor competitor, Fleet fleet) throws CompetitorRegistrationOnRaceLogDisabledException; + void registerCompetitor(Competitor competitor, Boat boat, Fleet fleet) throws CompetitorRegistrationOnRaceLogDisabledException; /** - * Registers competitors on the the race column's race log associated to the passed fleet. + * Registers competitors including their boats on the the race column's race log associated to the passed fleet. * * @throws CompetitorRegistrationOnRaceLogDisabledException * thrown if competitor registration is disabled on racelog as well as if RaceColumn belongs to a * {@link MetaLeaderboard} */ - void registerCompetitors(Iterable competitor, Fleet fleet) throws CompetitorRegistrationOnRaceLogDisabledException; + void registerCompetitors(Map competitorsAndBoats, Fleet fleet) throws CompetitorRegistrationOnRaceLogDisabledException; + + /** + * Registers a competitor and his boat on the the race column's race log associated to the passed fleet. + * + * @throws CompetitorRegistrationOnRaceLogDisabledException + * thrown if competitor registration is {@link #disableCompetitorRegistrationOnRaceLog(Fleet) disabled} + * on racelog as well as if RaceColumn belongs to a {@link MetaLeaderboard} + */ + void registerCompetitor(CompetitorWithBoat competitorWithBoat, Fleet fleet) throws CompetitorRegistrationOnRaceLogDisabledException; + + /** + * Registers competitors including their boats on the the race column's race log associated to the passed fleet. + * + * @throws CompetitorRegistrationOnRaceLogDisabledException + * thrown if competitor registration is disabled on racelog as well as if RaceColumn belongs to a + * {@link MetaLeaderboard} + */ + void registerCompetitors(Iterable competitorWithBoats, Fleet fleet) throws CompetitorRegistrationOnRaceLogDisabledException; + /** * Deregisters a competitor on the the race column's race log associated to the passed fleet. * @@ -376,5 +412,4 @@ public interface RaceColumn extends Named { * {@link MetaLeaderboard} */ void deregisterCompetitors(Iterable currentlyRegisteredCompetitors, Fleet fleet) throws CompetitorRegistrationOnRaceLogDisabledException; - } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/RaceDefinition.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/RaceDefinition.java index c83b75724f9..5314fdb4876 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/RaceDefinition.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/RaceDefinition.java @@ -1,6 +1,7 @@ package com.sap.sailing.domain.base; import java.io.Serializable; +import java.util.Map; import com.sap.sailing.domain.common.RaceCompetitorIdsAsStringWithMD5Hash; import com.sap.sse.common.NamedWithID; @@ -24,8 +25,12 @@ public interface RaceDefinition extends NamedWithID { Iterable getCompetitors(); + Iterable getBoats(); + Competitor getCompetitorById(Serializable competitorID); + Map getCompetitorsAndTheirBoats(); + /** * Gets the boat used by the competitor for this race. */ diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/Regatta.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/Regatta.java index e03ad7197e0..62bb9f38604 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/Regatta.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/Regatta.java @@ -128,6 +128,8 @@ public interface Regatta extends NamedWithID, IsRegattaLike, HasRaceColumnsAndRe */ Pair, Iterable> getAllCompetitorsWithRaceDefinitionsConsidered(); + Iterable getAllBoats(); + /** * Will remove the series from this regatta. Will also call {@link RaceColumn#removeRaceIdentifier(Fleet)} to make * sure that all raceLogs and race associations get removed for all race columns in this series. diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/AbstractRaceColumn.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/AbstractRaceColumn.java index 2be4f39810d..26fb8b57ebb 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/AbstractRaceColumn.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/AbstractRaceColumn.java @@ -22,12 +22,15 @@ import com.sap.sailing.domain.abstractlog.race.RaceLogEvent; import com.sap.sailing.domain.abstractlog.race.analyzing.impl.LastPublishedCourseDesignFinder; import com.sap.sailing.domain.abstractlog.race.tracking.RaceLogUseCompetitorsFromRaceLogEvent; import com.sap.sailing.domain.abstractlog.race.tracking.analyzing.impl.RegisteredCompetitorsAnalyzer; +import com.sap.sailing.domain.abstractlog.race.tracking.analyzing.impl.RegisteredCompetitorsAndBoatsAnalyzer; import com.sap.sailing.domain.abstractlog.race.tracking.impl.RaceLogRegisterCompetitorEventImpl; import com.sap.sailing.domain.abstractlog.race.tracking.impl.RaceLogUseCompetitorsFromRaceLogEventImpl; import com.sap.sailing.domain.abstractlog.regatta.RegattaLog; import com.sap.sailing.domain.abstractlog.regatta.tracking.analyzing.impl.RegattaLogDefinedMarkAnalyzer; 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; import com.sap.sailing.domain.base.CourseBase; import com.sap.sailing.domain.base.Fleet; import com.sap.sailing.domain.base.Mark; @@ -298,27 +301,78 @@ public abstract class AbstractRaceColumn extends SimpleAbstractRaceColumn implem public Iterable getAllCompetitors(final Fleet fleet) { return getAllCompetitorsWithRaceDefinitionsConsidered(fleet).getB(); } + @Override - public void registerCompetitor(Competitor competitor, Fleet fleet) throws CompetitorRegistrationOnRaceLogDisabledException { - registerCompetitors(Collections.singleton(competitor), fleet); + public Map getAllCompetitorsAndTheirBoats() { + Map result = new HashMap<>(); + for (Fleet fleet : getFleets()) { + result.putAll(getAllCompetitorsAndTheirBoats(fleet)); + } + return result; + } + + @Override + public Map getAllCompetitorsAndTheirBoats(Fleet fleet) { + final Map competitors; + TrackedRace trackedRace = getTrackedRace(fleet); + if (trackedRace != null) { + competitors = trackedRace.getRace().getCompetitorsAndTheirBoats(); + } else { + // if no tracked race is found, use competitors from race/regatta log depending on whether + // the mapping event is present or not; this assumes that if a tracked + // race exists, its competitors set takes precedence over what's in the race log. Usually, + // the tracked race will have the same competitors as those in the race log, or more because + // those from the regatta log are added to the tracked race as well. + competitors = new RegisteredCompetitorsAndBoatsAnalyzer(getRaceLog(fleet), getRegattaLog()).analyze(); + } + return competitors; } - + @Override - public void registerCompetitors(Iterable competitors, Fleet fleet) + public void registerCompetitor(CompetitorWithBoat competitorWithBoat, Fleet fleet) throws CompetitorRegistrationOnRaceLogDisabledException { + Map competitorsAndBoats = new HashMap<>(); + competitorsAndBoats.put(competitorWithBoat, competitorWithBoat.getBoat()); + registerCompetitorsInternal(competitorsAndBoats, fleet); + } + + @Override + public void registerCompetitors(Iterable competitorWithBoats, Fleet fleet) throws CompetitorRegistrationOnRaceLogDisabledException { + Map competitorsAndBoats = new HashMap<>(); + for (CompetitorWithBoat competitorWithBoat: competitorWithBoats) { + competitorsAndBoats.put(competitorWithBoat, competitorWithBoat.getBoat()); + } + registerCompetitorsInternal(competitorsAndBoats, fleet); + } + + @Override + public void registerCompetitor(Competitor competitor, Boat boat, Fleet fleet) throws CompetitorRegistrationOnRaceLogDisabledException { + Map competitorsAndBoats = new HashMap<>(); + competitorsAndBoats.put(competitor, boat); + registerCompetitorsInternal(competitorsAndBoats, fleet); + } + + @Override + public void registerCompetitors(Map competitorsAndBoats, Fleet fleet) + throws CompetitorRegistrationOnRaceLogDisabledException { + registerCompetitorsInternal(competitorsAndBoats, fleet); + } + + private void registerCompetitorsInternal(Map competitorsAndBoats, Fleet fleet) throws CompetitorRegistrationOnRaceLogDisabledException { if (!isCompetitorRegistrationInRacelogEnabled(fleet)) { - throw new CompetitorRegistrationOnRaceLogDisabledException("Competitor registration not allowed for fleet "+fleet+" in column "+this); + throw new CompetitorRegistrationOnRaceLogDisabledException("Competitor registration not allowed for fleet "+fleet+" in column "+this); } TimePoint now = MillisecondsTimePoint.now(); RaceLog raceLog = getRaceLog(fleet); int passId = raceLog.getCurrentPassId(); - for (Competitor competitor : competitors) { + for (Entry competitorAndBoat : competitorsAndBoats.entrySet()) { raceLog.add(new RaceLogRegisterCompetitorEventImpl(now, now, raceLogEventAuthorForRaceColumn, - UUID.randomUUID(), passId, competitor)); + UUID.randomUUID(), passId, competitorAndBoat.getKey(), competitorAndBoat.getValue())); } } + @Override public void deregisterCompetitor(Competitor competitor, Fleet fleet) throws CompetitorRegistrationOnRaceLogDisabledException { @@ -431,4 +485,5 @@ public abstract class AbstractRaceColumn extends SimpleAbstractRaceColumn implem } } } + } \ No newline at end of file diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/DomainFactoryImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/DomainFactoryImpl.java index 0a11c676f89..ca95530c97f 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/DomainFactoryImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/DomainFactoryImpl.java @@ -5,9 +5,12 @@ import java.io.InputStream; import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import java.util.Map; +import java.util.Map.Entry; import java.util.logging.Logger; import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver; +import com.sap.sailing.domain.base.Boat; import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.CompetitorStore; import com.sap.sailing.domain.base.DomainFactory; @@ -23,7 +26,9 @@ import com.sap.sailing.domain.common.Position; import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; import com.sap.sailing.domain.common.ScoringSchemeType; import com.sap.sailing.domain.common.WindSourceType; +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.sailing.domain.common.dto.FleetDTO; import com.sap.sailing.domain.common.dto.PlacemarkDTO; import com.sap.sailing.domain.common.dto.PlacemarkOrderDTO; @@ -46,10 +51,10 @@ import com.sap.sailing.domain.leaderboard.impl.HighPointFirstGets12Or8AndLastBre import com.sap.sailing.domain.leaderboard.impl.HighPointFirstGets12Or8AndLastBreaksTie2017; import com.sap.sailing.domain.leaderboard.impl.HighPointFirstGets1LastBreaksTie; import com.sap.sailing.domain.leaderboard.impl.HighPointLastBreaksTie; +import com.sap.sailing.domain.leaderboard.impl.HighPointMatchRacing; import com.sap.sailing.domain.leaderboard.impl.HighPointWinnerGetsEight; import com.sap.sailing.domain.leaderboard.impl.HighPointWinnerGetsEightAndInterpolation; import com.sap.sailing.domain.leaderboard.impl.HighPointWinnerGetsFive; -import com.sap.sailing.domain.leaderboard.impl.HighPointMatchRacing; import com.sap.sailing.domain.leaderboard.impl.HighPointWinnerGetsSix; import com.sap.sailing.domain.leaderboard.impl.LowPoint; import com.sap.sailing.domain.leaderboard.impl.LowPointForLeagueOverallLeaderboard; @@ -66,6 +71,7 @@ import com.sap.sailing.domain.tracking.impl.TrackedRaceImpl; import com.sap.sailing.geocoding.ReverseGeocoder; import com.sap.sse.common.TimePoint; import com.sap.sse.common.Util; +import com.sap.sse.common.Util.Pair; import com.sap.sse.common.impl.MillisecondsTimePoint; import com.sap.sse.util.ObjectInputStreamResolvingAgainstCache; @@ -73,7 +79,7 @@ public class DomainFactoryImpl extends SharedDomainFactoryImpl implements Domain private static Logger logger = Logger.getLogger(DomainFactoryImpl.class.getName()); /** - * Uses a transient competitor store + * Uses a transient competitor and boat store */ public DomainFactoryImpl(RaceLogResolver raceLogResolver) { super(new TransientCompetitorStoreImpl(), raceLogResolver); @@ -141,8 +147,28 @@ public class DomainFactoryImpl extends SharedDomainFactoryImpl implements Domain } @Override - public CompetitorDTO convertToCompetitorDTO(Competitor c) { - return competitorStore.convertToCompetitorDTO(c); + public CompetitorDTO convertToCompetitorDTO(Competitor competitor, Boat boat) { + return competitorAndBoatStore.convertToCompetitorWithBoatDTO(competitor, boat); + } + + @Override + public CompetitorDTO convertToCompetitorWithOptionalBoatDTO(Competitor competitor) { + return competitorAndBoatStore.convertToCompetitorWithOptionalBoatDTO(competitor); + } + + @Override + public CompetitorWithoutBoatDTO convertToCompetitorDTO(Competitor competitor) { + return competitorAndBoatStore.convertToCompetitorDTO(competitor); + } + + @Override + public Map convertToCompetitorAndBoatDTOs(Map competitorsAndBoats) { + return competitorAndBoatStore.convertToCompetitorAndBoatDTOs(competitorsAndBoats); + } + + @Override + public BoatDTO convertToBoatDTO(Boat boat) { + return competitorAndBoatStore.convertToBoatDTO(boat); } @Override @@ -192,6 +218,7 @@ public class DomainFactoryImpl extends SharedDomainFactoryImpl implements Domain // GPS data statisticsDTO.hasGPSData = trackedRace.hasGPSData(); Competitor leaderOrWinner = null; + Boat leaderOrWinnerBoat = null; TimePoint now = MillisecondsTimePoint.now(); try { if (trackedRace.isLive(now)) { @@ -201,13 +228,14 @@ public class DomainFactoryImpl extends SharedDomainFactoryImpl implements Domain Fleet fleetOfCompetitor = raceColumn.getFleetOfCompetitor(competitor); if (fleetOfCompetitor != null && fleetOfCompetitor.equals(fleet)) { leaderOrWinner = competitor; + leaderOrWinnerBoat = trackedRace.getBoatOfCompetitor(leaderOrWinner); break; } } } if (leaderOrWinner != null) { statisticsDTO.hasLeaderOrWinnerData = true; - statisticsDTO.leaderOrWinner = convertToCompetitorDTO(leaderOrWinner); + statisticsDTO.leaderOrWinner = convertToCompetitorDTO(leaderOrWinner, leaderOrWinnerBoat); GPSFixTrack track = trackedRace.getTrack(leaderOrWinner); if (track != null) { statisticsDTO.averageGPSDataSampleInterval = track.getAverageIntervalBetweenRawFixes(); @@ -341,12 +369,28 @@ public class DomainFactoryImpl extends SharedDomainFactoryImpl implements Domain } @Override - public List getCompetitorDTOList(Iterable competitors) { + public List getCompetitorDTOList(Map competitors) { List result = new ArrayList(); - for (Competitor competitor : competitors) { - result.add(convertToCompetitorDTO(competitor)); + for (Entry competitorAndBoatEntry : competitors.entrySet()) { + result.add(convertToCompetitorDTO(competitorAndBoatEntry.getKey(), competitorAndBoatEntry.getValue())); } return result; } + @Override + public List getCompetitorDTOList(Iterable competitors) { + List result = new ArrayList(); + for (Competitor competitor : competitors) { + result.add(convertToCompetitorWithOptionalBoatDTO(competitor)); + } + return result; + } + + public List getCompetitorDTOList(List> competitors) { + List result = new ArrayList(); + for (Pair competitorAndBoat : competitors) { + result.add(convertToCompetitorDTO(competitorAndBoat.getA(), competitorAndBoat.getB())); + } + return result; + } } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/DynamicCompetitorStore.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/DynamicCompetitorStore.java index 653c3ff24bd..5789a45f517 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/DynamicCompetitorStore.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/DynamicCompetitorStore.java @@ -12,10 +12,10 @@ public interface DynamicCompetitorStore extends CompetitorStore { DynamicCompetitor getExistingCompetitorById(Serializable competitorId); @Override - DynamicCompetitor getOrCreateCompetitor(Serializable competitorId, String name, Color displayColor, String email, URI flagImage, - DynamicTeam team, DynamicBoat boat, Double timeOnTimeFactor, Duration timeOnDistanceAllowancePerNauticalMile, String searchTag); + DynamicCompetitor getOrCreateCompetitor(Serializable competitorId, String name, String shortName, Color displayColor, String email, URI flagImage, + DynamicTeam team, Double timeOnTimeFactor, Duration timeOnDistanceAllowancePerNauticalMile, String searchTag); @Override - Iterable getCompetitors(); + Iterable getAllCompetitors(); } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/RaceColumnInSeriesImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/RaceColumnInSeriesImpl.java index 4bca4b108a0..0c71063170c 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/RaceColumnInSeriesImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/RaceColumnInSeriesImpl.java @@ -2,13 +2,18 @@ package com.sap.sailing.domain.base.impl; import java.util.Iterator; +import java.util.Map; import com.sap.sailing.domain.abstractlog.regatta.RegattaLog; +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.Fleet; import com.sap.sailing.domain.base.RaceColumnInSeries; import com.sap.sailing.domain.base.RaceDefinition; import com.sap.sailing.domain.base.Regatta; import com.sap.sailing.domain.base.Series; +import com.sap.sailing.domain.common.racelog.tracking.CompetitorRegistrationOnRaceLogDisabledException; import com.sap.sailing.domain.tracking.RaceExecutionOrderProvider; import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.domain.tracking.TrackedRegatta; @@ -138,4 +143,33 @@ public class RaceColumnInSeriesImpl extends AbstractRaceColumn implements RaceCo Regatta regatta = getRegatta(); return regatta == null ? null : regatta.getRegattaLog(); } + + @Override + public void registerCompetitor(CompetitorWithBoat competitorWithBoat, Fleet fleet) throws CompetitorRegistrationOnRaceLogDisabledException { + // this method can only be called if the competitors using the same boat for all races of the regatta + assert(!getRegatta().canBoatsOfCompetitorsChangePerRace()); + super.registerCompetitor(competitorWithBoat, fleet); + } + + @Override + public void registerCompetitors(Iterable competitorWithBoats, Fleet fleet) throws CompetitorRegistrationOnRaceLogDisabledException { + // this method can only be called if the competitors using the same boat for all races of the regatta + assert(!getRegatta().canBoatsOfCompetitorsChangePerRace()); + super.registerCompetitors(competitorWithBoats, fleet); + } + + @Override + public void registerCompetitor(Competitor competitor, Boat boat, Fleet fleet) throws CompetitorRegistrationOnRaceLogDisabledException { + // this method can only be called if the competitors changing the boats during the regatta + assert(getRegatta().canBoatsOfCompetitorsChangePerRace()); + super.registerCompetitor(competitor, boat, fleet); + } + + @Override + public void registerCompetitors(Map competitorsAndBoats, Fleet fleet) throws CompetitorRegistrationOnRaceLogDisabledException { + // this method can only be called if the competitors changing the boats during the regatta + assert(getRegatta().canBoatsOfCompetitorsChangePerRace()); + super.registerCompetitors(competitorsAndBoats, fleet); + } + } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/RaceDefinitionImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/RaceDefinitionImpl.java index 48193ecc607..73b9f6cb705 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/RaceDefinitionImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/RaceDefinitionImpl.java @@ -6,11 +6,9 @@ import java.security.NoSuchAlgorithmException; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; -import java.util.LinkedHashMap; import java.util.Map; import java.util.Map.Entry; import java.util.Set; -import java.util.logging.Logger; import com.sap.sailing.domain.base.Boat; import com.sap.sailing.domain.base.BoatClass; @@ -21,63 +19,49 @@ import com.sap.sailing.domain.common.RaceCompetitorIdsAsStringWithMD5Hash; import com.sap.sse.common.impl.NamedImpl; public class RaceDefinitionImpl extends NamedImpl implements RaceDefinition { - private static final Logger logger = Logger.getLogger(RaceDefinitionImpl.class.getName()); - private static final long serialVersionUID = -1900955198751393727L; private final Course course; - private final LinkedHashMap competitorsById; - private final Set competitors; - private final Map competitorBoats; + private final HashMap competitorsByCompetitorId; + private final HashMap boatsByCompetitorId; private final BoatClass boatClass; private final Serializable id; private final RaceCompetitorIdsAsStringWithMD5Hash raceCompetitorsMD5Hash; + private final Map competitorsAndTheirBoats; - public RaceDefinitionImpl(String name, Course course, BoatClass boatClass, Iterable competitors) { - this(name, course, boatClass, competitors, Collections.emptyMap()); + public RaceDefinitionImpl(String name, Course course, BoatClass boatClass) { + this(name, course, boatClass, Collections.emptyMap()); } - public RaceDefinitionImpl(String name, Course course, BoatClass boatClass, Iterable competitors, - Map competitorsAndTheirBoats) { - this(name, course, boatClass, competitors, competitorsAndTheirBoats, /* use name as default ID */ name); + public RaceDefinitionImpl(String name, Course course, BoatClass boatClass, Map competitorsAndTheirBoats) { + this(name, course, boatClass, competitorsAndTheirBoats, /* use name as default ID */ name); } - public RaceDefinitionImpl(String name, Course course, BoatClass boatClass, Iterable competitors, Serializable id) { - this(name, course, boatClass, competitors, /* per-race boats for competitors */ Collections.emptyMap(), id); - } - - public RaceDefinitionImpl(String name, Course course, BoatClass boatClass, Iterable competitors, - Map competitorsAndTheirBoats, Serializable id) { + public RaceDefinitionImpl(String name, Course course, BoatClass boatClass, Map competitorsAndTheirBoats, Serializable id) { super(name); assert name != null; + this.boatClass = boatClass; this.course = course; - this.competitors = new HashSet<>(); - this.competitorsById = new LinkedHashMap<>(); - this.competitorBoats = new HashMap<>(); + this.competitorsAndTheirBoats = new HashMap<>(competitorsAndTheirBoats); + this.id = id; + final Set idsOfCompetitorsAsString = new HashSet<>(); - for (Competitor competitor : competitors) { - Competitor competitorWithEqualID = competitorsById.put(competitor.getId(), competitor); - this.competitors.add(competitor); + this.competitorsByCompetitorId = new HashMap<>(); + this.boatsByCompetitorId = new HashMap<>(); + + for (Entry competitorAndBoat : competitorsAndTheirBoats.entrySet()) { + Competitor competitor = competitorAndBoat.getKey(); + Competitor competitorWithEqualID = competitorsByCompetitorId.put(competitor.getId(), competitor); + boatsByCompetitorId.put(competitor.getId(), competitorAndBoat.getValue()); idsOfCompetitorsAsString.add(competitor.getId().toString()); if (competitorWithEqualID != null && competitorWithEqualID != competitor) { throw new IllegalArgumentException("Two distinct competitors with equal ID "+competitor.getId()+" are not allowed within the single race "+name); } } - for (Entry competitorAndBoat : competitorsAndTheirBoats.entrySet()) { - Competitor competitor = competitorsById.get(competitorAndBoat.getKey().getId()); // only assign boat if competitor is part of race - if (competitor != null && competitorAndBoat.getValue() != null) { - competitorBoats.put(competitor, competitorAndBoat.getValue()); - } else { - logger.warning("Trying to set boat "+competitorAndBoat.getValue()+" for competitor "+competitorAndBoat.getKey()+ - " which is not part of race "+getName()+"'s set of competitors"); - } - } try { raceCompetitorsMD5Hash = new RaceCompetitorIdsAsStringWithMD5Hash(idsOfCompetitorsAsString); } catch (NoSuchAlgorithmException | UnsupportedEncodingException e) { throw new RuntimeException("Internal error: issue with UTF8 or MD5 for encoding competitor IDs as MD5 hash", e); } - this.boatClass = boatClass; - this.id = id; } @Override @@ -92,9 +76,19 @@ public class RaceDefinitionImpl extends NamedImpl implements RaceDefinition { @Override public Iterable getCompetitors() { - return Collections.unmodifiableSet(competitors); + return Collections.unmodifiableSet(competitorsAndTheirBoats.keySet()); } - + + @Override + public Iterable getBoats() { + return Collections.unmodifiableCollection(competitorsAndTheirBoats.values()); + } + + @Override + public Map getCompetitorsAndTheirBoats() { + return Collections.unmodifiableMap(competitorsAndTheirBoats); + } + @Override public byte[] getCompetitorMD5() { return raceCompetitorsMD5Hash.getMd5OfIdsAsStringOfCompetitorParticipatingInRaceInAlphanumericOrderOfTheirID(); @@ -102,7 +96,7 @@ public class RaceDefinitionImpl extends NamedImpl implements RaceDefinition { @Override public Competitor getCompetitorById(Serializable competitorID) { - return competitorsById.get(competitorID); + return competitorsByCompetitorId.get(competitorID); } @Override @@ -112,6 +106,6 @@ public class RaceDefinitionImpl extends NamedImpl implements RaceDefinition { @Override public Boat getBoatOfCompetitor(Competitor competitor) { - return competitorBoats.get(competitor); + return competitorsAndTheirBoats.get(competitor); } } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/RegattaImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/RegattaImpl.java index 389ae06408a..e43126fd9f9 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/RegattaImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/RegattaImpl.java @@ -25,9 +25,14 @@ import com.sap.sailing.domain.abstractlog.race.RaceLogEvent; import com.sap.sailing.domain.abstractlog.regatta.RegattaLog; import com.sap.sailing.domain.abstractlog.regatta.RegattaLogEvent; import com.sap.sailing.domain.abstractlog.regatta.RegattaLogEventVisitor; +import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogRegisterBoatEventImpl; import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogRegisterCompetitorEventImpl; +import com.sap.sailing.domain.abstractlog.regatta.tracking.analyzing.impl.RegattaLogBoatDeregistrator; +import com.sap.sailing.domain.abstractlog.regatta.tracking.analyzing.impl.RegattaLogBoatsInLogAnalyzer; import com.sap.sailing.domain.abstractlog.shared.analyzing.CompetitorDeregistrator; +import com.sap.sailing.domain.abstractlog.shared.analyzing.CompetitorsAndBoatsInLogAnalyzer; import com.sap.sailing.domain.abstractlog.shared.analyzing.CompetitorsInLogAnalyzer; +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.CourseArea; @@ -145,12 +150,15 @@ public class RegattaImpl extends NamedImpl implements Regatta, RaceColumnListene */ private boolean controlTrackingFromStartAndFinishTimes; + private boolean canBoatsOfCompetitorsChangePerRace; + /** * Defaults to true. See {@link Regatta#useStartTimeInference()}. */ private boolean useStartTimeInference; private transient CompetitorProviderFromRaceColumnsAndRegattaLike competitorsProvider; + private AbstractLogEventAuthor regattaLogEventAuthorForRegatta = new LogEventAuthorImpl( AbstractLeaderboardImpl.class.getName(), 0); @@ -158,10 +166,10 @@ public class RegattaImpl extends NamedImpl implements Regatta, RaceColumnListene * Constructs a regatta with an empty {@link RaceLogStore} and with * {@link Regatta#isControlTrackingFromStartAndFinishTimes()} set to {@code false}. */ - public RegattaImpl(String name, BoatClass boatClass, TimePoint startDate, TimePoint endDate, + public RegattaImpl(String name, BoatClass boatClass, boolean canBoatsOfCompetitorsChangePerRace, TimePoint startDate, TimePoint endDate, Iterable series, boolean persistent, ScoringScheme scoringScheme, Serializable id, CourseArea courseArea, RankingMetricConstructor rankingMetricConstructor) { - this(EmptyRaceLogStore.INSTANCE, EmptyRegattaLogStore.INSTANCE, name, boatClass, startDate, endDate, series, + this(EmptyRaceLogStore.INSTANCE, EmptyRegattaLogStore.INSTANCE, name, boatClass, canBoatsOfCompetitorsChangePerRace, startDate, endDate, series, persistent, scoringScheme, id, courseArea, 0.0, /* useStartTimeInference */true, /* controlTrackingFromStartAndFinishTimes */ false, rankingMetricConstructor); } @@ -179,9 +187,9 @@ public class RegattaImpl extends NamedImpl implements Regatta, RaceColumnListene * carried out. */ public RegattaImpl(RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, String name, BoatClass boatClass, - TimePoint startDate, TimePoint endDate, TrackedRegattaRegistry trackedRegattaRegistry, + boolean canBoatsOfCompetitorsChangePerRace, TimePoint startDate, TimePoint endDate, TrackedRegattaRegistry trackedRegattaRegistry, ScoringScheme scoringScheme, Serializable id, CourseArea courseArea) { - this(raceLogStore, regattaLogStore, name, boatClass, startDate, endDate, trackedRegattaRegistry, scoringScheme, + this(raceLogStore, regattaLogStore, name, boatClass, canBoatsOfCompetitorsChangePerRace, startDate, endDate, trackedRegattaRegistry, scoringScheme, id, courseArea, /* controlTrackingFromStartAndFinishTimes */ false, OneDesignRankingMetric::new); } @@ -196,10 +204,10 @@ public class RegattaImpl extends NamedImpl implements Regatta, RaceColumnListene * carried out. */ public RegattaImpl(RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, String name, BoatClass boatClass, - TimePoint startDate, TimePoint endDate, TrackedRegattaRegistry trackedRegattaRegistry, + boolean canBoatsOfCompetitorsChangePerRace, TimePoint startDate, TimePoint endDate, TrackedRegattaRegistry trackedRegattaRegistry, ScoringScheme scoringScheme, Serializable id, CourseArea courseArea, boolean controlTrackingFromStartAndFinishTimes, RankingMetricConstructor rankingMetricConstructor) { - this(raceLogStore, regattaLogStore, name, boatClass, startDate, endDate, Collections + this(raceLogStore, regattaLogStore, name, boatClass, canBoatsOfCompetitorsChangePerRace, startDate, endDate, Collections .singletonList(new SeriesImpl(LeaderboardNameConstants.DEFAULT_SERIES_NAME, /* isMedal */false, /* isFleetsCanRunInParallel */ true, Collections .singletonList(new FleetImpl(LeaderboardNameConstants.DEFAULT_FLEET_NAME)), @@ -214,7 +222,7 @@ public class RegattaImpl extends NamedImpl implements Regatta, RaceColumnListene * this new regatta. */ public RegattaImpl(RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, String name, - BoatClass boatClass, TimePoint startDate, TimePoint endDate, Iterable series, boolean persistent, + BoatClass boatClass, boolean canBoatsOfCompetitorsChangePerRace, TimePoint startDate, TimePoint endDate, Iterable series, boolean persistent, ScoringScheme scoringScheme, Serializable id, CourseArea courseArea, Double buoyZoneRadiusInHullLengths, boolean useStartTimeInference, boolean controlTrackingFromStartAndFinishTimes, RankingMetricConstructor rankingMetricConstructor) { super(name); @@ -227,6 +235,7 @@ public class RegattaImpl extends NamedImpl implements Regatta, RaceColumnListene regattaListeners = new HashSet(); raceColumnListeners = new RaceColumnListeners(); this.boatClass = boatClass; + this.canBoatsOfCompetitorsChangePerRace = canBoatsOfCompetitorsChangePerRace; this.startDate = startDate; this.endDate = endDate; final List seriesList = new ArrayList(); @@ -419,7 +428,7 @@ public class RegattaImpl extends NamedImpl implements Regatta, RaceColumnListene } return competitorsProvider; } - + @Override public Pair, Iterable> getAllCompetitorsWithRaceDefinitionsConsidered() { if (competitorsProvider == null) { @@ -449,6 +458,29 @@ public class RegattaImpl extends NamedImpl implements Regatta, RaceColumnListene return getAllCompetitorsWithRaceDefinitionsConsidered().getB(); } + @Override + public Iterable getAllBoats() { + Set result = new HashSet(); + + boolean hasRaceColumns = false; + for (RaceColumn rc : getRaceColumns()) { + hasRaceColumns = true; + for (final Fleet fleet : rc.getFleets()) { + RaceDefinition raceDefinition = rc.getRaceDefinition(fleet); + Util.addAll(raceDefinition.getBoats(), result); + } + } + final RegattaLog regattaLog = getRegattaLog(); + if (!hasRaceColumns) { + // If no race exists, the regatta log-provided competitor registrations will not have + // been considered yet; add them: + final Map regattaLogProvidedCompetitorsAndBoats = new CompetitorsAndBoatsInLogAnalyzer<>(regattaLog).analyze(); + Util.addAll(regattaLogProvidedCompetitorsAndBoats.values(), result); + } + + return result; + } + @Override public void addRegattaListener(RegattaListener listener) { synchronized (regattaListeners) { @@ -713,6 +745,19 @@ public class RegattaImpl extends NamedImpl implements Regatta, RaceColumnListene return useStartTimeInference; } + @Override + public boolean canBoatsOfCompetitorsChangePerRace() { + return canBoatsOfCompetitorsChangePerRace; + } + + /** + * Changes whether the competitors use the same boat for the whole regatta or change the boat used during the competition. + * Actually this should never be called as this should not change once set, but we need it to migrate older regattas to the new model. + */ + protected void setCanBoatsOfCompetitorsChangePerRace(boolean canBoatsOfCompetitorsChangePerRace) { + this.canBoatsOfCompetitorsChangePerRace = canBoatsOfCompetitorsChangePerRace; + } + @Override public RegattaLog getRegattaLog() { return regattaLikeHelper.getRegattaLog(); @@ -835,7 +880,7 @@ public class RegattaImpl extends NamedImpl implements Regatta, RaceColumnListene @Override public void registerCompetitor(Competitor competitor) { - registerCompetitors(Collections.singleton(competitor)); + registerCompetitors(Collections.singletonList(competitor)); } @Override @@ -843,7 +888,7 @@ public class RegattaImpl extends NamedImpl implements Regatta, RaceColumnListene RegattaLog regattaLog = getRegattaLike().getRegattaLog(); TimePoint now = MillisecondsTimePoint.now(); - for (Competitor competitor : competitors) { + for (Competitor competitor: competitors) { regattaLog.add(new RegattaLogRegisterCompetitorEventImpl(now, now, regattaLogEventAuthorForRegatta, UUID.randomUUID(), competitor)); } @@ -867,5 +912,42 @@ public class RegattaImpl extends NamedImpl implements Regatta, RaceColumnListene series.setIsFleetsCanRunInParallel(true); } } - + + // boat functions + @Override + public Iterable getBoatsRegisteredInRegattaLog() { + RegattaLog regattaLog = getRegattaLog(); + RegattaLogBoatsInLogAnalyzer analyzer = new RegattaLogBoatsInLogAnalyzer<>( + regattaLog); + return analyzer.analyze(); + } + + @Override + public void registerBoat(Boat boat) { + registerBoats(Collections.singleton(boat)); + } + + @Override + public void registerBoats(Iterable boats) { + RegattaLog regattaLog = getRegattaLike().getRegattaLog(); + TimePoint now = MillisecondsTimePoint.now(); + + for (Boat boat : boats) { + regattaLog.add(new RegattaLogRegisterBoatEventImpl(now, now, regattaLogEventAuthorForRegatta, + UUID.randomUUID(), boat)); + } + } + + @Override + public void deregisterBoat(Boat boat) { + deregisterBoats(Collections.singleton(boat)); + } + + @Override + public void deregisterBoats(Iterable boats) { + RegattaLog regattaLog = getRegattaLike().getRegattaLog(); + RegattaLogBoatDeregistrator deregisterer = new RegattaLogBoatDeregistrator<>(regattaLog, boats, regattaLogEventAuthorForRegatta); + deregisterer.deregister(deregisterer.analyze()); + } + } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/SimpleAbstractRaceColumn.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/SimpleAbstractRaceColumn.java index 3cebd9d3d06..e76b1f77ca8 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/SimpleAbstractRaceColumn.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/SimpleAbstractRaceColumn.java @@ -1,10 +1,12 @@ package com.sap.sailing.domain.base.impl; import java.util.Collections; +import java.util.Map; import com.sap.sailing.domain.abstractlog.race.RaceLog; -import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogRegisteredCompetitorsAnalyzer; +import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogRegisteredCompetitorsAndBoatsAnalyzer; import com.sap.sailing.domain.abstractlog.race.tracking.analyzing.impl.RaceLogUsesOwnCompetitorsAnalyzer; +import com.sap.sailing.domain.base.Boat; import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.Fleet; import com.sap.sailing.domain.base.RaceColumn; @@ -121,12 +123,12 @@ public abstract class SimpleAbstractRaceColumn implements RaceColumn { } @Override - public Iterable getCompetitorsRegisteredInRacelog(final Fleet fleet) { + public Map getCompetitorsRegisteredInRacelog(final Fleet fleet) { RaceLog raceLog = getRaceLog(fleet); if (raceLog == null) { - return Collections.emptySet(); + return Collections.emptyMap(); } else { - RaceLogRegisteredCompetitorsAnalyzer analyzer = new RaceLogRegisteredCompetitorsAnalyzer(raceLog); + RaceLogRegisteredCompetitorsAndBoatsAnalyzer analyzer = new RaceLogRegisteredCompetitorsAndBoatsAnalyzer(raceLog); return analyzer.analyze(); } } @@ -141,4 +143,5 @@ public abstract class SimpleAbstractRaceColumn implements RaceColumn { return analyzer.analyze(); } } + } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/HasRaceColumnsAndRegattaLike.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/HasRaceColumnsAndRegattaLike.java index 0316ccf8a66..29d5b5ee948 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/HasRaceColumnsAndRegattaLike.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/HasRaceColumnsAndRegattaLike.java @@ -6,5 +6,4 @@ import com.sap.sailing.domain.regattalike.HasRegattaLike; public interface HasRaceColumnsAndRegattaLike extends HasRegattaLike, HasRaceColumns { CompetitorProviderFromRaceColumnsAndRegattaLike getOrCreateCompetitorsProvider(); - } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/Leaderboard.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/Leaderboard.java index abd48732d49..b41961648e3 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/Leaderboard.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/Leaderboard.java @@ -6,6 +6,7 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.ExecutionException; +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.CourseArea; @@ -15,6 +16,7 @@ import com.sap.sailing.domain.base.LeaderboardBase; import com.sap.sailing.domain.base.RaceColumn; import com.sap.sailing.domain.base.RaceColumnListener; import com.sap.sailing.domain.base.RaceDefinition; +import com.sap.sailing.domain.base.Regatta; import com.sap.sailing.domain.base.Series; import com.sap.sailing.domain.common.Distance; import com.sap.sailing.domain.common.LeaderboardType; @@ -98,6 +100,8 @@ public interface Leaderboard extends LeaderboardBase, HasRaceColumns { */ Iterable getAllCompetitors(); + Iterable getAllBoats(); + /** * Same as {@link #getAllCompetitors()}, only that additionally the method returns as a first element in a pair * which {@link RaceDefinition}s were used in order to fetch their {@link RaceDefinition#getCompetitors()} to @@ -140,7 +144,12 @@ public interface Leaderboard extends LeaderboardBase, HasRaceColumns { * It will, however, continue to be returned from {@link #getAllCompetitors()}. */ void setSuppressed(Competitor competitor, boolean suppressed); - + + /** + * Retrieves the boat of a given competitor for the specified raceColumn and fleet. + */ + Boat getBoatOfCompetitor(Competitor competitor, RaceColumn raceColumn, Fleet fleet); + /** * Returns the first fleet found in the sequence of this leaderboard's {@link #getRaceColumns() race columns}' * {@link RaceColumn#getFleets() fleets} whose name equals fleetName. If no such fleet is found, @@ -191,7 +200,8 @@ public interface Leaderboard extends LeaderboardBase, HasRaceColumns { * exactly n races. Still, this method pretends such a time point would have existed, actually ignoring * the times at which a race took place but only looking at the resulting scores and discards.

* - * When computing the net points sum after all columns up to and including the race column that is the key of the resulting + * When computing the ne + * t points sum after all columns up to and including the race column that is the key of the resulting * map, the method applies the discarding and tie breaking rules as they would have had to be applied had the races * in the respective column just completed. * diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/LeaderboardCacheManager.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/LeaderboardCacheManager.java index 691e4e2e47a..d1cd9ef8ff1 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/LeaderboardCacheManager.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/LeaderboardCacheManager.java @@ -99,11 +99,6 @@ public class LeaderboardCacheManager { updateCompetitorListeners(); } - @Override - public void sailIdChanged(String oldSailId, String newSailId) { - removeFromCache(leaderboard); - } - @Override public void nationalityChanged(WithNationality what, Nationality oldNationality, Nationality newNationality) { removeFromCache(leaderboard); @@ -119,6 +114,11 @@ public class LeaderboardCacheManager { removeFromCache(leaderboard); } + @Override + public void shortNameChanged(String oldShortName, String newShortName) { + removeFromCache(leaderboard); + } + public synchronized void updateCompetitorListeners() { Set competitorsToStopObserving = new HashSet<>(observedCompetitors); for (Competitor competitor : leaderboard.getCompetitors()) { diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/LeaderboardGroupResolver.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/LeaderboardGroupResolver.java index 857f3b9cfcd..da9c3a50261 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/LeaderboardGroupResolver.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/LeaderboardGroupResolver.java @@ -12,4 +12,6 @@ public interface LeaderboardGroupResolver { LeaderboardGroup getLeaderboardGroupByName(String leaderboardGroupName); LeaderboardGroup getLeaderboardGroupByID(UUID leaderboardGroupID); + + LeaderboardGroup resolveLeaderboardGroupByRegattaName(String regattaName); } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractLeaderboardImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractLeaderboardImpl.java index 02f44367c17..c03bc4e5a1d 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractLeaderboardImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractLeaderboardImpl.java @@ -13,9 +13,13 @@ import com.sap.sailing.domain.abstractlog.race.RaceLog; import com.sap.sailing.domain.abstractlog.regatta.RegattaLog; import com.sap.sailing.domain.abstractlog.regatta.RegattaLogEvent; import com.sap.sailing.domain.abstractlog.regatta.RegattaLogEventVisitor; +import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogRegisterBoatEventImpl; import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogRegisterCompetitorEventImpl; +import com.sap.sailing.domain.abstractlog.regatta.tracking.analyzing.impl.RegattaLogBoatDeregistrator; +import com.sap.sailing.domain.abstractlog.regatta.tracking.analyzing.impl.RegattaLogBoatsInLogAnalyzer; import com.sap.sailing.domain.abstractlog.shared.analyzing.CompetitorDeregistrator; import com.sap.sailing.domain.abstractlog.shared.analyzing.CompetitorsInLogAnalyzer; +import com.sap.sailing.domain.base.Boat; import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.Fleet; import com.sap.sailing.domain.base.RaceColumn; @@ -98,11 +102,22 @@ public abstract class AbstractLeaderboardImpl extends AbstractSimpleLeaderboardI return getOrCreateCompetitorsProvider().getAllCompetitorsWithRaceDefinitionsConsidered(); } + @Override + public Iterable getAllBoats() { + return getOrCreateCompetitorsProvider().getAllBoats(); + } + @Override public Iterable getAllCompetitors(RaceColumn raceColumn, Fleet fleet) { return getOrCreateCompetitorsProvider().getAllCompetitors(raceColumn, fleet); } + @Override + public Boat getBoatOfCompetitor(Competitor competitor, RaceColumn raceColumn, Fleet fleet) { + RaceDefinition raceDefinition = raceColumn.getRaceDefinition(fleet); + return raceDefinition != null ? raceDefinition.getBoatOfCompetitor(competitor) : null; + } + @Override public CompetitorProviderFromRaceColumnsAndRegattaLike getOrCreateCompetitorsProvider() { if (competitorsProvider == null) { @@ -202,10 +217,10 @@ public abstract class AbstractLeaderboardImpl extends AbstractSimpleLeaderboardI regattaLog); return analyzer.analyze(); } - + @Override public void registerCompetitor(Competitor competitor) { - registerCompetitors(Collections.singleton(competitor)); + registerCompetitors(Collections.singletonList(competitor)); } @Override @@ -213,12 +228,12 @@ public abstract class AbstractLeaderboardImpl extends AbstractSimpleLeaderboardI RegattaLog regattaLog = getRegattaLike().getRegattaLog(); TimePoint now = MillisecondsTimePoint.now(); - for (Competitor competitor : competitors) { + for (Competitor competitor: competitors) { regattaLog.add(new RegattaLogRegisterCompetitorEventImpl(now, now, regattaLogEventAuthorForAbstractLeaderboard, UUID.randomUUID(), competitor)); } } - + @Override public void deregisterCompetitor(Competitor competitor) { deregisterCompetitors(Collections.singleton(competitor)); @@ -230,4 +245,42 @@ public abstract class AbstractLeaderboardImpl extends AbstractSimpleLeaderboardI CompetitorDeregistrator deregisterer = new CompetitorDeregistrator<>(regattaLog, competitors, regattaLogEventAuthorForAbstractLeaderboard); deregisterer.deregister(deregisterer.analyze()); } + + // boat functions + @Override + public Iterable getBoatsRegisteredInRegattaLog() { + RegattaLog regattaLog = getRegattaLike().getRegattaLog(); + RegattaLogBoatsInLogAnalyzer analyzer = new RegattaLogBoatsInLogAnalyzer<>( + regattaLog); + return analyzer.analyze(); + } + + @Override + public void registerBoat(Boat boat) { + registerBoats(Collections.singleton(boat)); + } + + @Override + public void registerBoats(Iterable boats) { + RegattaLog regattaLog = getRegattaLike().getRegattaLog(); + TimePoint now = MillisecondsTimePoint.now(); + + for (Boat boat : boats) { + regattaLog.add(new RegattaLogRegisterBoatEventImpl(now, now, regattaLogEventAuthorForAbstractLeaderboard, + UUID.randomUUID(), boat)); + } + } + + @Override + public void deregisterBoat(Boat boat) { + deregisterBoats(Collections.singleton(boat)); + } + + @Override + public void deregisterBoats(Iterable boats) { + RegattaLog regattaLog = getRegattaLike().getRegattaLog(); + RegattaLogBoatDeregistrator deregisterer = new RegattaLogBoatDeregistrator<>(regattaLog, boats, regattaLogEventAuthorForAbstractLeaderboard); + deregisterer.deregister(deregisterer.analyze()); + } + } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractLeaderboardWithCache.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractLeaderboardWithCache.java index 36d5fc3012e..70982df935a 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractLeaderboardWithCache.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractLeaderboardWithCache.java @@ -66,6 +66,7 @@ import com.sap.sailing.domain.leaderboard.meta.MetaLeaderboardColumn; import com.sap.sailing.domain.racelog.RaceLogIdentifier; import com.sap.sailing.domain.ranking.RankingMetric.CompetitorRankingInfo; import com.sap.sailing.domain.ranking.RankingMetric.RankingInfo; +import com.sap.sailing.domain.regattalike.LeaderboardThatHasRegattaLike; import com.sap.sailing.domain.tracking.GPSFixTrack; import com.sap.sailing.domain.tracking.Maneuver; import com.sap.sailing.domain.tracking.MarkPassing; @@ -361,8 +362,16 @@ public abstract class AbstractLeaderboardWithCache implements Leaderboard { result.name = this.getName(); result.displayName = this.getDisplayName(); result.competitorDisplayNames = new HashMap(); + boolean isLeaderboardThatHasRegattaLike = this instanceof LeaderboardThatHasRegattaLike; + if (isLeaderboardThatHasRegattaLike) { + LeaderboardThatHasRegattaLike regattaLikeLeaderboard = (LeaderboardThatHasRegattaLike) this; + result.canBoatsOfCompetitorsChangePerRace = regattaLikeLeaderboard.getRegattaLike().canBoatsOfCompetitorsChangePerRace(); + } else { + result.canBoatsOfCompetitorsChangePerRace = false; + } + for (Competitor suppressedCompetitor : this.getSuppressedCompetitors()) { - result.setSuppressed(baseDomainFactory.convertToCompetitorDTO(suppressedCompetitor), true); + result.setSuppressed(baseDomainFactory.convertToCompetitorWithOptionalBoatDTO(suppressedCompetitor), true); } // Now create the race columns and, as a future task, set their competitorsFromBestToWorst, then wait for all these // futures to finish: @@ -374,8 +383,7 @@ public abstract class AbstractLeaderboardWithCache implements Leaderboard { raceColumn instanceof RaceColumnInSeries ? ((RaceColumnInSeries) raceColumn).getSeries().getName() : null, isMetaLeaderboardColumn); if (isMetaLeaderboardColumn && raceColumnDTO instanceof MetaLeaderboardRaceColumnDTO) { - calculateRacesMetadata((MetaLeaderboardColumn) raceColumn, (MetaLeaderboardRaceColumnDTO) raceColumnDTO, - trackedRegattaRegistry, baseDomainFactory); + calculateRacesMetadata((MetaLeaderboardColumn) raceColumn, (MetaLeaderboardRaceColumnDTO) raceColumnDTO, baseDomainFactory); } for (Fleet fleet : raceColumn.getFleets()) { RegattaAndRaceIdentifier raceIdentifier = null; @@ -450,7 +458,7 @@ public abstract class AbstractLeaderboardWithCache implements Leaderboard { } } for (final Competitor competitor : this.getCompetitorsFromBestToWorst(timePoint)) { - CompetitorDTO competitorDTO = baseDomainFactory.convertToCompetitorDTO(competitor); + CompetitorDTO competitorDTO = baseDomainFactory.convertToCompetitorWithOptionalBoatDTO(competitor); LeaderboardRowDTO row = new LeaderboardRowDTO(); row.competitor = competitorDTO; row.fieldsByRaceColumnName = new HashMap(); @@ -502,6 +510,16 @@ public abstract class AbstractLeaderboardWithCache implements Leaderboard { if (displayName != null) { result.competitorDisplayNames.put(competitorDTO, displayName); } + // in case boats can't change set the also the boat on the row to simplify access + if (result.canBoatsOfCompetitorsChangePerRace == false && !row.fieldsByRaceColumnName.isEmpty()) { + // find a raceColumn where a boat is available + for (LeaderboardEntryDTO leaderboardEntry: row.fieldsByRaceColumnName.values()) { + if (leaderboardEntry.boat != null) { + row.boat = leaderboardEntry.boat; + break; + } + } + } } logger.info("computeLeaderboardByName(" + this.getName() + ", " + timePoint + ", " + namesOfRaceColumnsForWhichToLoadLegDetails + ", addOverallDetails=" + addOverallDetails + ") took " @@ -560,6 +578,8 @@ public abstract class AbstractLeaderboardWithCache implements Leaderboard { LeaderboardEntryDTO entryDTO = new LeaderboardEntryDTO(); TrackedRace trackedRace = raceColumn.getTrackedRace(competitor); entryDTO.race = trackedRace == null ? null : trackedRace.getRaceIdentifier(); + // TODO bug2822: Should this rather come from raceColumn.getBoatOfCompetitor() ? + entryDTO.boat = trackedRace == null ? null : baseDomainFactory.convertToBoatDTO(trackedRace.getBoatOfCompetitor(competitor)); entryDTO.totalPoints = entry.getTotalPoints(); if (fillTotalPointsUncorrected) { entryDTO.totalPointsUncorrected = entry.getTotalPointsUncorrected(); @@ -654,7 +674,7 @@ public abstract class AbstractLeaderboardWithCache implements Leaderboard { } private void calculateRacesMetadata(MetaLeaderboardColumn metaLeaderboardColumn, MetaLeaderboardRaceColumnDTO columnDTO, - TrackedRegattaRegistry trackedRegattaRegistry, final DomainFactory baseDomainFactory) { + final DomainFactory baseDomainFactory) { for (final RaceColumn raceColumn : metaLeaderboardColumn.getLeaderboard().getRaceColumns()) { for (Fleet fleet : raceColumn.getFleets()) { TrackedRace trackedRace = raceColumn.getTrackedRace(fleet); @@ -1175,7 +1195,7 @@ public abstract class AbstractLeaderboardWithCache implements Leaderboard { } return result; } - + @Override public Iterable getAllCompetitors() { return getAllCompetitorsWithRaceDefinitionsConsidered().getB(); diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractSimpleLeaderboardImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractSimpleLeaderboardImpl.java index 7b53dcc733a..84e2ab49c5a 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractSimpleLeaderboardImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractSimpleLeaderboardImpl.java @@ -18,6 +18,7 @@ import java.util.stream.StreamSupport; import com.sap.sailing.domain.abstractlog.race.RaceLogEvent; import com.sap.sailing.domain.abstractlog.regatta.RegattaLogEvent; +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.Fleet; @@ -908,8 +909,13 @@ public abstract class AbstractSimpleLeaderboardImpl extends AbstractLeaderboardW @Override public BoatClass getBoatClass() { - return Util.getDominantObject(StreamSupport.stream(getCompetitors().spliterator(), /* parallel */ false). - map(c->c.getBoat().getBoatClass()).collect(Collectors.toList())); + Set allBoats = new HashSet<>(); + for (final RaceColumn raceColumn : getRaceColumns()) { + Map competitorsAndTheirBoats = raceColumn.getAllCompetitorsAndTheirBoats(); + allBoats.addAll(competitorsAndTheirBoats.values()); + } + return Util.getDominantObject(StreamSupport.stream(allBoats.spliterator(), /* parallel */ false). + map(b->b.getBoatClass()).collect(Collectors.toList())); } } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/CompetitorProviderFromRaceColumnsAndRegattaLike.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/CompetitorProviderFromRaceColumnsAndRegattaLike.java index fdbfd864643..e842be67ce4 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/CompetitorProviderFromRaceColumnsAndRegattaLike.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/CompetitorProviderFromRaceColumnsAndRegattaLike.java @@ -16,10 +16,12 @@ import com.sap.sailing.domain.abstractlog.race.tracking.RaceLogRegisterCompetito import com.sap.sailing.domain.abstractlog.race.tracking.RaceLogUseCompetitorsFromRaceLogEvent; import com.sap.sailing.domain.abstractlog.regatta.RegattaLog; import com.sap.sailing.domain.abstractlog.regatta.RegattaLogEventVisitor; +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.impl.BaseRegattaLogEventVisitor; import com.sap.sailing.domain.abstractlog.shared.analyzing.CompetitorsInLogAnalyzer; +import com.sap.sailing.domain.base.Boat; import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.Fleet; import com.sap.sailing.domain.base.RaceColumn; @@ -85,11 +87,15 @@ public class CompetitorProviderFromRaceColumnsAndRegattaLike { invalidateAllCompetitorsCaches(); } + public void visit(RegattaLogRegisterBoatEvent event) { + invalidateAllCompetitorsCaches(); + } + @Override public void visit(RegattaLogRevokeEvent event) { try { - if (RegattaLogRegisterCompetitorEvent.class.isAssignableFrom(Class.forName(event - .getRevokedEventType()))) { + if (RegattaLogRegisterCompetitorEvent.class.isAssignableFrom(Class.forName(event.getRevokedEventType())) || + RegattaLogRegisterBoatEvent.class.isAssignableFrom(Class.forName(event.getRevokedEventType()))) { invalidateAllCompetitorsCaches(); } } catch (ClassNotFoundException e) { @@ -113,7 +119,6 @@ public class CompetitorProviderFromRaceColumnsAndRegattaLike { public void visit(RaceLogRevokeEvent event) { try { final Class revokedEventClass = Class.forName(event.getRevokedEventType()); - // if (RaceLogRegisterCompetitorEvent.class.isAssignableFrom(revokedEventClass) || RaceLogUseCompetitorsFromRaceLogEvent.class.isAssignableFrom(revokedEventClass)) { invalidateAllCompetitorsCaches(); @@ -169,7 +174,24 @@ public class CompetitorProviderFromRaceColumnsAndRegattaLike { public Iterable getAllCompetitors() { return getAllCompetitorsWithRaceDefinitionsConsidered().getB(); } - + + /** + * Returns a Collection of all {@link Boat boats} collected over {@link RegattaLog}, {@link RaceLog} as well as the + * {@link RaceDefinition RaceDefinitions} of all {@link TrackedRace TrackedRaces} attached. While subsequent calls + * may return different {@link Collection Collections} the contents of a {@link Collection} returned may never + * change. + */ + public Iterable getAllBoats() { + Set result = new HashSet<>(); + for (RaceColumn rc : provider.getRaceColumns()) { + result.addAll(rc.getAllCompetitorsAndTheirBoats().values()); + } + for (Boat boat : provider.getBoatsRegisteredInRegattaLog()) { + result.add(boat); + } + return result; + } + /** * Returns a Collection of all {@link Competitor Competitors} collected over {@link RegattaLog}, {@link RaceLog} as * well as the {@link RaceDefinition RaceDefinitions} of all {@link TrackedRace TrackedRaces} attached. While diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/DelegatingRegattaLeaderboardWithCompetitorElimination.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/DelegatingRegattaLeaderboardWithCompetitorElimination.java index f4da263926c..49dac7c5967 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/DelegatingRegattaLeaderboardWithCompetitorElimination.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/DelegatingRegattaLeaderboardWithCompetitorElimination.java @@ -14,6 +14,7 @@ import java.util.function.Consumer; import java.util.function.Supplier; import com.sap.sailing.domain.abstractlog.race.RaceLog; +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.CourseArea; @@ -205,8 +206,8 @@ public class DelegatingRegattaLeaderboardWithCompetitorElimination extends Abstr getFullLeaderboard().registerCompetitor(competitor); } - public void registerCompetitors(Iterable competitor) { - getFullLeaderboard().registerCompetitors(competitor); + public void registerCompetitors(Iterable competitors) { + getFullLeaderboard().registerCompetitors(competitors); } public void deregisterCompetitor(Competitor competitor) { @@ -221,7 +222,6 @@ public class DelegatingRegattaLeaderboardWithCompetitorElimination extends Abstr return getFullLeaderboard().getAllCompetitors(); } - @Override public Pair, Iterable> getAllCompetitorsWithRaceDefinitionsConsidered() { return getFullLeaderboard().getAllCompetitorsWithRaceDefinitionsConsidered(); } @@ -434,6 +434,11 @@ public class DelegatingRegattaLeaderboardWithCompetitorElimination extends Abstr return getFullLeaderboard().getBoatClass(); } + @Override + public Boat getBoatOfCompetitor(Competitor competitor, RaceColumn raceColumn, Fleet fleet) { + return getFullLeaderboard().getBoatOfCompetitor(competitor, raceColumn, fleet); + } + private RegattaLeaderboard getFullLeaderboard() { if (fullLeaderboard == null) { if (fullLeaderboardSupplier == null) { @@ -460,4 +465,34 @@ public class DelegatingRegattaLeaderboardWithCompetitorElimination extends Abstr getFullLeaderboard(); oos.defaultWriteObject(); } + + @Override + public Iterable getBoatsRegisteredInRegattaLog() { + return getFullLeaderboard().getBoatsRegisteredInRegattaLog(); + } + + @Override + public Iterable getAllBoats() { + return getFullLeaderboard().getAllBoats(); + } + + @Override + public void registerBoat(Boat boat) { + getFullLeaderboard().registerBoat(boat); + } + + @Override + public void registerBoats(Iterable boats) { + getFullLeaderboard().registerBoats(boats); + } + + @Override + public void deregisterBoat(Boat boat) { + getFullLeaderboard().deregisterBoat(boat); + } + + @Override + public void deregisterBoats(Iterable boats) { + getFullLeaderboard().deregisterBoats(boats); + } } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/FlexibleLeaderboardImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/FlexibleLeaderboardImpl.java index ab162442892..c1ad87952d9 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/FlexibleLeaderboardImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/FlexibleLeaderboardImpl.java @@ -113,7 +113,7 @@ public class FlexibleLeaderboardImpl extends AbstractLeaderboardImpl implements this.regattaLikeHelper.addListener(new RegattaLogEventAdditionForwarder(getRaceColumnListeners())); this.raceExecutionOrderProvider = new RaceExecutionOrderCache(); } - + /** * Deserialization has to be maintained in lock-step with {@link #writeObject(ObjectOutputStream) serialization}. * When de-serializing, a possibly remote {@link #raceLogStore} is ignored because it is transient. Instead, an @@ -372,6 +372,11 @@ public class FlexibleLeaderboardImpl extends AbstractLeaderboardImpl implements return LeaderboardType.FlexibleLeaderboard; } + @Override + public boolean canBoatsOfCompetitorsChangePerRace() { + return false; + } + /** * In addition to invoking the superclass implementation, a flexible leaderboard also * detaches all race logs from any tracked race currently linked to any of the race columns diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/RegattaLeaderboardImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/RegattaLeaderboardImpl.java index 93380626e1e..b2ee3dc3d1d 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/RegattaLeaderboardImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/RegattaLeaderboardImpl.java @@ -116,7 +116,7 @@ public class RegattaLeaderboardImpl extends AbstractLeaderboardImpl implements R public Pair, Iterable> getAllCompetitorsWithRaceDefinitionsConsidered() { return regatta.getAllCompetitorsWithRaceDefinitionsConsidered(); } - + @Override public IsRegattaLike getRegattaLike() { return regatta; diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/meta/AbstractMetaLeaderboard.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/meta/AbstractMetaLeaderboard.java index 1609478961c..cade8ccda2a 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/meta/AbstractMetaLeaderboard.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/meta/AbstractMetaLeaderboard.java @@ -13,6 +13,7 @@ import java.util.Map; import java.util.Set; import java.util.WeakHashMap; +import com.sap.sailing.domain.base.Boat; import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.Fleet; import com.sap.sailing.domain.base.RaceColumn; @@ -141,6 +142,17 @@ public abstract class AbstractMetaLeaderboard extends AbstractSimpleLeaderboardI return (MetaLeaderboardScoreCorrection) super.getScoreCorrection(); } + @Override + public Iterable getAllBoats() { + Set boats = new HashSet<>(); + for (Leaderboard leaderboard : getLeaderboards()) { + for (Boat boat: leaderboard.getAllBoats()) { + boats.add(boat); + } + } + return boats; + } + @Override public Pair, Iterable> getAllCompetitorsWithRaceDefinitionsConsidered() { Set competitors = new HashSet(); @@ -152,7 +164,7 @@ public abstract class AbstractMetaLeaderboard extends AbstractSimpleLeaderboardI } return new Pair<>(raceDefinitionsConsidered, competitors); } - + @Override public Iterable getAllCompetitors(RaceColumn raceColumn, Fleet fleet) { final Iterable result; @@ -164,6 +176,11 @@ public abstract class AbstractMetaLeaderboard extends AbstractSimpleLeaderboardI return result; } + @Override + public Boat getBoatOfCompetitor(Competitor competitor, RaceColumn raceColumn, Fleet fleet) { + return null; + } + @Override public Fleet getFleet(String fleetName) { return fleetName.equals(metaFleet.getName()) ? metaFleet : null; diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/meta/MetaLeaderboardColumn.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/meta/MetaLeaderboardColumn.java index d5f58159a98..d30c474ca10 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/meta/MetaLeaderboardColumn.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/meta/MetaLeaderboardColumn.java @@ -1,11 +1,14 @@ package com.sap.sailing.domain.leaderboard.meta; import java.util.Collections; +import java.util.Map; import com.sap.sailing.domain.abstractlog.race.RaceLog; import com.sap.sailing.domain.abstractlog.race.RaceLogEvent; import com.sap.sailing.domain.abstractlog.regatta.RegattaLog; +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.Fleet; import com.sap.sailing.domain.base.LeaderboardChangeListener; import com.sap.sailing.domain.base.Mark; @@ -230,7 +233,7 @@ public class MetaLeaderboardColumn extends SimpleAbstractRaceColumn implements R public Pair, Iterable> getAllCompetitorsWithRaceDefinitionsConsidered() { return leaderboard.getAllCompetitorsWithRaceDefinitionsConsidered(); } - + @Override public Iterable getAllCompetitors(Fleet fleet) { final Iterable result; @@ -242,6 +245,18 @@ public class MetaLeaderboardColumn extends SimpleAbstractRaceColumn implements R return result; } + @Override + public Map getAllCompetitorsAndTheirBoats() { + // TODO bug2822: What should we do here? Returning a boat makes only sense when the competitors keep their boats through all regattas + return Collections.emptyMap(); + } + + @Override + public Map getAllCompetitorsAndTheirBoats(Fleet fleet) { + // TODO bug2822: What should we do here? Returning a boat makes only sense when the competitors keep their boats through all regattas + return Collections.emptyMap(); + } + @Override public RegattaLog getRegattaLog() { return null; @@ -268,13 +283,25 @@ public class MetaLeaderboardColumn extends SimpleAbstractRaceColumn implements R } @Override - public void registerCompetitor(Competitor competitor, Fleet fleet) + public void registerCompetitor(CompetitorWithBoat competitorWithBoat, Fleet fleet) throws CompetitorRegistrationOnRaceLogDisabledException { throw new CompetitorRegistrationOnRaceLogDisabledException(); } @Override - public void registerCompetitors(Iterable competitor, Fleet fleet) + public void registerCompetitors(Iterable competitorsWithBoat, Fleet fleet) + throws CompetitorRegistrationOnRaceLogDisabledException { + throw new CompetitorRegistrationOnRaceLogDisabledException(); + } + + @Override + public void registerCompetitor(Competitor competitor, Boat boat, Fleet fleet) + throws CompetitorRegistrationOnRaceLogDisabledException { + throw new CompetitorRegistrationOnRaceLogDisabledException(); + } + + @Override + public void registerCompetitors(Map competitorsAndBoats, Fleet fleet) throws CompetitorRegistrationOnRaceLogDisabledException { throw new CompetitorRegistrationOnRaceLogDisabledException(); } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ApproximatedFixesCalculatorImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ApproximatedFixesCalculatorImpl.java index 8a73a3d23f9..7689159fb69 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ApproximatedFixesCalculatorImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ApproximatedFixesCalculatorImpl.java @@ -19,8 +19,9 @@ public class ApproximatedFixesCalculatorImpl implements ApproximatedFixesCalcula @Override public Iterable approximate(TimePoint earliestStart, TimePoint latestEnd) { return trackedRace.approximate(competitor, - competitor.getBoat().getBoatClass().getMaximumDistanceForCourseApproximation(), earliestStart, - latestEnd); + trackedRace.getRace().getBoatOfCompetitor(competitor).getBoatClass() + .getMaximumDistanceForCourseApproximation(), + earliestStart, latestEnd); } } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/IncrementalApproximatedFixesCalculatorImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/IncrementalApproximatedFixesCalculatorImpl.java index 6abeb3f1382..33f194463c1 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/IncrementalApproximatedFixesCalculatorImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/IncrementalApproximatedFixesCalculatorImpl.java @@ -43,8 +43,8 @@ public class IncrementalApproximatedFixesCalculatorImpl extends ApproximatedFixe public IncrementalApproximatedFixesCalculatorImpl(TrackedRace trackedRace, Competitor competitor) { super(trackedRace, competitor); this.track = trackedRace.getTrack(competitor); - this.minDurationFromLastFixToPreviousMarkPassingToReusePreviousLegFixes = competitor.getBoat().getBoatClass() - .getApproximateManeuverDuration().times(10.0); + this.minDurationFromLastFixToPreviousMarkPassingToReusePreviousLegFixes = trackedRace.getRace() + .getBoatOfCompetitor(competitor).getBoatClass().getApproximateManeuverDuration().times(10.0); } @Override diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverDetectorImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverDetectorImpl.java index 0377eacd779..c13e1cf65dd 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverDetectorImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverDetectorImpl.java @@ -208,8 +208,8 @@ public class ManeuverDetectorImpl implements ManeuverDetector { */ protected List detectManeuvers(TimePoint earliestManeuverStart, TimePoint latestManeuverEnd) { return detectManeuvers(trackedRace.approximate(competitor, - competitor.getBoat().getBoatClass().getMaximumDistanceForCourseApproximation(), earliestManeuverStart, - latestManeuverEnd), earliestManeuverStart, latestManeuverEnd); + trackedRace.getRace().getBoatOfCompetitor(competitor).getBoatClass().getMaximumDistanceForCourseApproximation(), + earliestManeuverStart, latestManeuverEnd), earliestManeuverStart, latestManeuverEnd); } /** @@ -284,7 +284,7 @@ public class ManeuverDetectorImpl implements ManeuverDetector { if (lastCourseChangeDirection != newCourseChangeDirection) { return false; } - Distance threeHullLengths = competitor.getBoat().getBoatClass().getHullLength().scale(3); + Distance threeHullLengths = trackedRace.getRace().getBoatOfCompetitor(competitor).getBoatClass().getHullLength().scale(3); if (currentFix.getTimePoint().asMillis() - previousFix.getTimePoint().asMillis() > getApproximateManeuverDuration().asMillis() && currentFix.getPosition().getDistance(previousFix.getPosition()).compareTo(threeHullLengths) > 0) { @@ -1225,7 +1225,7 @@ public class ManeuverDetectorImpl implements ManeuverDetector { * Gets the approximated duration of the maneuver main curve considering the boat class of the competitor. */ protected Duration getApproximateManeuverDuration() { - return competitor.getBoat().getBoatClass().getApproximateManeuverDuration(); + return trackedRace.getRace().getBoatOfCompetitor(competitor).getBoatClass().getApproximateManeuverDuration(); } } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/regattalike/BaseRegattaLikeImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/regattalike/BaseRegattaLikeImpl.java index aa65fedcf79..6af52a166ce 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/regattalike/BaseRegattaLikeImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/regattalike/BaseRegattaLikeImpl.java @@ -60,7 +60,12 @@ public abstract class BaseRegattaLikeImpl implements IsRegattaLike { public void removeListener(RegattaLikeListener listener) { listeners.remove(listener); } - + + @Override + public boolean canBoatsOfCompetitorsChangePerRace() { + return false; + } + private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException { ois.defaultReadObject(); listeners = new HashSet<>(); diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/regattalike/HasRegattaLike.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/regattalike/HasRegattaLike.java index 9a73f72b3d6..6029f7c64b3 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/regattalike/HasRegattaLike.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/regattalike/HasRegattaLike.java @@ -1,6 +1,7 @@ package com.sap.sailing.domain.regattalike; import com.sap.sailing.domain.abstractlog.race.RaceLog; +import com.sap.sailing.domain.base.Boat; import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.RaceColumn; import com.sap.sailing.domain.base.Regatta; @@ -17,13 +18,30 @@ public interface HasRegattaLike { IsRegattaLike getRegattaLike(); RaceLog getRacelog(String raceColumnName, String fleetName); + + /** + * Determines the boats registered in the regatta log. Note that this is not necessarily the complete set of + * boats participating in this "regatta." For that, use {@link #getAllBoats()}. + */ + Iterable getBoatsRegisteredInRegattaLog(); /** - * Determines the competitors registered in the regatta log. Note that this is not necessarily the complete set of - * competitors participating in this "regatta." For that, use {@link #getAllCompetitors()}. + * Determines the combined set of all boats from all race columns that this object's {@link IsRegattaLike} + * has, as well as those coming from registrations on the {@link #getRegattaLike()}'s + * {@link IsRegattaLike#getRegattaLog() regatta log}. + * + * @see #getRegattaLike() + * @see IsRegattaLike#getRaceColumnByName(String) + * @see RaceColumn#getAllBoats() */ - Iterable getCompetitorsRegisteredInRegattaLog(); + Iterable getAllBoats(); + void registerBoat(Boat boat); + void registerBoats(Iterable boat); + + void deregisterBoat(Boat boat); + void deregisterBoats(Iterable boat); + /** * Determines the combined set of all competitors from all race columns that this object's {@link IsRegattaLike} * has, as well as those coming from registrations on the {@link #getRegattaLike()}'s @@ -34,10 +52,17 @@ public interface HasRegattaLike { * @see RaceColumn#getAllCompetitors() */ Iterable getAllCompetitors(); + + /** + * Determines the competitors registered in the regatta log. Note that this is not necessarily the complete set of + * competitors participating in this "regatta." For that, use {@link #getAllCompetitors()}. + */ + Iterable getCompetitorsRegisteredInRegattaLog(); void registerCompetitor(Competitor competitor); - void registerCompetitors(Iterable competitor); - + void registerCompetitors(Iterable competitors); + void deregisterCompetitor(Competitor competitor); - void deregisterCompetitors(Iterable competitor); + void deregisterCompetitors(Iterable competitors); + } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/regattalike/IsRegattaLike.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/regattalike/IsRegattaLike.java index 63ea0c96859..31d79128404 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/regattalike/IsRegattaLike.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/regattalike/IsRegattaLike.java @@ -26,6 +26,12 @@ public interface IsRegattaLike extends Serializable { */ RegattaLog getRegattaLog(); + /** + * Indicates whether the competitors use the same boat for the whole regatta or change the boat used during the competition + * @return true when the competitors change their boats, false otherwise + */ + boolean canBoatsOfCompetitorsChangePerRace(); + RaceColumn getRaceColumnByName(String raceColumnName); RegattaLikeIdentifier getRegattaLikeIdentifier(); diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/regattalike/LeaderboardThatHasRegattaLike.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/regattalike/LeaderboardThatHasRegattaLike.java index a245e7d2f86..59095c54970 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/regattalike/LeaderboardThatHasRegattaLike.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/regattalike/LeaderboardThatHasRegattaLike.java @@ -1,9 +1,12 @@ package com.sap.sailing.domain.regattalike; +import com.sap.sailing.domain.base.Boat; import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.leaderboard.HasRaceColumnsAndRegattaLike; import com.sap.sailing.domain.leaderboard.Leaderboard; public interface LeaderboardThatHasRegattaLike extends Leaderboard, HasRaceColumnsAndRegattaLike { Iterable getCompetitorsRegisteredInRegattaLog(); + + Iterable getBoatsRegisteredInRegattaLog(); } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/trackimport/DoubleVectorFixImporter.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/trackimport/DoubleVectorFixImporter.java index d54fb0e3efe..1c9efed220c 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/trackimport/DoubleVectorFixImporter.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/trackimport/DoubleVectorFixImporter.java @@ -4,7 +4,9 @@ import java.io.Serializable; import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor; import com.sap.sailing.domain.abstractlog.regatta.RegattaLogEvent; +import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceBoatSensorDataMappingEvent; import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceCompetitorSensorDataMappingEvent; +import com.sap.sailing.domain.base.Boat; import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.common.DeviceIdentifier; import com.sap.sse.common.TimePoint; @@ -22,4 +24,11 @@ public interface DoubleVectorFixImporter extends BaseDoubleVectorFixImporter { RegattaLogDeviceCompetitorSensorDataMappingEvent createEvent(TimePoint createdAt, TimePoint logicalTimePoint, AbstractLogEventAuthor author, Serializable id, Competitor mappedTo, DeviceIdentifier device, TimePoint from, TimePoint to); + + /** + * Creates the {@link RegattaLogEvent} for the DeviceMapping. + */ + RegattaLogDeviceBoatSensorDataMappingEvent createEvent(TimePoint createdAt, TimePoint logicalTimePoint, + AbstractLogEventAuthor author, Serializable id, Boat mappedTo, DeviceIdentifier device, + TimePoint from, TimePoint to); } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/RaceTrackingConnectivityParameters.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/RaceTrackingConnectivityParameters.java index 3add0406c9f..831043f97f6 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/RaceTrackingConnectivityParameters.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/RaceTrackingConnectivityParameters.java @@ -4,6 +4,7 @@ import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver; import com.sap.sailing.domain.base.Fleet; import com.sap.sailing.domain.base.Regatta; import com.sap.sailing.domain.base.Series; +import com.sap.sailing.domain.leaderboard.LeaderboardGroupResolver; /** * Different tracking providers require different sets of arguments to start tracking a race. @@ -39,7 +40,7 @@ public interface RaceTrackingConnectivityParameters { * tracker. */ RaceTracker createRaceTracker(TrackedRegattaRegistry trackedRegattaRegistry, WindStore windStore, - RaceLogResolver raceLogResolver, long timeoutInMilliseconds) throws Exception; + RaceLogResolver raceLogResolver, LeaderboardGroupResolver leaderboardGroupResolver, long timeoutInMilliseconds) throws Exception; /** * Starts a {@link RaceTracker}, associating the resulting races with the {@link Regatta} passed as argument instead @@ -53,7 +54,7 @@ public interface RaceTrackingConnectivityParameters { * {@link TrackedRegattaRegistry#removeRace(Regatta, com.sap.sailing.domain.base.RaceDefinition)}. */ RaceTracker createRaceTracker(Regatta regatta, TrackedRegattaRegistry trackedRegattaRegistry, WindStore windStore, - RaceLogResolver raceLogResolver, long timeoutInMilliseconds) throws Exception; + RaceLogResolver raceLogResolver, LeaderboardGroupResolver leaderboardGroupResolver, long timeoutInMilliseconds) throws Exception; /** * Deliver an ID object equal to that of the {@link RaceTracker#getID()} delivered by the {@link RaceTracker} diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedLegOfCompetitor.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedLegOfCompetitor.java index a25baa3c6a7..56ac285cd92 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedLegOfCompetitor.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedLegOfCompetitor.java @@ -2,6 +2,7 @@ package com.sap.sailing.domain.tracking; import java.io.Serializable; +import com.sap.sailing.domain.base.Boat; import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.Leg; import com.sap.sailing.domain.common.Bearing; @@ -21,6 +22,8 @@ public interface TrackedLegOfCompetitor extends Serializable { Competitor getCompetitor(); + Boat getBoat(); + /** * How much time did the {@link #getCompetitor competitor} spend in this {@link #getLeg() leg} at * timePoint? If the competitor hasn't started the leg yet at timePoint, null diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRace.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRace.java index 68272f8ce4a..965b2d0b48d 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRace.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRace.java @@ -13,6 +13,7 @@ import com.sap.sailing.domain.abstractlog.race.state.racingprocedure.RacingProce import com.sap.sailing.domain.abstractlog.regatta.RegattaLog; import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDefineMarkEvent; import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMappingEvent; +import com.sap.sailing.domain.base.Boat; import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.Course; import com.sap.sailing.domain.base.Leg; @@ -746,6 +747,10 @@ public interface TrackedRace extends Serializable, IsManagedByCache> implem private final ConcurrentMap currentLeg; private final MarkPassing NULL_MARK_PASSING = new MarkPassingImpl(null, null, null); - private final TrackedLegOfCompetitor NULL_TRACKED_LEG_OF_COMPETITOR = new TrackedLegOfCompetitorImpl(null, null); + private final TrackedLegOfCompetitor NULL_TRACKED_LEG_OF_COMPETITOR = new TrackedLegOfCompetitorImpl(null, null, null); /** * @param lessIsBetter diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/DummyMarkPassingWithTimePointOnly.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/DummyMarkPassingWithTimePointOnly.java index 77e081606c8..35f29d3f670 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/DummyMarkPassingWithTimePointOnly.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/DummyMarkPassingWithTimePointOnly.java @@ -2,7 +2,6 @@ package com.sap.sailing.domain.tracking.impl; import java.net.URI; -import com.sap.sailing.domain.base.Boat; import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.CompetitorChangeListener; import com.sap.sailing.domain.base.Nationality; @@ -44,26 +43,30 @@ public class DummyMarkPassingWithTimePointOnly implements MarkPassing { return "Dummy"; } + @Override + public String getShortName() { + return "Dummy"; + } + @Override public String getId() { return "Dummy"; } + @Override + public boolean hasBoat() { + return false; + } + @Override public Team getTeam() { return null; } - @Override public Nationality getNationality() { return null; } - @Override - public Boat getBoat() { - return null; - } - @Override public IsManagedByCache resolve(SharedDomainFactory domainFactory) { return this; @@ -111,6 +114,11 @@ public class DummyMarkPassingWithTimePointOnly implements MarkPassing { public String getSearchTag() { return null; } + + @Override + public String getShortInfo() { + return getShortName(); + } }; } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/DynamicTrackedRaceLogListener.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/DynamicTrackedRaceLogListener.java index 2547b155557..aaa83b444ec 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/DynamicTrackedRaceLogListener.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/DynamicTrackedRaceLogListener.java @@ -296,7 +296,7 @@ public class DynamicTrackedRaceLogListener extends BaseRaceLogEventVisitor { @Override public void visit(RaceLogFixedMarkPassingEvent event) { if (markPassingUpdateListener != null) { - markPassingUpdateListener.addFixedPassing(event.getInvolvedBoats().get(0), event.getZeroBasedIndexOfPassedWaypoint(), + markPassingUpdateListener.addFixedPassing(event.getInvolvedCompetitors().get(0), event.getZeroBasedIndexOfPassedWaypoint(), event.getTimePointOfFixedPassing()); } } @@ -304,7 +304,7 @@ public class DynamicTrackedRaceLogListener extends BaseRaceLogEventVisitor { @Override public void visit(RaceLogSuppressedMarkPassingsEvent event) { if (markPassingUpdateListener != null) { - markPassingUpdateListener.addSuppressedPassing(event.getInvolvedBoats().get(0), + markPassingUpdateListener.addSuppressedPassing(event.getInvolvedCompetitors().get(0), event.getZeroBasedIndexOfFirstSuppressedWaypoint()); } } @@ -326,10 +326,10 @@ public class DynamicTrackedRaceLogListener extends BaseRaceLogEventVisitor { } if (revokedEvent instanceof RaceLogSuppressedMarkPassingsEvent) { - markPassingUpdateListener.removeSuppressedPassing(revokedEvent.getInvolvedBoats().get(0)); + markPassingUpdateListener.removeSuppressedPassing(revokedEvent.getInvolvedCompetitors().get(0)); } if (revokedEvent instanceof RaceLogFixedMarkPassingEvent) { - markPassingUpdateListener.removeFixedPassing(revokedEvent.getInvolvedBoats().get(0), + markPassingUpdateListener.removeFixedPassing(revokedEvent.getInvolvedCompetitors().get(0), ((RaceLogFixedMarkPassingEvent) revokedEvent).getZeroBasedIndexOfPassedWaypoint()); } } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedLegImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedLegImpl.java index 6bcbaf8e926..02765e3b3bd 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedLegImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedLegImpl.java @@ -70,7 +70,7 @@ public class TrackedLegImpl implements TrackedLeg { this.trackedRace = trackedRace; trackedLegsOfCompetitors = new HashMap(); for (Competitor competitor : competitors) { - trackedLegsOfCompetitors.put(competitor, new TrackedLegOfCompetitorImpl(this, competitor)); + trackedLegsOfCompetitors.put(competitor, new TrackedLegOfCompetitorImpl(this, competitor, trackedRace.getBoatOfCompetitor(competitor))); } trackedRace.addListener(new CacheClearingRaceChangeListener()); competitorTracksOrderedByRank = new ConcurrentHashMap<>(); diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedLegOfCompetitorImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedLegOfCompetitorImpl.java index ff3b42f9189..0501c5af59a 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedLegOfCompetitorImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedLegOfCompetitorImpl.java @@ -5,6 +5,7 @@ import java.util.Iterator; import java.util.List; import java.util.Set; +import com.sap.sailing.domain.base.Boat; import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.Leg; import com.sap.sailing.domain.base.Mark; @@ -45,10 +46,12 @@ public class TrackedLegOfCompetitorImpl implements TrackedLegOfCompetitor { private static final long serialVersionUID = -7060076837717432808L; private final TrackedLegImpl trackedLeg; private final Competitor competitor; + private final Boat boat; - public TrackedLegOfCompetitorImpl(TrackedLegImpl trackedLeg, Competitor competitor) { + public TrackedLegOfCompetitorImpl(TrackedLegImpl trackedLeg, Competitor competitor, Boat boat) { this.trackedLeg = trackedLeg; this.competitor = competitor; + this.boat = boat; } @Override @@ -61,6 +64,11 @@ public class TrackedLegOfCompetitorImpl implements TrackedLegOfCompetitor { return competitor; } + @Override + public Boat getBoat() { + return boat; + } + @Override public Leg getLeg() { return trackedLeg.getLeg(); diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java index d82fc8e09d1..cfde4bc9fac 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java @@ -53,6 +53,7 @@ import com.sap.sailing.domain.abstractlog.race.state.impl.RaceStateImpl; import com.sap.sailing.domain.abstractlog.race.state.racingprocedure.ReadonlyRacingProcedure; import com.sap.sailing.domain.abstractlog.regatta.RegattaLog; import com.sap.sailing.domain.abstractlog.regatta.tracking.analyzing.impl.RegattaLogDefinedMarkAnalyzer; +import com.sap.sailing.domain.base.Boat; import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.Course; import com.sap.sailing.domain.base.CourseListener; @@ -1460,6 +1461,24 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl return result; } + @Override + public Boat getBoatOfCompetitor(Competitor competitor) { + return getRace().getBoatOfCompetitor(competitor); + } + + @Override + public Competitor getCompetitorOfBoat(Boat boat) { + if (boat == null) { + return null; + } + for (Map.Entry competitorWithBoat : getRace().getCompetitorsAndTheirBoats().entrySet()) { + if (boat.equals(competitorWithBoat.getValue())) { + return competitorWithBoat.getKey(); + } + } + return null; + } + @Override public List getCompetitorsFromBestToWorst(TimePoint timePoint) { return getCompetitorsFromBestToWorst(timePoint, new LeaderboardDTOCalculationReuseCache(timePoint)); diff --git a/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/AbstractListFilterTest.java b/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/AbstractListFilterTest.java index ff681afcd8e..8fe2bc16f10 100755 --- a/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/AbstractListFilterTest.java +++ b/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/AbstractListFilterTest.java @@ -25,12 +25,12 @@ public class AbstractListFilterTest { @Before public void setUp() { - all.add(new BoatImpl("Race 1", null, "Kiel")); - all.add(new BoatImpl("Race 2", null, "Kiel")); - all.add(new BoatImpl("Race 3", null, "Kiel")); - all.add(new BoatImpl("Race 1", null, "Wannsee")); - all.add(new BoatImpl("Race 2", null, "Wannsee")); - all.add(new BoatImpl("Final", null, "Wannsee")); + all.add(new BoatImpl("1", "Race 1", null, "Kiel")); + all.add(new BoatImpl("2", "Race 2", null, "Kiel")); + all.add(new BoatImpl("3", "Race 3", null, "Kiel")); + all.add(new BoatImpl("4", "Race 1", null, "Wannsee")); + all.add(new BoatImpl("5", "Race 2", null, "Wannsee")); + all.add(new BoatImpl("6", "Final", null, "Wannsee")); } @Test diff --git a/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/MockedTrackedRace.java b/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/MockedTrackedRace.java index d0b893acc81..c6990f69bd3 100644 --- a/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/MockedTrackedRace.java +++ b/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/MockedTrackedRace.java @@ -14,6 +14,7 @@ import java.util.concurrent.Future; import com.sap.sailing.domain.abstractlog.race.RaceLog; import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver; import com.sap.sailing.domain.abstractlog.regatta.RegattaLog; +import com.sap.sailing.domain.base.Boat; import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.CourseBase; import com.sap.sailing.domain.base.Leg; @@ -888,6 +889,15 @@ public class MockedTrackedRace implements DynamicTrackedRace { } @Override + public Boat getBoatOfCompetitor(Competitor competitor) { + return null; + } + + @Override + public Competitor getCompetitorOfBoat(Boat boat) { + return null; + } + public Distance getEstimatedDistanceToComplete(TimePoint now) { return null; } diff --git a/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/PingMarkViaRegattaLogTest.java b/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/PingMarkViaRegattaLogTest.java index e6402092f02..5ba15b24bcb 100644 --- a/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/PingMarkViaRegattaLogTest.java +++ b/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/PingMarkViaRegattaLogTest.java @@ -39,7 +39,8 @@ public class PingMarkViaRegattaLogTest { service.getMongoObjectFactory().getDatabase().dropDatabase(); Series series = new SeriesImpl("series", false, /* isFleetsCanRunInParallel */ true, Collections.singletonList(fleet), Collections.singletonList(columnName), service); - Regatta regatta = service.createRegatta(RegattaImpl.getDefaultName("regatta", "Laser"), "Laser", /*startDate*/ null, /*endDate*/ null, + Regatta regatta = service.createRegatta(RegattaImpl.getDefaultName("regatta", "Laser"), "Laser", + /* canBoatsOfCompetitorsChangePerRace */ true, /*startDate*/ null, /*endDate*/ null, UUID.randomUUID(), Collections.singletonList(series), false, new HighPoint(), UUID.randomUUID(), /*buoyZoneRadiusInHullLengths*/2.0, /* useStartTimeInference */ true, /* controlTrackingFromStartAndFinishTimes */ false, RankingMetricsFactory.getRankingMetricConstructor(RankingMetrics.ONE_DESIGN)); diff --git a/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/SlicingTest.java b/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/SlicingTest.java index 8a4b954e15b..f0beb4ed1ec 100644 --- a/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/SlicingTest.java +++ b/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/SlicingTest.java @@ -55,9 +55,10 @@ public class SlicingTest { private final String fleetName = "default"; private final String columnNameOriginalRace = "original"; private final String columnNameSlicedRace = "sliced"; - private final CompetitorDescriptor competitor = new CompetitorDescriptor(null, regattaName, boatClassName, - columnNameOriginalRace, fleetName, "test1", "test", "test", "test", - CountryCodeFactoryImpl.INSTANCE.getFromTwoLetterISOName("de"), null, 1.0, null); + + private final CompetitorDescriptor competitor = new CompetitorDescriptor(null, regattaName, columnNameOriginalRace, fleetName, null, + "test1", "test", "test", null, CountryCodeFactoryImpl.INSTANCE.getFromTwoLetterISOName("de"), + 1.0, null, null, "B1", boatClassName, "GER 123"); private SailingServiceImplMock sailingService; @@ -94,7 +95,7 @@ public class SlicingTest { new SeriesCreationParametersDTO(fleets, false, false, true, false, new int[0], false, 0)); final RegattaCreationParametersDTO regattaCreationParameters = new RegattaCreationParametersDTO( seriesCreationParameters); - final RegattaDTO regatta = sailingService.createRegatta(regattaName, boatClassName, null, null, + final RegattaDTO regatta = sailingService.createRegatta(regattaName, boatClassName, false, null, null, regattaCreationParameters, false, ScoringSchemeType.HIGH_POINT, null, 3.0, false, false, RankingMetrics.ONE_DESIGN); final List> columnNames = new ArrayList<>(); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/google/gwt/user/client/rpc/core/com/sap/sailing/domain/common/CompetitorDescriptor_CustomFieldSerializer.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/google/gwt/user/client/rpc/core/com/sap/sailing/domain/common/CompetitorDescriptor_CustomFieldSerializer.java index 29b37f9a2e6..80a3b59cca2 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/google/gwt/user/client/rpc/core/com/sap/sailing/domain/common/CompetitorDescriptor_CustomFieldSerializer.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/google/gwt/user/client/rpc/core/com/sap/sailing/domain/common/CompetitorDescriptor_CustomFieldSerializer.java @@ -1,6 +1,7 @@ package com.google.gwt.user.client.rpc.core.com.sap.sailing.domain.common; import java.util.List; +import java.util.UUID; import com.google.gwt.user.client.rpc.CustomFieldSerializer; import com.google.gwt.user.client.rpc.SerializationException; @@ -24,16 +25,19 @@ public class CompetitorDescriptor_CustomFieldSerializer extends CustomFieldSeria streamWriter.writeObject(instance.getPersons()); streamWriter.writeString(instance.getEventName()); streamWriter.writeString(instance.getRegattaName()); - streamWriter.writeString(instance.getBoatClassName()); streamWriter.writeString(instance.getRaceName()); streamWriter.writeString(instance.getFleetName()); - streamWriter.writeString(instance.getSailNumber()); + streamWriter.writeObject(instance.getCompetitorUUID()); streamWriter.writeString(instance.getName()); + streamWriter.writeString(instance.getShortName()); streamWriter.writeString(instance.getTeamName()); - streamWriter.writeString(instance.getBoatName()); streamWriter.writeObject(instance.getCountryCode()); streamWriter.writeObject(instance.getTimeOnTimeFactor()); streamWriter.writeObject(instance.getTimeOnDistanceAllowancePerNauticalMile()); + streamWriter.writeObject(instance.getBoatUUID()); + streamWriter.writeString(instance.getBoatName()); + streamWriter.writeString(instance.getBoatClassName()); + streamWriter.writeString(instance.getSailNumber()); } @Override @@ -51,11 +55,27 @@ public class CompetitorDescriptor_CustomFieldSerializer extends CustomFieldSeria throws SerializationException { @SuppressWarnings("unchecked") final List persons = (List) streamReader.readObject(); - return new CompetitorDescriptor(streamReader.readString(), streamReader.readString(), - streamReader.readString(), streamReader.readString(), streamReader.readString(), - streamReader.readString(), streamReader.readString(), streamReader.readString(), - streamReader.readString(), (CountryCode) streamReader.readObject(), - persons, (Double) streamReader.readObject(), (Duration) streamReader.readObject()); + String eventName = streamReader.readString(); + String regattaName = streamReader.readString(); + String raceName = streamReader.readString(); + String fleetName = streamReader.readString(); + UUID competitorUUID = (UUID) streamReader.readObject(); + String fullName = streamReader.readString(); + String shortName = streamReader.readString(); + String teamName = streamReader.readString(); + CountryCode countryCode = (CountryCode) streamReader.readObject(); + Double timeOnTimeFactor = (Double) streamReader.readObject(); + Duration timeOnDistanceAllowancePerNauticalMile = (Duration) streamReader.readObject(); + UUID boatUUID = (UUID) streamReader.readObject(); + String boatName = streamReader.readString(); + String boatClassName = streamReader.readString(); + String sailNumber = streamReader.readString(); + + return new CompetitorDescriptor(eventName, regattaName, + raceName, fleetName, competitorUUID, + fullName, shortName, teamName, + persons, countryCode, timeOnTimeFactor, + timeOnDistanceAllowancePerNauticalMile, boatUUID, boatName, boatClassName, sailNumber); } @Override diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/nodes/IdleOverallLeaderBoardNode.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/nodes/IdleOverallLeaderBoardNode.java index eec95153037..8208a449122 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/nodes/IdleOverallLeaderBoardNode.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/nodes/IdleOverallLeaderBoardNode.java @@ -32,7 +32,6 @@ public class IdleOverallLeaderBoardNode extends FiresPlaceNode { public IdleOverallLeaderBoardNode(AutoPlayClientFactory cf) { super(IdleOverallLeaderBoardNode.class.getName()); this.cf = cf; - } public void onStart() { @@ -42,13 +41,12 @@ public class IdleOverallLeaderBoardNode extends FiresPlaceNode { List raceDetails = new ArrayList<>(); // raceDetails.add(DetailType.RACE_RANK); - final MultiRaceLeaderboardSettings leaderboardSettings = new MultiRaceLeaderboardSettings( /* maneuverDetailsToShow */ null, /* legDetailsToShow */ null, raceDetails, overallDetails, /* namesOfRaceColumnsToShow */ null, /* numberOfLastRacesToShow */ null, /* delayBetweenAutoAdvancesInMilliseconds */ null, RaceColumnSelectionStrategies.EXPLICIT, - /* showAddedScores */ true, /* showCompetitorSailIdColumn */ true, - /* showCompetitorFullNameColumn */ false, /* isCompetitorNationalityColumnVisible */ true); + /* showAddedScores */ true, /* showCompetitorShortNameColumn */ true, + /* showCompetitorFullNameColumn */ false, /* showCompetitorBoatInfoColumn */ false, /* isCompetitorNationalityColumnVisible */ true); timer = new Timer( // perform the first request as "live" but don't by default auto-play diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/places/screens/afterliveraceloop/boats/RaceEndWithBoatsPresenterImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/places/screens/afterliveraceloop/boats/RaceEndWithBoatsPresenterImpl.java index d842cb4add1..8336e6ff22c 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/places/screens/afterliveraceloop/boats/RaceEndWithBoatsPresenterImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/places/screens/afterliveraceloop/boats/RaceEndWithBoatsPresenterImpl.java @@ -14,9 +14,9 @@ import com.sap.sailing.gwt.autoplay.client.app.AutoPlayClientFactory; import com.sap.sailing.gwt.autoplay.client.app.AutoPlayPresenterConfigured; import com.sap.sailing.gwt.autoplay.client.events.AutoPlayHeaderEvent; import com.sap.sailing.gwt.settings.client.leaderboard.SingleRaceLeaderboardSettings; -import com.sap.sailing.gwt.ui.client.CompetitorSelectionModel; import com.sap.sailing.gwt.ui.client.FlagImageResolverImpl; import com.sap.sailing.gwt.ui.client.LeaderboardUpdateListener; +import com.sap.sailing.gwt.ui.client.RaceCompetitorSelectionModel; import com.sap.sailing.gwt.ui.client.SailingServiceAsync; import com.sap.sailing.gwt.ui.client.StringMessages; import com.sap.sailing.gwt.ui.leaderboard.LeaderboardEntryPoint; @@ -34,7 +34,7 @@ public class RaceEndWithBoatsPresenterImpl extends AutoPlayPresenterConfigured compList = new ArrayList<>(); private com.sap.sse.gwt.client.player.Timer timer; @@ -97,12 +97,12 @@ public class PreLiveRaceLeaderBoardWithImagePresenterImpl final SingleRaceLeaderboardSettings leaderboardSettings = new SingleRaceLeaderboardSettings( /* maneuverDetailsToShow */ null, /* legDetailsToShow */ null, /* raceDetailsToShow */ null, /* overallDetailsToShow */ null, /* delayBetweenAutoAdvancesInMilliseconds */ null, - /* showAddedScores */ false, /* showCompetitorSailIdColumn */ false, + /* showAddedScores */ false, /* showCompetitorShortNameColumn */ false, /* showCompetitorFullNameColumn */ false, /* isCompetitorNationalityColumnVisible */ false, - /* showRaceRankColumn */ false); + /* showCompetitorBoatInfoColumn */ false, /* showRaceRankColumn */ false); GWT.log("event " + getSlideCtx().getEvent()); - competitorSelectionProvider = new CompetitorSelectionModel(/* hasMultiSelection */ false); + competitorSelectionProvider = new RaceCompetitorSelectionModel(/* hasMultiSelection */ false); timer = new com.sap.sse.gwt.client.player.Timer( // perform the first request as "live" but don't by default auto-play diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/utils/AutoplayHelper.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/utils/AutoplayHelper.java index 6e759441cfa..041025e2c0c 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/utils/AutoplayHelper.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/utils/AutoplayHelper.java @@ -17,7 +17,7 @@ import com.sap.sailing.domain.common.dto.RaceColumnDTO; import com.sap.sailing.gwt.home.communication.SailingDispatchSystem; 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.RaceTimesInfoProviderListener; import com.sap.sailing.gwt.ui.client.SailingServiceAsync; @@ -161,9 +161,9 @@ public class AutoplayHelper { public final RaceTimesInfoProvider creationTimeProvider; public final Timer raceboardTimer; public final RaceMap raceboardPerspective; - public final CompetitorSelectionModel csel; + public final RaceCompetitorSelectionModel csel; - public RVWrapper(RaceMap raceboardPerspective, CompetitorSelectionModel competitorSelectionProvider, + public RVWrapper(RaceMap raceboardPerspective, RaceCompetitorSelectionModel competitorSelectionProvider, Timer raceboardTimer, RaceTimesInfoProvider creationTimeProvider) { this.raceboardPerspective = raceboardPerspective; this.csel = competitorSelectionProvider; @@ -288,7 +288,7 @@ public class AutoplayHelper { RaceMapLifecycle raceMapLifecycle = new RaceMapLifecycle(StringMessages.INSTANCE); final CompetitorColorProvider colorProvider = new CompetitorColorProviderImpl(currentLiveRace, result); - CompetitorSelectionModel competitorSelectionProvider = new CompetitorSelectionModel( + RaceCompetitorSelectionModel competitorSelectionProvider = new RaceCompetitorSelectionModel( /* hasMultiSelection */ true, colorProvider); competitorSelectionProvider.setCompetitors(competitors); RaceMap raceboardPerspective = new RaceMap(null, null, raceMapLifecycle, settings, sailingService, diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/communication/event/SimpleCompetitorDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/communication/event/SimpleCompetitorDTO.java index ebde9a2c6af..5215325e6bc 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/communication/event/SimpleCompetitorDTO.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/communication/event/SimpleCompetitorDTO.java @@ -14,7 +14,7 @@ public class SimpleCompetitorDTO extends NamedDTO implements DTO, Serializable, private static final long serialVersionUID = -5743976446085202047L; - private String sailID; + private String shortInfo; private String twoLetterIsoCountryCode; private String flagImageURL; @@ -26,32 +26,38 @@ public class SimpleCompetitorDTO extends NamedDTO implements DTO, Serializable, super(competitor.getName()); final Nationality nationality = competitor.getTeam().getNationality(); CountryCode countryCode = nationality == null ? null : nationality.getCountryCode(); - this.sailID = competitor.getBoat().getSailID(); this.twoLetterIsoCountryCode = countryCode == null ? null : countryCode.getTwoLetterISOCode(); this.flagImageURL = competitor.getFlagImage() == null ? null : competitor.getFlagImage().toString(); + this.shortInfo = competitor.getShortInfo(); + if (shortInfo == null) { + this.shortInfo = competitor.getName(); + } } @GwtIncompatible public SimpleCompetitorDTO(CompetitorDTO competitor) { super(competitor.getName()); - this.sailID = competitor.getSailID(); + this.shortInfo = competitor.getShortInfo(); + if (shortInfo == null) { + this.shortInfo = competitor.getName(); + } this.twoLetterIsoCountryCode = competitor.getTwoLetterIsoCountryCode(); this.flagImageURL = competitor.getFlagImageURL(); } - public SimpleCompetitorDTO(String name, String sailID, String twoLetterIsoCountryCode, String flagImageURL) { + public SimpleCompetitorDTO(String name, String shortInfo, String twoLetterIsoCountryCode, String flagImageURL) { super(name); - this.sailID = sailID; + this.shortInfo = shortInfo; this.twoLetterIsoCountryCode = twoLetterIsoCountryCode; this.flagImageURL = flagImageURL; } - public String getSailID() { - return sailID; + public String getShortInfo() { + return shortInfo; } - public void setSailID(String sailID) { - this.sailID = sailID; + public void setShortInfo(String shortInfo) { + this.shortInfo = shortInfo; } public String getTwoLetterIsoCountryCode() { @@ -74,7 +80,7 @@ public class SimpleCompetitorDTO extends NamedDTO implements DTO, Serializable, public int hashCode() { final int prime = 31; int result = super.hashCode(); - result = prime * result + ((sailID == null) ? 0 : sailID.hashCode()); + result = prime * result + ((shortInfo == null) ? 0 : shortInfo.hashCode()); return result; } @@ -87,18 +93,18 @@ public class SimpleCompetitorDTO extends NamedDTO implements DTO, Serializable, if (getClass() != obj.getClass()) return false; SimpleCompetitorDTO other = (SimpleCompetitorDTO) obj; - if (sailID == null) { - if (other.sailID != null) + if (shortInfo == null) { + if (other.shortInfo != null) return false; - } else if (!sailID.equals(other.sailID)) + } else if (!shortInfo.equals(other.shortInfo)) return false; return true; } @Override public int compareTo(SimpleCompetitorDTO obj) { - int compareSailIds = this.sailID.compareTo(obj.sailID); - return compareSailIds == 0 ? this.getName().compareTo(obj.getName()): compareSailIds; + int compareShortInfos = this.shortInfo.compareTo(obj.shortInfo); + return compareShortInfos == 0 ? this.getName().compareTo(obj.getName()): compareShortInfos; } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/communication/event/SimpleCompetitorWithIdDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/communication/event/SimpleCompetitorWithIdDTO.java index 21860294e5e..b4038c5bec0 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/communication/event/SimpleCompetitorWithIdDTO.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/communication/event/SimpleCompetitorWithIdDTO.java @@ -25,9 +25,9 @@ public class SimpleCompetitorWithIdDTO extends SimpleCompetitorDTO { this.idAsString = competitor.getIdAsString(); } - public SimpleCompetitorWithIdDTO(String idAsString, String name, String sailID, + public SimpleCompetitorWithIdDTO(String idAsString, String name, String shortInfo, String twoLetterIsoCountryCode, String flagImageURL) { - super(name, sailID, twoLetterIsoCountryCode, flagImageURL); + super(name, shortInfo, twoLetterIsoCountryCode, flagImageURL); this.idAsString = idAsString; } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/communication/user/profile/GetCompetitorSuggestionAction.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/communication/user/profile/GetCompetitorSuggestionAction.java index 8b70fe591f4..5765020d369 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/communication/user/profile/GetCompetitorSuggestionAction.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/communication/user/profile/GetCompetitorSuggestionAction.java @@ -31,8 +31,7 @@ public class GetCompetitorSuggestionAction implements SailingAction competitorFilter = new AbstractListFilter() { @Override public Iterable getStrings(Competitor competitor) { - if (competitor.getBoat() == null) return Arrays.asList(competitor.getName()); - return Arrays.asList(competitor.getBoat().getSailID(), competitor.getName()); + return Arrays.asList(competitor.getName()); } }; @@ -64,7 +63,7 @@ public class GetCompetitorSuggestionAction implements SailingAction getFilteredCompetitors(SailingDispatchContext ctx) { - Iterable allCompetitors = ctx.getRacingEventService().getCompetitorStore().getCompetitors(); + Iterable allCompetitors = ctx.getRacingEventService().getCompetitorStore().getAllCompetitors(); return competitorFilter.applyFilter(queryTokens, Util.addAll(allCompetitors, new ArrayList())); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/racelist/RaceListColumnFactory.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/racelist/RaceListColumnFactory.java index 7c065a84086..44bf6636e0a 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/racelist/RaceListColumnFactory.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/racelist/RaceListColumnFactory.java @@ -453,7 +453,7 @@ public class RaceListColumnFactory { String sailIdStyle = CSS.race_item_sailid(); String nameStyle = CSS.race_item_winner(); sb.append(TEMPLATE.winner( sailIdStyle, nameStyle, flagImageUri.asString(), - value.getSailID(), value.getName())); + value.getShortInfo(), value.getName())); } } }; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/standings/StandingsListCompetitor.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/standings/StandingsListCompetitor.java index 01b7acb13c1..caf8ec3ba6d 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/standings/StandingsListCompetitor.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/standings/StandingsListCompetitor.java @@ -33,7 +33,7 @@ public class StandingsListCompetitor extends UIObject { SimpleCompetitorDTO competitor = item.getCompetitor(); rankUi.setInnerText(Integer.toString(item.getRank())); - teamIdUi.setInnerText(competitor.getSailID()); + teamIdUi.setInnerText(competitor.getShortInfo()); teamNameUi.setInnerText(competitor.getName()); String pointsString = i18n.pointsValue(item.getNetPoints()); if (showRaceCounts) { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/partials/minileaderboard/MinileaderboardBoxItem.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/partials/minileaderboard/MinileaderboardBoxItem.java index d86701a819f..9bc551463cc 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/partials/minileaderboard/MinileaderboardBoxItem.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/partials/minileaderboard/MinileaderboardBoxItem.java @@ -28,7 +28,7 @@ public class MinileaderboardBoxItem extends Widget { public MinileaderboardBoxItem(MiniLeaderboardItemDTO entry, boolean showRaceCount, FlagImageResolver flagImageResolver) { setElement(uiBinder.createAndBindUi(this)); competitorNameUi.setInnerText(entry.getCompetitor().getName()); - competitorCountryNameUi.setInnerText(String.valueOf(entry.getCompetitor().getSailID())); + competitorCountryNameUi.setInnerText(String.valueOf(entry.getCompetitor().getShortInfo())); competitorRankUi.setInnerText(String.valueOf(entry.getRank()) + "."); competitorPointsUi.setInnerText(StringMessages.INSTANCE.pointsValue(entry.getNetPoints())); if (showRaceCount) { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/partials/filter/RacesByCompetitorTextBoxFilter.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/partials/filter/RacesByCompetitorTextBoxFilter.java index ec2ec51da32..99f0d86007d 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/partials/filter/RacesByCompetitorTextBoxFilter.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/partials/filter/RacesByCompetitorTextBoxFilter.java @@ -20,12 +20,12 @@ public class RacesByCompetitorTextBoxFilter extends AbstractListSuggestBoxFilter super(new AbstractListSuggestOracle() { @Override protected Iterable getMatchingStrings(SimpleCompetitorDTO value) { - return Arrays.asList(value.getName(), value.getSailID()); + return Arrays.asList(value.getName(), value.getShortInfo()); } @Override protected String createSuggestionKeyString(SimpleCompetitorDTO value) { - return value.getSailID(); + return value.getShortInfo(); } @Override diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/partials/multiselection/AbstractSuggestedMultiSelectionCompetitorDataProvider.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/partials/multiselection/AbstractSuggestedMultiSelectionCompetitorDataProvider.java index b4d8585ac68..98fa7c6c8cb 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/partials/multiselection/AbstractSuggestedMultiSelectionCompetitorDataProvider.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/partials/multiselection/AbstractSuggestedMultiSelectionCompetitorDataProvider.java @@ -23,7 +23,7 @@ public abstract class AbstractSuggestedMultiSelectionCompetitorDataProvider exte @Override public final String createSuggestionKeyString(SimpleCompetitorWithIdDTO value) { - return value.getSailID(); + return value.getShortInfo(); } @Override diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/partials/multiselection/SuggestedMultiSelectionCompetitorItemDescription.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/partials/multiselection/SuggestedMultiSelectionCompetitorItemDescription.java index 2679f649606..f2d29a1c1c0 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/partials/multiselection/SuggestedMultiSelectionCompetitorItemDescription.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/partials/multiselection/SuggestedMultiSelectionCompetitorItemDescription.java @@ -25,7 +25,7 @@ class SuggestedMultiSelectionCompetitorItemDescription extends Widget { setElement(uiBinder.createAndBindUi(this)); flagImageUi.getStyle().setBackgroundImage("url('" + flagImageResolver.getFlagImageUri( competitor.getFlagImageURL(), competitor.getTwoLetterIsoCountryCode()).asString() + "')"); - sailIdUi.setInnerText(competitor.getSailID()); + sailIdUi.setInnerText(competitor.getShortInfo()); nameUi.setInnerText(competitor.getName()); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/partials/statistics/AbstractStatisticsBoxPresenter.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/partials/statistics/AbstractStatisticsBoxPresenter.java index 5281bfbe634..56a507592a9 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/partials/statistics/AbstractStatisticsBoxPresenter.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/partials/statistics/AbstractStatisticsBoxPresenter.java @@ -16,7 +16,7 @@ public abstract class AbstractStatisticsBoxPresenter { public void addCompetitorItem(DataResource icon, String name, SimpleCompetitorDTO competitor) { if (competitor != null) { - addItem(icon, name, competitor.getSailID() != null ? competitor.getSailID() : competitor.getName()); + addItem(icon, name, competitor.getShortInfo() != null ? competitor.getShortInfo() : competitor.getName()); } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/settings/client/leaderboard/LeaderboardSettings.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/settings/client/leaderboard/LeaderboardSettings.java index 26240176092..18191d4832a 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/settings/client/leaderboard/LeaderboardSettings.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/settings/client/leaderboard/LeaderboardSettings.java @@ -36,9 +36,10 @@ public abstract class LeaderboardSettings extends AbstractGenericSerializableSet */ protected BooleanSetting showAddedScores; - protected BooleanSetting showCompetitorSailIdColumn; + protected BooleanSetting showCompetitorShortNameColumn; protected BooleanSetting showCompetitorFullNameColumn; - + protected BooleanSetting showCompetitorBoatInfoColumn; + @Override protected void addChildSettings() { isShowCompetitorNationality = new BooleanSetting("showCompetitorNationality", this, false); @@ -60,8 +61,9 @@ public abstract class LeaderboardSettings extends AbstractGenericSerializableSet overallDetailsToShow = new EnumSetSetting<>("overallDetailsToShow", this, overallDetails, DetailType::valueOf); delayBetweenAutoAdvancesInMilliseconds = new LongSetting("delayBetweenAutoAdvancesInMilliseconds", this, LeaderboardEntryPoint.DEFAULT_REFRESH_INTERVAL_MILLIS); showAddedScores = new BooleanSetting("showAddedScores", this, false); - showCompetitorSailIdColumn = new BooleanSetting("showCompetitorSailIdColumn", this, true); + showCompetitorShortNameColumn = new BooleanSetting("showCompetitorShortNameColumn", this, true); showCompetitorFullNameColumn = new BooleanSetting("showCompetitorFullNameColumn", this, true); + showCompetitorBoatInfoColumn = new BooleanSetting("showCompetitorBoatInfoColumn", this, false); } public LeaderboardSettings() { @@ -72,20 +74,22 @@ public abstract class LeaderboardSettings extends AbstractGenericSerializableSet */ public LeaderboardSettings(Collection maneuverDetailsToShow, Collection legDetailsToShow, Collection raceDetailsToShow, Collection overallDetailsToShow, - Long delayBetweenAutoAdvancesInMilliseconds, boolean showAddedScores, boolean showCompetitorSailIdColumn, - boolean showCompetitorFullNameColumn, boolean isCompetitorNationalityColumnVisible) { + Long delayBetweenAutoAdvancesInMilliseconds, + boolean showAddedScores, boolean showCompetitorShortNameColumn, + boolean showCompetitorFullNameColumn, boolean showCompetitorBoatInfoColumn, + boolean isCompetitorNationalityColumnVisible) { this.legDetailsToShow.setValues(legDetailsToShow); this.raceDetailsToShow.setValues(raceDetailsToShow); this.overallDetailsToShow.setValues(overallDetailsToShow); this.delayBetweenAutoAdvancesInMilliseconds.setValue(delayBetweenAutoAdvancesInMilliseconds); this.maneuverDetailsToShow.setValues(maneuverDetailsToShow); this.showAddedScores.setValue(showAddedScores); - this.showCompetitorSailIdColumn.setValue(showCompetitorSailIdColumn); + this.showCompetitorShortNameColumn.setValue(showCompetitorShortNameColumn); this.showCompetitorFullNameColumn.setValue(showCompetitorFullNameColumn); + this.showCompetitorBoatInfoColumn.setValue(showCompetitorBoatInfoColumn); this.isShowCompetitorNationality.setValue(isCompetitorNationalityColumnVisible); } - /** * A live collection that reflects the current state of the settings of a leaderboard panel */ @@ -126,14 +130,18 @@ public abstract class LeaderboardSettings extends AbstractGenericSerializableSet return showAddedScores.getValue(); } - public boolean isShowCompetitorSailIdColumn() { - return showCompetitorSailIdColumn.getValue(); + public boolean isShowCompetitorShortNameColumn() { + return showCompetitorShortNameColumn.getValue(); } public boolean isShowCompetitorFullNameColumn() { return showCompetitorFullNameColumn.getValue(); } + public boolean isShowCompetitorBoatInfoColumn() { + return showCompetitorBoatInfoColumn.getValue(); + } + public boolean isShowCompetitorNationality() { return isShowCompetitorNationality.getValue(); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/settings/client/leaderboard/LeaderboardSettingsDialogComponent.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/settings/client/leaderboard/LeaderboardSettingsDialogComponent.java index 13df6e63412..ec21d8158f6 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/settings/client/leaderboard/LeaderboardSettingsDialogComponent.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/settings/client/leaderboard/LeaderboardSettingsDialogComponent.java @@ -33,19 +33,22 @@ public abstract class LeaderboardSettingsDialogComponent overallDetailCheckboxes; protected final StringMessages stringMessages; protected LongBox refreshIntervalInSecondsBox; + protected final boolean canBoatInfoBeShown; protected RadioButton explicitRaceColumnSelectionRadioBtn; protected RadioButton lastNRacesColumnSelectionRadioBtn; protected IntegerBox numberOfLastRacesToShowBox; protected CheckBox showAddedScoresCheckBox; - protected CheckBox showCompetitorSailIdColumnheckBox; + protected CheckBox showCompetitorShortNameColumnCheckBox; protected CheckBox showCompetitorFullNameColumnCheckBox; + protected CheckBox showCompetitorBoatInfoColumnCheckBox; protected CheckBox isCompetitorNationalityColumnVisible; protected T initialSettings; - public LeaderboardSettingsDialogComponent(T initialSettings, StringMessages stringMessages) { + public LeaderboardSettingsDialogComponent(T initialSettings, StringMessages stringMessages, boolean canBoatInfoBeShown) { this.initialSettings = initialSettings; this.stringMessages = stringMessages; + this.canBoatInfoBeShown = canBoatInfoBeShown; maneuverDetailCheckboxes = new LinkedHashMap(); legDetailCheckboxes = new LinkedHashMap(); @@ -164,17 +167,21 @@ public abstract class LeaderboardSettingsDialogComponent namesOfRaceColumnsToShow = settingsWithRaceSelection.getNamesOfRaceColumnsToShow(); Integer numberOfLastRacesToShow = settingsWithRaceSelection.getNumberOfLastRacesToShow(); - boolean showCompetitorSailIdColumn = settingsWithRaceSelection.isShowCompetitorSailIdColumn(); - boolean showCompetitorFullNameColumns = settingsWithRaceSelection.isShowCompetitorFullNameColumn(); + boolean showCompetitorShortNameColumn = settingsWithRaceSelection.isShowCompetitorShortNameColumn(); + boolean showCompetitorFullNameColumn = settingsWithRaceSelection.isShowCompetitorFullNameColumn(); + boolean showCompetitorBoatInfoColumn = settingsWithRaceSelection.isShowCompetitorBoatInfoColumn(); return new MultiRaceLeaderboardSettings(maneuverDetails, legDetails, raceDetails, overallDetailsToShow, namesOfRaceColumnsToShow, numberOfLastRacesToShow, refreshIntervalInMs, strategy, /*showAddedScores*/ false, - showCompetitorSailIdColumn, showCompetitorFullNameColumns, + showCompetitorShortNameColumn, showCompetitorFullNameColumn, showCompetitorBoatInfoColumn, settingsWithRaceSelection.isShowCompetitorNationality()); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/settings/client/leaderboard/MultiRaceLeaderboardPanelLifecycle.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/settings/client/leaderboard/MultiRaceLeaderboardPanelLifecycle.java index 6b873575834..61321075592 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/settings/client/leaderboard/MultiRaceLeaderboardPanelLifecycle.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/settings/client/leaderboard/MultiRaceLeaderboardPanelLifecycle.java @@ -26,8 +26,9 @@ public class MultiRaceLeaderboardPanelLifecycle extends LeaderboardPanelLifecycl currentLeaderboardSettings.getDelayBetweenAutoAdvancesInMilliseconds(), currentLeaderboardSettings.getActiveRaceColumnSelectionStrategy(), currentLeaderboardSettings.isShowAddedScores(), - currentLeaderboardSettings.isShowCompetitorSailIdColumn(), + currentLeaderboardSettings.isShowCompetitorShortNameColumn(), currentLeaderboardSettings.isShowCompetitorFullNameColumn(), + currentLeaderboardSettings.isShowCompetitorBoatInfoColumn(), currentLeaderboardSettings.isShowCompetitorNationality()); return SettingsUtil.copyValues(globalLeaderboardSettings, defaultLeaderboardSettings); } @@ -43,15 +44,16 @@ public class MultiRaceLeaderboardPanelLifecycle extends LeaderboardPanelLifecycl currentLeaderboardSettings.getDelayBetweenAutoAdvancesInMilliseconds(), currentLeaderboardSettings.getActiveRaceColumnSelectionStrategy(), currentLeaderboardSettings.isShowAddedScores(), - currentLeaderboardSettings.isShowCompetitorSailIdColumn(), + currentLeaderboardSettings.isShowCompetitorShortNameColumn(), currentLeaderboardSettings.isShowCompetitorFullNameColumn(), + currentLeaderboardSettings.isShowCompetitorBoatInfoColumn(), currentLeaderboardSettings.isShowCompetitorNationality()); return SettingsUtil.copyValues(contextSpecificLeaderboardSettings, defaultLeaderboardSettings); } @Override public MultiRaceLeaderboardSettingsDialogComponent getSettingsDialogComponent(MultiRaceLeaderboardSettings settings) { - return new MultiRaceLeaderboardSettingsDialogComponent(settings, namesOfRaceColumns, stringMessages); + return new MultiRaceLeaderboardSettingsDialogComponent(settings, namesOfRaceColumns, stringMessages, false); } @Override diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/settings/client/leaderboard/MultiRaceLeaderboardSettings.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/settings/client/leaderboard/MultiRaceLeaderboardSettings.java index c9d40bbadcd..e166d36801b 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/settings/client/leaderboard/MultiRaceLeaderboardSettings.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/settings/client/leaderboard/MultiRaceLeaderboardSettings.java @@ -37,13 +37,11 @@ public class MultiRaceLeaderboardSettings extends LeaderboardSettings { Integer numberOfLastRacesToShow, Long delayBetweenAutoAdvancesInMilliseconds, RaceColumnSelectionStrategies activeRaceColumnSelectionStrategy, - boolean showAddedScores, - boolean showCompetitorSailIdColumn, boolean showCompetitorFullNameColumn, - boolean isCompetitorNationalityColumnVisible) { + boolean showAddedScores, boolean showCompetitorShortNameColumn, boolean showCompetitorFullNameColumn, + boolean showCompetitorBoatInfoColumn, boolean isCompetitorNationalityColumnVisible) { super(maneuverDetailsToShow, legDetailsToShow, raceDetailsToShow, overallDetailsToShow, delayBetweenAutoAdvancesInMilliseconds, - showAddedScores, showCompetitorSailIdColumn, - showCompetitorFullNameColumn, isCompetitorNationalityColumnVisible); - + showAddedScores, showCompetitorShortNameColumn, + showCompetitorFullNameColumn, showCompetitorBoatInfoColumn, isCompetitorNationalityColumnVisible); this.namesOfRaceColumnsToShow.setValues(namesOfRaceColumnsToShow); this.numberOfLastRacesToShow.setValue(numberOfLastRacesToShow); this.activeRaceColumnSelectionStrategy.setValue(activeRaceColumnSelectionStrategy); @@ -63,7 +61,7 @@ public class MultiRaceLeaderboardSettings extends LeaderboardSettings { newSettings.delayBetweenAutoAdvancesInMilliseconds.setValue(this.getDelayBetweenAutoAdvancesInMilliseconds()); newSettings.maneuverDetailsToShow.setValues(this.getManeuverDetailsToShow()); newSettings.showAddedScores.setValue(this.isShowAddedScores()); - newSettings.showCompetitorSailIdColumn.setValue(this.isShowCompetitorSailIdColumn()); + newSettings.showCompetitorShortNameColumn.setValue(this.isShowCompetitorShortNameColumn()); newSettings.showCompetitorFullNameColumn.setValue(this.isShowCompetitorFullNameColumn()); newSettings.isShowCompetitorNationality.setValue(this.isShowCompetitorNationality()); newSettings.namesOfRaceColumnsToShow.setValues(this.getNamesOfRaceColumnsToShow()); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/settings/client/leaderboard/MultiRaceLeaderboardSettingsDialogComponent.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/settings/client/leaderboard/MultiRaceLeaderboardSettingsDialogComponent.java index 8d9ab84dab0..83677ba429a 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/settings/client/leaderboard/MultiRaceLeaderboardSettingsDialogComponent.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/settings/client/leaderboard/MultiRaceLeaderboardSettingsDialogComponent.java @@ -29,8 +29,8 @@ public class MultiRaceLeaderboardSettingsDialogComponent private RaceColumnSelectionStrategies activeRaceColumnSelectionStrategy; public MultiRaceLeaderboardSettingsDialogComponent(MultiRaceLeaderboardSettings initialSettings, - List allRaceColumnNames, StringMessages stringMessages) { - super(initialSettings, stringMessages); + List allRaceColumnNames, StringMessages stringMessages, boolean canBoatInfoBeShown) { + super(initialSettings, stringMessages, canBoatInfoBeShown); this.activeRaceColumnSelectionStrategy = initialSettings.getActiveRaceColumnSelectionStrategy(); this.raceAllRaceColumnNames = allRaceColumnNames; raceColumnCheckboxes = new LinkedHashMap<>(); @@ -61,8 +61,8 @@ public class MultiRaceLeaderboardSettingsDialogComponent 1000l * (delayBetweenAutoAdvancesValue == null ? 0l : delayBetweenAutoAdvancesValue.longValue()), activeRaceColumnSelectionStrategy, /* showAddedScores */ showAddedScoresCheckBox.getValue().booleanValue(), - showCompetitorSailIdColumnheckBox.getValue(), showCompetitorFullNameColumnCheckBox.getValue(), - isCompetitorNationalityColumnVisible.getValue()); + showCompetitorShortNameColumnCheckBox.getValue(), showCompetitorFullNameColumnCheckBox.getValue(), + showCompetitorBoatInfoColumnCheckBox.getValue(), isCompetitorNationalityColumnVisible.getValue()); return newSettings; } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/settings/client/leaderboard/OverallLeaderboardSettingsDialogComponent.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/settings/client/leaderboard/OverallLeaderboardSettingsDialogComponent.java index e4fa940e63f..f78aca99405 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/settings/client/leaderboard/OverallLeaderboardSettingsDialogComponent.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/settings/client/leaderboard/OverallLeaderboardSettingsDialogComponent.java @@ -9,7 +9,7 @@ import com.sap.sse.gwt.client.dialog.DataEntryDialog; public class OverallLeaderboardSettingsDialogComponent extends MultiRaceLeaderboardSettingsDialogComponent { public OverallLeaderboardSettingsDialogComponent(MultiRaceLeaderboardSettings initialSettings, List allRaceColumnNames, StringMessages stringMessages) { - super(initialSettings, allRaceColumnNames, stringMessages); + super(initialSettings, allRaceColumnNames, stringMessages, false); } @Override diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/settings/client/leaderboard/SingleRaceLeaderboardSettings.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/settings/client/leaderboard/SingleRaceLeaderboardSettings.java index 8aabee50d17..edf6c10639a 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/settings/client/leaderboard/SingleRaceLeaderboardSettings.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/settings/client/leaderboard/SingleRaceLeaderboardSettings.java @@ -17,11 +17,11 @@ public class SingleRaceLeaderboardSettings extends LeaderboardSettings { public SingleRaceLeaderboardSettings(Collection maneuverDetailsToShow, Collection legDetailsToShow, Collection raceDetailsToShow, Collection overallDetailsToShow, Long delayBetweenAutoAdvancesInMilliseconds, - boolean showAddedScores, boolean showCompetitorSailIdColumn, boolean showCompetitorFullNameColumn, - boolean isCompetitorNationalityColumnVisible, boolean showRaceRankColumn) { + boolean showAddedScores, boolean showCompetitorShortNameColumn, + boolean showCompetitorFullNameColumn, boolean showCompetitorBoatInfoColumn, boolean isCompetitorNationalityColumnVisible,boolean showRaceRankColumn) { super(maneuverDetailsToShow, legDetailsToShow, raceDetailsToShow, overallDetailsToShow, - delayBetweenAutoAdvancesInMilliseconds, showAddedScores, showCompetitorSailIdColumn, - showCompetitorFullNameColumn, isCompetitorNationalityColumnVisible); + delayBetweenAutoAdvancesInMilliseconds, showAddedScores, showCompetitorShortNameColumn, + showCompetitorFullNameColumn, showCompetitorBoatInfoColumn, isCompetitorNationalityColumnVisible); this.showRaceRankColumn.setValue(showRaceRankColumn); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/settings/client/leaderboard/SingleRaceLeaderboardSettingsDialogComponent.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/settings/client/leaderboard/SingleRaceLeaderboardSettingsDialogComponent.java index 06b15fc9583..f0ca921cd1f 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/settings/client/leaderboard/SingleRaceLeaderboardSettingsDialogComponent.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/settings/client/leaderboard/SingleRaceLeaderboardSettingsDialogComponent.java @@ -17,8 +17,7 @@ public class SingleRaceLeaderboardSettingsDialogComponent public SingleRaceLeaderboardSettingsDialogComponent(SingleRaceLeaderboardSettings initialSettings, StringMessages stringMessages) { - super(initialSettings, stringMessages); - + super(initialSettings, stringMessages, true /*canBoatInfoBeShown*/); } @Override @@ -33,8 +32,8 @@ public class SingleRaceLeaderboardSettingsDialogComponent legDetailsToShow, raceDetailsToShow, overallDetailsToShow, 1000l * (delayBetweenAutoAdvancesValue == null ? 0l : delayBetweenAutoAdvancesValue.longValue()), /* showAddedScores */ showAddedScoresCheckBox.getValue().booleanValue(), - showCompetitorSailIdColumnheckBox.getValue(), showCompetitorFullNameColumnCheckBox.getValue(), - isCompetitorNationalityColumnVisible.getValue(), showRaceRankColumn.getValue()); + showCompetitorShortNameColumnCheckBox.getValue(), showCompetitorFullNameColumnCheckBox.getValue(), + showCompetitorBoatInfoColumnCheckBox.getValue(), isCompetitorNationalityColumnVisible.getValue(), showRaceRankColumn.getValue()); return newSettings; } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AbstractCompetitorRegistrationDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AbstractCompetitorRegistrationDialog.java index 24d8a34563f..369f44679e8 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AbstractCompetitorRegistrationDialog.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AbstractCompetitorRegistrationDialog.java @@ -17,19 +17,22 @@ public abstract class AbstractCompetitorRegistrationDialog extends DataEntryDial protected final SailingServiceAsync sailingService; protected final StringMessages stringMessages; protected final String leaderboardName; + protected final boolean canBoatsOfCompetitorsChangePerRace; protected final CompetitorRegistrationsPanel competitorRegistrationsPanel; public AbstractCompetitorRegistrationDialog(SailingServiceAsync sailingService, StringMessages stringMessages, ErrorReporter errorReporter, boolean editable, com.sap.sse.gwt.client.dialog.DataEntryDialog.DialogCallback> callback, - String leaderboardName, String boatClass, Validator> validator) { - super(stringMessages.registerCompetitors(), /* messsage */null, stringMessages.save(), stringMessages.cancel(), + String leaderboardName, boolean canBoatsOfCompetitorsChangePerRace, String boatClass, String okButtonMessage, + Validator> validator) { + super(stringMessages.registerCompetitors(), /* messsage */null, okButtonMessage, stringMessages.cancel(), validator, callback); this.errorReporter = errorReporter; this.stringMessages = stringMessages; this.sailingService = sailingService; this.leaderboardName = leaderboardName; - this.competitorRegistrationsPanel = new CompetitorRegistrationsPanel(sailingService, stringMessages, errorReporter, editable, leaderboardName, boatClass, + this.canBoatsOfCompetitorsChangePerRace = canBoatsOfCompetitorsChangePerRace; + this.competitorRegistrationsPanel = new CompetitorRegistrationsPanel(sailingService, stringMessages, errorReporter, editable, leaderboardName, canBoatsOfCompetitorsChangePerRace, boatClass, ()->validateAndUpdate(), getRegisteredCompetitorsRetriever(), /* restrictPoolToLeaderboard */ false, getAdditionalWidgetsToInsertAboveCompetitorTables(stringMessages)); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AbstractCompetitorWithBoatDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AbstractCompetitorWithBoatDialog.java new file mode 100755 index 00000000000..dd2d250c6b2 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AbstractCompetitorWithBoatDialog.java @@ -0,0 +1,101 @@ +package com.sap.sailing.gwt.ui.adminconsole; + +import com.google.gwt.user.client.ui.FocusWidget; +import com.google.gwt.user.client.ui.SuggestBox; +import com.google.gwt.user.client.ui.TextBox; +import com.sap.sailing.domain.common.dto.BoatDTO; +import com.sap.sailing.domain.common.dto.CompetitorDTO; +import com.sap.sailing.domain.common.dto.CompetitorDTOImpl; +import com.sap.sailing.domain.common.dto.CompetitorWithoutBoatDTO; +import com.sap.sailing.gwt.common.client.suggestion.BoatClassMasterdataSuggestOracle; +import com.sap.sailing.gwt.ui.client.StringMessages; + +/** + * An abstract base class for dialogs which can handle a competitor AND a boat together + * @author Frank Mittag + * + */ +public abstract class AbstractCompetitorWithBoatDialog extends CompetitorEditDialog { + protected final SuggestBox boatClassNameTextBox; + protected final TextBox sailIdTextBox; + protected final TextBox boatNameTextBox; + protected final TextBox boatDisplayColorTextBox; + + protected static class CompetitorWithBoatValidator extends CompetitorWithoutBoatValidator { + protected final StringMessages stringMessages; + + public CompetitorWithBoatValidator(StringMessages stringMessages) { + super(stringMessages); + + this.stringMessages = stringMessages; + } + + @Override + public String getErrorMessage(CompetitorDTO competitorToValidate) { + String result = super.getErrorMessage(competitorToValidate); + if (result == null) { + BoatDTO boatToValidate = competitorToValidate.getBoat(); + boolean invalidSailId = boatToValidate.getSailId() == null || boatToValidate.getSailId().isEmpty(); + boolean invalidName = boatToValidate.getName() == null || boatToValidate.getName().isEmpty(); + if (invalidSailId && invalidName) { + result = stringMessages.pleaseEnterASailNumberOrABoatName(); + } else if (boatToValidate.getColor() != null && boatToValidate.getColor() instanceof InvalidColor) { + result = boatToValidate.getColor().getAsHtml(); + } else if (boatToValidate.getBoatClass().getName() == null || boatToValidate.getBoatClass().getName().isEmpty()) { + result = stringMessages.pleaseEnterABoatClass(); + } + } + return result; + } + } + + /** + * The class creates the UI-dialog to type in the Data about a competitor. + * + * @param competitorToEdit + * The 'competitorToEdit' parameter contains the competitor which should be changed or initialized. + * @param boatClass + * The boat class is the default shown boat class for new boats. Set null if your boat is + * already initialized or you don't want a default boat class. + */ + public AbstractCompetitorWithBoatDialog(String dialogTitle, StringMessages stringMessages, CompetitorDTO competitorToEdit, + DialogCallback callback, String boatClass) { + super(dialogTitle, stringMessages, competitorToEdit, new AbstractCompetitorWithBoatDialog.CompetitorWithBoatValidator(stringMessages), callback); + + final BoatDTO boatToEdit = competitorToEdit.getBoat(); + this.boatClassNameTextBox = createSuggestBox(new BoatClassMasterdataSuggestOracle()); + boatClassNameTextBox.ensureDebugId("BoatClassNameSuggestBox"); + if (boatToEdit.getBoatClass() != null) { + boatClassNameTextBox.setValue(boatToEdit.getBoatClass().getName()); + boatClassNameTextBox.setEnabled(false); + } else { + boatClassNameTextBox.setValue(boatClass); // widgets have to accept null values here + } + this.boatNameTextBox = createTextBox(boatToEdit.getName()); + boatNameTextBox.ensureDebugId("BoatNameTextBox"); + this.boatDisplayColorTextBox = createTextBox(boatToEdit.getColor() == null ? "" : boatToEdit.getColor().getAsHtml()); + this.sailIdTextBox = createTextBox(boatToEdit.getSailId()); + sailIdTextBox.ensureDebugId("SailIdTextBox"); + } + + @Override + protected FocusWidget getInitialFocusWidget() { + return super.getInitialFocusWidget(); + } + + protected abstract BoatDTO getBoat(); + + @Override + protected CompetitorDTO getResult() { + CompetitorWithoutBoatDTO c = super.getResult(); + BoatDTO boat = getBoat(); + CompetitorDTO result = new CompetitorDTOImpl(c.getName(), c.getShortName(), c.getColor(), c.getEmail(), + c.getTwoLetterIsoCountryCode(), c.getThreeLetterIocCountryCode(), c.getCountryName(), + getCompetitorToEdit().getIdAsString(), + c.getImageURL(), c.getFlagImageURL(), + c.getTimeOnTimeFactor(), c.getTimeOnDistanceAllowancePerNauticalMile(), c.getSearchTag(), + boat); + return result; + } + +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AbstractFileImportWidget.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AbstractFileImportWidget.java index ae2c6bf44a3..88a48717b39 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AbstractFileImportWidget.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AbstractFileImportWidget.java @@ -51,7 +51,6 @@ public abstract class AbstractFileImportWidget extends Composite { private final TrackFileImportDeviceIdentifierTableWrapper table; protected final SailingServiceAsync sailingService; private final ErrorReporter errorReporter; - private boolean shouldClearListOnNewUploadComplete = true; protected AbstractFileImportWidget(String formActionUrl, StringMessages stringMessages, TrackFileImportDeviceIdentifierTableWrapper table, SailingServiceAsync sailingService, @@ -88,9 +87,6 @@ public abstract class AbstractFileImportWidget extends Composite { @UiHandler("formPanelUi") void onFileImportComplete(SubmitCompleteEvent event) { - if (shouldClearListOnNewUploadComplete) { - table.getDataProvider().getList().clear(); - } SensorDataImportResponse importResponse = SensorDataImportResponse.parse(event.getResults()); if (importResponse == null) { Window.alert(StringMessages.INSTANCE.unexpectedErrorDuringFileImport()); @@ -126,8 +122,4 @@ public abstract class AbstractFileImportWidget extends Composite { } protected abstract void getImporterTypes(AsyncCallback> callback); - - protected void setShouldClearListOnNewUploadComplete(boolean shouldClearListOnNewUploadComplete) { - this.shouldClearListOnNewUploadComplete = shouldClearListOnNewUploadComplete; - } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AbstractLeaderboardConfigPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AbstractLeaderboardConfigPanel.java index c765bd61a9d..8a1047d9d48 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AbstractLeaderboardConfigPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AbstractLeaderboardConfigPanel.java @@ -269,7 +269,6 @@ public abstract class AbstractLeaderboardConfigPanel extends FormPanel implement return selectedRaceColumnAndFleetName.getA().getRaceIdentifier(selectedRaceColumnAndFleetName.getB()).equals(selectedRace.getRaceIdentifier()); } }; - trackedRaceListHandlerRegistration = refreshableTrackedRaceSelectionModel.addSelectionChangeHandler(trackedRaceListHandler); Button reloadAllRaceLogs = new Button(stringMessages.reloadAllRaceLogs()); reloadAllRaceLogs.ensureDebugId("ReloadAllRaceLogsButton"); @@ -501,6 +500,10 @@ public abstract class AbstractLeaderboardConfigPanel extends FormPanel implement return getSelectedLeaderboard() != null ? getSelectedLeaderboard().name : null; } + protected boolean canBoatsOfCompetitorsChangePerRace() { + return getSelectedLeaderboard() != null ? getSelectedLeaderboard().canBoatsOfCompetitorsChangePerRace: false; + } + protected abstract void leaderboardSelectionChanged(); @Override diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AbstractRegattaLogFixesAddMappingsDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AbstractRegattaLogFixesAddMappingsDialog.java index 6fb142f2a5d..79ab27783be 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AbstractRegattaLogFixesAddMappingsDialog.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AbstractRegattaLogFixesAddMappingsDialog.java @@ -2,9 +2,9 @@ package com.sap.sailing.gwt.ui.adminconsole; import java.util.ArrayList; import java.util.Collection; -import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.function.Consumer; import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.user.client.ui.CaptionPanel; @@ -12,8 +12,8 @@ import com.google.gwt.user.client.ui.HorizontalPanel; import com.google.gwt.user.client.ui.SimplePanel; import com.google.gwt.user.client.ui.VerticalPanel; import com.google.gwt.user.client.ui.Widget; -import com.google.gwt.view.client.SelectionChangeEvent; import com.google.gwt.view.client.SingleSelectionModel; +import com.sap.sailing.domain.common.dto.BoatDTO; import com.sap.sailing.domain.common.dto.CompetitorDTO; import com.sap.sailing.domain.common.racelog.tracking.MappableToDevice; import com.sap.sailing.gwt.ui.client.SailingServiceAsync; @@ -27,18 +27,19 @@ import com.sap.sse.gwt.client.celltable.RefreshableSingleSelectionModel; import com.sap.sse.gwt.client.dialog.DataEntryDialog; public class AbstractRegattaLogFixesAddMappingsDialog extends DataEntryDialog> { - private String leaderboardName; + + private final String leaderboardName; + private final StringMessages stringMessages; private final SimplePanel importWidgetHolder; protected final TrackFileImportDeviceIdentifierTableWrapper deviceIdTable; - protected final CompetitorTableWrapper> competitorTable; - protected final MarkTableWrapper> markTable; - private final StringMessages stringMessages; - - private final Map mappings = new HashMap<>(); + private final CompetitorTableWrapper> competitorTable; + private final MarkTableWrapper> markTable; + private final BoatTableWrapper> boatTable; private TrackFileImportDeviceIdentifierDTO deviceToSelect; private CompetitorDTO compToSelect; private MarkDTO markToSelect; + private BoatDTO boatToSelect; private boolean inInstableTransitionState = false; public AbstractRegattaLogFixesAddMappingsDialog(SailingServiceAsync sailingService, @@ -56,40 +57,28 @@ public class AbstractRegattaLogFixesAddMappingsDialog extends DataEntryDialog(sailingService, stringMessages, errorReporter, /* multiSelection */ - false, true); - markTable = new MarkTableWrapper>( - /* multiSelection */false, sailingService, stringMessages, errorReporter); - - competitorTable.getSelectionModel().addSelectionChangeHandler(new SelectionChangeEvent.Handler() { - @Override - public void onSelectionChange(SelectionChangeEvent event) { - mappedToSelectionChanged(competitorTable.getSelectionModel().getSelectedObject()); - } - }); - markTable.getSelectionModel().addSelectionChangeHandler(new SelectionChangeEvent.Handler() { - @Override - public void onSelectionChange(SelectionChangeEvent event) { - mappedToSelectionChanged(markTable.getSelectionModel().getSelectedObject()); - } - }); - this.leaderboardName = leaderboardName; + this.stringMessages = stringMessages; + importWidgetHolder = new SimplePanel(); + deviceIdTable = new TrackFileImportDeviceIdentifierTableWrapper(sailingService, stringMessages, errorReporter); + registerSelectionChangeHandler(deviceIdTable.getSelectionModel(), this::deviceSelectionChanged); + competitorTable = new CompetitorTableWrapper<>(sailingService, stringMessages, errorReporter, + /* multiSelection */ false, /* enable pager */ true, /* filterCompetitorWithBoat */ false, /* filterCompetitorsWithoutBoat */ false); + markTable = new MarkTableWrapper>( + /* multiSelection */ false, sailingService, stringMessages, errorReporter); + boatTable = new BoatTableWrapper>(sailingService, stringMessages, + errorReporter, /* multiSelection */ false, /* enable Pager */ true, /* allowActions */ false); + registerSelectionChangeHandler(competitorTable.getSelectionModel(), this::mappedToSelectionChanged); + registerSelectionChangeHandler(markTable.getSelectionModel(), this::mappedToSelectionChanged); + registerSelectionChangeHandler(boatTable.getSelectionModel(), this::mappedToSelectionChanged); + + getBoatRegistrations(sailingService, errorReporter); getCompetitorRegistrations(sailingService, errorReporter); getMarks(sailingService, errorReporter); } - void getMarks(SailingServiceAsync sailingService, final ErrorReporter errorReporter) { + private void getMarks(SailingServiceAsync sailingService, final ErrorReporter errorReporter) { sailingService.getMarksInRegattaLog(leaderboardName, new AsyncCallback>() { @Override public void onSuccess(Iterable result) { @@ -103,7 +92,21 @@ public class AbstractRegattaLogFixesAddMappingsDialog extends DataEntryDialog>() { + @Override + public void onSuccess(Collection result) { + boatTable.filterBoats(result); + } + + @Override + public void onFailure(Throwable caught) { + errorReporter.reportError("Could not load boats: " + caught.getMessage()); + } + }); + } + + private void getCompetitorRegistrations(SailingServiceAsync sailingService, final ErrorReporter errorReporter) { sailingService.getCompetitorRegistrationsForLeaderboard(leaderboardName, new AsyncCallback>() { @Override @@ -133,6 +136,7 @@ public class AbstractRegattaLogFixesAddMappingsDialog extends DataEntryDialog void registerSelectionChangeHandler(SingleSelectionModel selectionModel, Consumer callback) { + selectionModel.addSelectionChangeHandler(event -> callback.accept(selectionModel.getSelectedObject())); + } + private void mappedToSelectionChanged(MappableToDevice mappedTo) { if (!inInstableTransitionState) { - TrackFileImportDeviceIdentifierDTO device = deviceIdTable.getSelectionModel().getSelectedObject(); - if (device != null) { - mappings.put(device, mappedTo); - } + deviceIdTable.setMappedObjectForSelectedDevice(mappedTo); if (mappedTo instanceof CompetitorDTO) { - markToSelect = null; compToSelect = (CompetitorDTO) mappedTo; - } else { + boatToSelect = null; + markToSelect = null; + } else if (mappedTo instanceof BoatDTO) { compToSelect = null; + boatToSelect = (BoatDTO) mappedTo; + markToSelect = null; + } else if (mappedTo instanceof MarkDTO) { + compToSelect = null; + boatToSelect = null; markToSelect = (MarkDTO) mappedTo; } } @@ -167,12 +179,15 @@ public class AbstractRegattaLogFixesAddMappingsDialog extends DataEntryDialog getResult() { List result = new ArrayList<>(); - for (TrackFileImportDeviceIdentifierDTO device : mappings.keySet()) { - DeviceIdentifierDTO deviceIdDto = new DeviceIdentifierDTO("FILE", device.uuidAsString); - MappableToDevice mappedTo = mappings.get(device); - DeviceMappingDTO mapping = new DeviceMappingDTO(deviceIdDto, device.from, device.to, mappedTo, null); + for (Map.Entry deviceEntry : deviceIdTable.getMappings().entrySet()) { + final TrackFileImportDeviceIdentifierDTO device = deviceEntry.getKey(); + final DeviceIdentifierDTO deviceIdDto = new DeviceIdentifierDTO("FILE", device.uuidAsString); + final MappableToDevice mappedTo = deviceEntry.getValue(); + final DeviceMappingDTO mapping = new DeviceMappingDTO(deviceIdDto, device.from, device.to, mappedTo, null); result.add(mapping); } return result; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AbstractRegattaLogSensorDataAddMappingsDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AbstractRegattaLogSensorDataAddMappingsDialog.java index 3f6c27dea1a..dffbb8aa716 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AbstractRegattaLogSensorDataAddMappingsDialog.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AbstractRegattaLogSensorDataAddMappingsDialog.java @@ -3,6 +3,7 @@ package com.sap.sailing.gwt.ui.adminconsole; import java.util.ArrayList; import java.util.Collection; import java.util.List; +import java.util.Map; import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.user.client.ui.CaptionPanel; @@ -10,7 +11,8 @@ import com.google.gwt.user.client.ui.HorizontalPanel; import com.google.gwt.user.client.ui.SimplePanel; import com.google.gwt.user.client.ui.VerticalPanel; import com.google.gwt.user.client.ui.Widget; -import com.google.gwt.view.client.SelectionChangeEvent; +import com.google.gwt.view.client.SingleSelectionModel; +import com.sap.sailing.domain.common.dto.BoatDTO; import com.sap.sailing.domain.common.dto.CompetitorDTO; import com.sap.sailing.domain.common.racelog.tracking.MappableToDevice; import com.sap.sailing.gwt.ui.client.SailingServiceAsync; @@ -23,12 +25,19 @@ import com.sap.sse.gwt.client.celltable.RefreshableSingleSelectionModel; import com.sap.sse.gwt.client.dialog.DataEntryDialog; public abstract class AbstractRegattaLogSensorDataAddMappingsDialog extends DataEntryDialog> { + private final String leaderboardName; private final SimplePanel importWidgetHolder; protected final TrackFileImportDeviceIdentifierTableWrapper deviceIdTable; - protected final CompetitorTableWrapper> competitorTable; + private final BoatTableWrapper> boatTable; + private final CompetitorTableWrapper> competitorTable; private final StringMessages stringMessages; + private TrackFileImportDeviceIdentifierDTO deviceToSelect; + private CompetitorDTO compToSelect; + private BoatDTO boatToSelect; + private boolean inInstableTransitionState = false; + public AbstractRegattaLogSensorDataAddMappingsDialog(SailingServiceAsync sailingService, final ErrorReporter errorReporter, final StringMessages stringMessages, String leaderboardName, DialogCallback> callback) { @@ -45,31 +54,43 @@ public abstract class AbstractRegattaLogSensorDataAddMappingsDialog extends Data deviceIdTable.removeTrackNameColumn(); importWidgetHolder = new SimplePanel(); - deviceIdTable.getSelectionModel().addSelectionChangeHandler(new SelectionChangeEvent.Handler() { - @Override - public void onSelectionChange(SelectionChangeEvent event) { - CompetitorDTO mappedComp = deviceIdTable.getMappedCompetitorForCurrentSelection(); - if (mappedComp != null) { - competitorTable.getSelectionModel().setSelected(mappedComp, true); - } - } - }); + deviceIdTable.getSelectionModel().addSelectionChangeHandler( + event -> deviceSelectionChanged(deviceIdTable.getSelectionModel().getSelectedObject())); + + boatTable = new BoatTableWrapper>(sailingService, stringMessages, + errorReporter, /* multiSelection */ false, /* enable Pager */ true, /* allowActions */ false); competitorTable = new CompetitorTableWrapper<>(sailingService, stringMessages, errorReporter, - /* multiSelection */ false, /* enablePager */ true); - competitorTable.getSelectionModel().addSelectionChangeHandler(new SelectionChangeEvent.Handler() { - @Override - public void onSelectionChange(SelectionChangeEvent event) { - deviceIdTable.didSelectCompetitorForMapping(competitorTable.getSelectionModel().getSelectedObject()); - validateAndUpdate(); - } + /* multiSelection */ false, /* enablePager */ true, /* filterCompetitorWithBoat */ false, /* filterCompetitorsWithoutBoat */ false); + + boatTable.getSelectionModel().addSelectionChangeHandler(event -> { + this.mappedToSelectionChanged(boatTable.getSelectionModel().getSelectedObject()); + validateAndUpdate(); + }); + competitorTable.getSelectionModel().addSelectionChangeHandler(event -> { + this.mappedToSelectionChanged(competitorTable.getSelectionModel().getSelectedObject()); + validateAndUpdate(); }); this.leaderboardName = leaderboardName; - + getBoatRegistrations(sailingService, errorReporter); getCompetitorRegistrations(sailingService, errorReporter); } - void getCompetitorRegistrations(SailingServiceAsync sailingService, final ErrorReporter errorReporter) { + private void getBoatRegistrations(final SailingServiceAsync sailingService, final ErrorReporter errorReporter) { + sailingService.getBoatRegistrationsForLeaderboard(leaderboardName, new AsyncCallback>() { + @Override + public void onSuccess(Collection result) { + boatTable.filterBoats(result); + } + + @Override + public void onFailure(Throwable caught) { + errorReporter.reportError("Could not load boats: " + caught.getMessage()); + } + }); + } + + private void getCompetitorRegistrations(SailingServiceAsync sailingService, final ErrorReporter errorReporter) { sailingService.getCompetitorRegistrationsForLeaderboard(leaderboardName, new AsyncCallback>() { @Override @@ -90,11 +111,14 @@ public abstract class AbstractRegattaLogSensorDataAddMappingsDialog extends Data VerticalPanel leftPanel = new VerticalPanel(); VerticalPanel tablesPanel = new VerticalPanel(); CaptionPanel competitorsPanel = new CaptionPanel(stringMessages.competitor()); + CaptionPanel boatsPanel = new CaptionPanel(stringMessages.boat()); leftPanel.add(importWidgetHolder); leftPanel.add(deviceIdTable); panel.add(leftPanel); panel.add(tablesPanel); + tablesPanel.add(boatsPanel); tablesPanel.add(competitorsPanel); + boatsPanel.setContentWidget(boatTable.asWidget()); competitorsPanel.setContentWidget(competitorTable.asWidget()); return panel; } @@ -105,9 +129,10 @@ public abstract class AbstractRegattaLogSensorDataAddMappingsDialog extends Data protected Collection getResult() { List result = new ArrayList<>(); String dataType = getSelectedImporterType(); - for (TrackFileImportDeviceIdentifierDTO device : deviceIdTable.getMappings().keySet()) { - DeviceIdentifierDTO deviceIdDto = new DeviceIdentifierDTO("FILE", device.uuidAsString); - MappableToDevice mappedTo = deviceIdTable.getMappings().get(device); + for (Map.Entry deviceEntry : deviceIdTable.getMappings().entrySet()) { + final TrackFileImportDeviceIdentifierDTO device = deviceEntry.getKey(); + final DeviceIdentifierDTO deviceIdDto = new DeviceIdentifierDTO("FILE", device.uuidAsString); + final MappableToDevice mappedTo = deviceIdTable.getMappings().get(device); result.add(new TypedDeviceMappingDTO(deviceIdDto, device.from, device.to, mappedTo, null, dataType)); } return result; @@ -116,4 +141,66 @@ public abstract class AbstractRegattaLogSensorDataAddMappingsDialog extends Data protected void setImportWidget(Widget importWidget) { importWidgetHolder.setWidget(importWidget); } + + // TODO Refactor! + + private static void selectOrClear(SingleSelectionModel selectionModel, T object) { + if (object == null) { + selectionModel.clear(); + } else { + selectionModel.setSelected(object, true); + } + } + + /** + * Avoid programmatic deselections that re-trigger the selection listeners and lead to a loop. + */ + private void select() { + if (inInstableTransitionState) { + if (deviceIdTable.getSelectionModel().getSelectedObject() == deviceToSelect + && competitorTable.getSelectionModel().getSelectedObject() == compToSelect + && boatTable.getSelectionModel().getSelectedObject() == boatToSelect) { + inInstableTransitionState = false; + } + } else { + inInstableTransitionState = true; + selectOrClear(deviceIdTable.getSelectionModel(), deviceToSelect); + selectOrClear(competitorTable.getSelectionModel(), compToSelect); + selectOrClear(boatTable.getSelectionModel(), boatToSelect); + } + } + + private void mappedToSelectionChanged(MappableToDevice mappedTo) { + if (!inInstableTransitionState) { + deviceIdTable.setMappedObjectForSelectedDevice(mappedTo); + + if (mappedTo instanceof CompetitorDTO) { + compToSelect = (CompetitorDTO) mappedTo; + boatToSelect = null; + } else if (mappedTo instanceof BoatDTO) { + compToSelect = null; + boatToSelect = (BoatDTO) mappedTo; + } + } + select(); + validateAndUpdate(); + } + + private void deviceSelectionChanged(TrackFileImportDeviceIdentifierDTO deviceId) { + if (!inInstableTransitionState) { + deviceToSelect = deviceId; + compToSelect = null; + boatToSelect = null; + + if (deviceId != null) { + final MappableToDevice mappedTo = deviceIdTable.getMappedObjectForDeviceId(deviceId); + if (mappedTo instanceof CompetitorDTO) { + compToSelect = (CompetitorDTO) mappedTo; + } else if (mappedTo instanceof BoatDTO) { + boatToSelect = (BoatDTO) mappedTo; + } + } + } + select(); + } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AdminConsoleEntryPoint.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AdminConsoleEntryPoint.java index 0b58cf3b1da..2e1378601f7 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AdminConsoleEntryPoint.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AdminConsoleEntryPoint.java @@ -174,6 +174,15 @@ public class AdminConsoleEntryPoint extends AbstractSailingEntryPoint implements } }, getStringMessages().competitors(), Permission.MANAGE_ALL_COMPETITORS); + final BoatPanel boatPanel = new BoatPanel(sailingService, getStringMessages(), this); + boatPanel.ensureDebugId("BoatPanel"); + panel.addToTabPanel(racesTabPanel, new DefaultRefreshableAdminConsolePanel(boatPanel) { + @Override + public void refreshAfterBecomingVisible() { + getWidget().refreshBoatList(); + } + }, getStringMessages().boats(), Permission.MANAGE_ALL_BOATS); + RaceCourseManagementPanel raceCourseManagementPanel = new RaceCourseManagementPanel(sailingService, this, this, getStringMessages()); panel.addToTabPanel(racesTabPanel, new DefaultRefreshableAdminConsolePanel(raceCourseManagementPanel), getStringMessages().courseLayout(), Permission.MANAGE_COURSE_LAYOUT); regattasDisplayers.add(raceCourseManagementPanel); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AdminConsoleResources.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AdminConsoleResources.java index 5f30f30659f..38b73d4aea3 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AdminConsoleResources.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AdminConsoleResources.java @@ -31,6 +31,9 @@ interface AdminConsoleResources extends ClientBundle { @Source("com/sap/sailing/gwt/ui/client/images/users.png") ImageResource competitorsIcon(); + @Source("com/sap/sailing/gwt/ui/client/images/sailboat.png") + ImageResource sailboatIcon(); + @Source("com/sap/sailing/gwt/ui/client/images/blackdot.png") ImageResource blackdotIcon(); @@ -86,6 +89,9 @@ interface AdminConsoleResources extends ClientBundle { @Source("com/sap/sailing/gwt/ui/client/images/competitor_registrations.png") ImageResource competitorRegistrations(); + @Source("com/sap/sailing/gwt/ui/client/images/boat_registrations.png") + ImageResource boatRegistrations(); + @Source("com/sap/sailing/gwt/ui/client/images/define_course.png") ImageResource defineCourse(); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/BoatConfigImagesBarCell.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/BoatConfigImagesBarCell.java new file mode 100644 index 00000000000..94c88a4431f --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/BoatConfigImagesBarCell.java @@ -0,0 +1,33 @@ +package com.sap.sailing.gwt.ui.adminconsole; + +import java.util.Arrays; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.text.shared.SafeHtmlRenderer; +import com.sap.sailing.gwt.ui.client.StringMessages; +import com.sap.sailing.gwt.ui.client.shared.controls.ImagesBarCell; + +public class BoatConfigImagesBarCell extends ImagesBarCell { + static final String ACTION_EDIT = "ACTION_EDIT"; + static final String ACTION_REFRESH = "ACTION_REFRESH"; + private final StringMessages stringMessages; + private static AdminConsoleResources resources = GWT.create(AdminConsoleResources.class); + + public BoatConfigImagesBarCell(StringMessages stringMessages) { + super(); + this.stringMessages = stringMessages; + } + + public BoatConfigImagesBarCell(SafeHtmlRenderer renderer, StringMessages stringMessages) { + super(); + this.stringMessages = stringMessages; + } + + @Override + protected Iterable getImageSpecs() { + return Arrays.asList( + new ImageSpec(ACTION_EDIT, stringMessages.actionEdit(), makeImagePrototype(resources.editIcon())), + new ImageSpec(ACTION_REFRESH, stringMessages.reload(), makeImagePrototype(resources.reloadIcon())) + ); + } +} \ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/BoatEditDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/BoatEditDialog.java new file mode 100644 index 00000000000..4f3b37e6c74 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/BoatEditDialog.java @@ -0,0 +1,149 @@ +package com.sap.sailing.gwt.ui.adminconsole; + +import com.google.gwt.user.client.ui.FocusWidget; +import com.google.gwt.user.client.ui.Grid; +import com.google.gwt.user.client.ui.Label; +import com.google.gwt.user.client.ui.SuggestBox; +import com.google.gwt.user.client.ui.TextBox; +import com.google.gwt.user.client.ui.Widget; +import com.sap.sailing.domain.common.Distance; +import com.sap.sailing.domain.common.dto.BoatClassDTO; +import com.sap.sailing.domain.common.dto.BoatDTO; +import com.sap.sailing.gwt.common.client.suggestion.BoatClassMasterdataSuggestOracle; +import com.sap.sailing.gwt.ui.client.StringMessages; +import com.sap.sse.common.Color; +import com.sap.sse.common.impl.RGBColor; +import com.sap.sse.gwt.client.dialog.DataEntryDialog; + +/** + * @author Frank Mittag (c516374) + * + */ +public class BoatEditDialog extends DataEntryDialog { + private final BoatDTO boatToEdit; + private final TextBox nameTextBox; + private final SuggestBox boatClassNameBox; + private final TextBox displayColorTextBox; + private final TextBox sailIdTextBox; + private final StringMessages stringMessages; + + public BoatEditDialog(final StringMessages stringMessages, BoatDTO boatToEdit, DialogCallback callback) { + this(stringMessages, boatToEdit, null, callback); + } + + /** + * The class creates the UI-dialog to edit the data of a boat. + * + * @param boatToEdit + * The 'boatToEdit' parameter contains the boat which should be changed or initialized. + */ + public BoatEditDialog(final StringMessages stringMessages, BoatDTO boatToEdit, String boatClassName, + DialogCallback callback) { + super(stringMessages.edit() + " " + stringMessages.boat(), null, stringMessages.ok(), stringMessages + .cancel(), new Validator() { + @Override + public String getErrorMessage(BoatDTO valueToValidate) { + String result = null; + boolean invalidSailId = valueToValidate.getSailId() == null || valueToValidate.getSailId().isEmpty(); + boolean invalidName = valueToValidate.getName() == null || valueToValidate.getName().isEmpty(); + if (invalidSailId && invalidName) { + result = stringMessages.pleaseEnterASailNumberOrABoatName(); + } else if (valueToValidate.getColor() != null && valueToValidate.getColor() instanceof InvalidColor) { + result = valueToValidate.getColor().getAsHtml(); + } else if (valueToValidate.getBoatClass().getName() == null || valueToValidate.getBoatClass().getName().isEmpty()) { + result = stringMessages.pleaseEnterABoatClass(); + } + return result; + } + }, /* animationEnabled */true, callback); + this.ensureDebugId("BoatEditDialog"); + this.stringMessages = stringMessages; + this.boatToEdit = boatToEdit; + + this.boatClassNameBox = createSuggestBox(new BoatClassMasterdataSuggestOracle()); + boatClassNameBox.ensureDebugId("BoatClassNameSuggestBox"); + if (boatToEdit.getBoatClass() != null) { + boatClassNameBox.setValue(boatToEdit.getBoatClass().getName()); + boatClassNameBox.setEnabled(false); + } else { + boatClassNameBox.setValue(boatClassName); // widgets have to accept null values here + } + this.nameTextBox = createTextBox(boatToEdit.getName()); + nameTextBox.ensureDebugId("NameTextBox"); + this.displayColorTextBox = createTextBox(boatToEdit.getColor() == null ? "" : boatToEdit.getColor().getAsHtml()); + this.sailIdTextBox = createTextBox(boatToEdit.getSailId()); + sailIdTextBox.ensureDebugId("SailIdTextBox"); + } + + @Override + protected FocusWidget getInitialFocusWidget() { + return nameTextBox; + } + + /** + * Encodes an invalid color; can be used + * @author Axel Uhl (D043530) + * + */ + private class InvalidColor implements Color { + private static final long serialVersionUID = 4012986110898149543L; + private final Exception exception; + + protected InvalidColor(Exception exception) { + this.exception = exception; + } + + @Override + public com.sap.sse.common.Util.Triple getAsRGB() { + return null; + } + + @Override + public com.sap.sse.common.Util.Triple getAsHSV() { + return null; + } + + @Override + public String getAsHtml() { + return stringMessages.invalidColor(exception.getMessage()); + } + + @Override + public Color invert() { + return null; + } + + } + + @Override + protected BoatDTO getResult() { + Color color; + if (displayColorTextBox.getValue() == null || displayColorTextBox.getValue().isEmpty()) { + color = null; + } else { + try { + color = new RGBColor(displayColorTextBox.getText()); + } catch (IllegalArgumentException iae) { + color = new InvalidColor(iae); + } + } + BoatClassDTO boatClass = new BoatClassDTO(boatClassNameBox.getValue(), Distance.NULL, Distance.NULL); + BoatDTO boat = new BoatDTO(boatToEdit.getIdAsString(), nameTextBox.getValue(), boatClass, sailIdTextBox.getValue(), color); + return boat; + } + + @Override + protected Widget getAdditionalWidget() { + Grid result = new Grid(4, 2); + result.setWidget(0, 0, new Label(stringMessages.name())); + result.setWidget(0, 1, nameTextBox); + result.setWidget(1, 0, new Label(stringMessages.sailNumber())); + result.setWidget(1, 1, sailIdTextBox); + result.setWidget(2, 0, new Label(stringMessages.color())); + result.setWidget(2, 1, displayColorTextBox); + result.setWidget(3, 0, new Label(stringMessages.boatClass())); + result.setWidget(3, 1, boatClassNameBox); + return result; + } + +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/BoatPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/BoatPanel.java new file mode 100644 index 00000000000..d576992b04e --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/BoatPanel.java @@ -0,0 +1,96 @@ +package com.sap.sailing.gwt.ui.adminconsole; + +import com.google.gwt.event.dom.client.ClickEvent; +import com.google.gwt.event.dom.client.ClickHandler; +import com.google.gwt.user.client.ui.Button; +import com.google.gwt.user.client.ui.HorizontalPanel; +import com.google.gwt.user.client.ui.SimplePanel; +import com.google.gwt.user.client.ui.VerticalPanel; +import com.google.gwt.view.client.SelectionChangeEvent; +import com.google.gwt.view.client.SelectionChangeEvent.Handler; +import com.sap.sailing.domain.common.dto.BoatDTO; +import com.sap.sailing.gwt.ui.client.SailingServiceAsync; +import com.sap.sailing.gwt.ui.client.StringMessages; +import com.sap.sse.gwt.client.ErrorReporter; +import com.sap.sse.gwt.client.celltable.RefreshableMultiSelectionModel; + +/** + * Allows an administrator to view and edit the set of boats currently maintained by the server. + * + * @author Frank Mittag (c5163874) + * + */ +public class BoatPanel extends SimplePanel { + private final BoatTableWrapper> boatTable; + private final RefreshableMultiSelectionModel refreshableBoatSelectionModel; + + public BoatPanel(final SailingServiceAsync sailingService, final StringMessages stringMessages, final ErrorReporter errorReporter) { + super(); + this.boatTable = new BoatTableWrapper<>(sailingService, stringMessages, errorReporter, /* multiSelection */ true, /* enablePager */ true, 100, true); + this.refreshableBoatSelectionModel = (RefreshableMultiSelectionModel) boatTable.getSelectionModel(); + VerticalPanel mainPanel = new VerticalPanel(); + this.setWidget(mainPanel); + mainPanel.setWidth("100%"); + HorizontalPanel boatsPanel = new HorizontalPanel(); + boatsPanel.setSpacing(5); + mainPanel.add(boatsPanel); + HorizontalPanel buttonPanel = new HorizontalPanel(); + buttonPanel.setSpacing(5); + Button refreshButton = new Button(stringMessages.refresh()); + refreshButton.addClickHandler(new ClickHandler() { + @Override + public void onClick(ClickEvent event) { + refreshBoatList(); + } + }); + refreshButton.ensureDebugId("RefreshButton"); + buttonPanel.add(refreshButton); + final Button allowReloadButton = new Button(stringMessages.allowReload()); + allowReloadButton.addClickHandler(new ClickHandler() { + @Override + public void onClick(ClickEvent event) { + boatTable.allowUpdate(refreshableBoatSelectionModel.getSelectedSet()); + } + }); + buttonPanel.add(allowReloadButton); + Button addBoatButton = new Button(stringMessages.add()); + addBoatButton.ensureDebugId("AddBoatButton"); + addBoatButton.addClickHandler(new ClickHandler() { + @Override + public void onClick(ClickEvent event) { + openAddBoatDialog(); + } + }); + buttonPanel.add(addBoatButton); + + Button selectAllButton = new Button(stringMessages.selectAll()); + selectAllButton.addClickHandler(new ClickHandler() { + @Override + public void onClick(ClickEvent event) { + for (BoatDTO b : boatTable.getDataProvider().getList()) { + refreshableBoatSelectionModel.setSelected(b, true); + } + } + }); + buttonPanel.add(selectAllButton); + + boatsPanel.add(buttonPanel); + mainPanel.add(boatTable); + + refreshableBoatSelectionModel.addSelectionChangeHandler(new Handler() { + @Override + public void onSelectionChange(SelectionChangeEvent event) { + allowReloadButton.setEnabled(!refreshableBoatSelectionModel.getSelectedSet().isEmpty()); + } + }); + allowReloadButton.setEnabled(!refreshableBoatSelectionModel.getSelectedSet().isEmpty()); + } + + private void openAddBoatDialog() { + boatTable.openEditBoatDialog(new BoatDTO(), null); + } + + public void refreshBoatList() { + boatTable.refreshBoatList(/* loadOnlyStandaloneBoats */ false, /* callback */ null); + } +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/BoatRegistrationsPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/BoatRegistrationsPanel.java new file mode 100644 index 00000000000..227c4d70ed5 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/BoatRegistrationsPanel.java @@ -0,0 +1,233 @@ +package com.sap.sailing.gwt.ui.adminconsole; + +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.function.Consumer; + +import com.google.gwt.core.client.Callback; +import com.google.gwt.event.dom.client.ClickEvent; +import com.google.gwt.event.dom.client.ClickHandler; +import com.google.gwt.user.client.Window; +import com.google.gwt.user.client.rpc.AsyncCallback; +import com.google.gwt.user.client.ui.Button; +import com.google.gwt.user.client.ui.CaptionPanel; +import com.google.gwt.user.client.ui.CheckBox; +import com.google.gwt.user.client.ui.FlowPanel; +import com.google.gwt.user.client.ui.HasVerticalAlignment; +import com.google.gwt.user.client.ui.HorizontalPanel; +import com.google.gwt.user.client.ui.VerticalPanel; +import com.sap.sailing.domain.common.dto.BoatDTO; +import com.sap.sailing.gwt.ui.client.SailingServiceAsync; +import com.sap.sailing.gwt.ui.client.StringMessages; +import com.sap.sse.common.Util; +import com.sap.sse.gwt.client.ErrorReporter; +import com.sap.sse.gwt.client.celltable.RefreshableMultiSelectionModel; +import com.sap.sse.gwt.client.controls.busyindicator.BusyDisplay; +import com.sap.sse.gwt.client.controls.busyindicator.BusyIndicator; +import com.sap.sse.gwt.client.controls.busyindicator.SimpleBusyIndicator; + +/** + * Shows two boat tables next to each other; the table on the left is that of the "registered" boats, the table + * on the right is the "pool" of all boats with those already "registered" removed. Between the tables are buttons + * to move boats left and right, thereby assigning them to the "registered" set or moving them back to the pool. + * The {@link #getResult result} consists of all {@link BoatDTO boat} in the table of "registered" boats.

+ * + * @author Frank Mittag + * + */ +public class BoatRegistrationsPanel extends FlowPanel implements BusyDisplay { + private final BoatTableWrapper> allBoatsTable; + private final BoatTableWrapper> registeredBoatsTable; + private final ErrorReporter errorReporter; + private Button registerBtn; + private Button unregisterBtn; + private Button addBoatButton; + private CheckBox showOnlyBoatsOfLogCheckBox; + private final BusyIndicator busyIndicator; + private final Runnable validator; + private final Consumer>> registeredBoatsRetriever; + + /** + * @param boatClass + * The boatClass parameter describes the default shown boat class for new boats. The + * boatClass parameter is null, if you want to edit a boat or there is no + * boat class for the new boat. + * @param validator + * when this panel is used to modify the resulting competitor set, this callback's {@link Runnable#run} + * method is invoked if a non-{@code null} value is passed here. + * @param registeredBoatsRetriever + * although declared as a consumer, this is a provider of the set of competitors to be shown in the + * "registered" competitors table. Technically, it "consumes" a callback to which to pass the competitors + * retrieved as the "registered" ones. + * @param restrictPoolToLeaderboard + * whether the pool of "all" boats is to be restricted to those obtained from the leaderboard, or + * to all boats in the server's boat store + */ + protected BoatRegistrationsPanel(final SailingServiceAsync sailingService, + final StringMessages stringMessages, final ErrorReporter errorReporter, boolean editable, + String leaderboardName, boolean canBoatsOfCompetitorsChangePerRace, String boatClass, Runnable validator, + Consumer>> registeredBoatsRetriever, + boolean restrictPoolToLeaderboard) { + this.errorReporter = errorReporter; + this.validator = validator; + this.busyIndicator = new SimpleBusyIndicator(); + this.registeredBoatsRetriever = registeredBoatsRetriever; + final HorizontalPanel buttonPanel = new HorizontalPanel(); + addBoatButton = new Button(stringMessages.add(stringMessages.boat())); + addBoatButton.addClickHandler(new ClickHandler() { + @Override + public void onClick(ClickEvent event) { + BoatDTO newBoat = new BoatDTO(); + registeredBoatsTable.openEditBoatDialog(newBoat, boatClass); + } + }); + + final HorizontalPanel boatRegistrationPanel = new HorizontalPanel(); + final CaptionPanel allBoatsPanel = new CaptionPanel(stringMessages.boatPool()); + final CaptionPanel registeredBoatsPanel = new CaptionPanel(stringMessages.registeredBoats()); + allBoatsTable = new BoatTableWrapper<>(sailingService, stringMessages, errorReporter, /* multiSelection */ + true, /* enablePager */true, 20, false); + registeredBoatsTable = new BoatTableWrapper<>(sailingService, stringMessages, errorReporter, /* multiSelection */ + true, /* enablePager */false, 20, false); + allBoatsPanel.add(allBoatsTable); + registeredBoatsPanel.add(registeredBoatsTable); + VerticalPanel movePanel = new VerticalPanel(); + registerBtn = new Button("<"); + unregisterBtn = new Button(">"); + registerBtn.setEnabled(editable); + unregisterBtn.setEnabled(editable); + movePanel.add(registerBtn); + movePanel.add(unregisterBtn); + registerBtn.addClickHandler(new ClickHandler() { + @Override + public void onClick(ClickEvent event) { + moveSelected(allBoatsTable, registeredBoatsTable); + } + }); + unregisterBtn.addClickHandler(new ClickHandler() { + @Override + public void onClick(ClickEvent event) { + moveSelected(registeredBoatsTable, allBoatsTable); + } + }); + boatRegistrationPanel.add(registeredBoatsPanel); + boatRegistrationPanel.add(movePanel); + boatRegistrationPanel.setCellVerticalAlignment(movePanel, HasVerticalAlignment.ALIGN_MIDDLE); + boatRegistrationPanel.add(allBoatsPanel); + buttonPanel.add(addBoatButton); + buttonPanel.add(busyIndicator); + this.add(buttonPanel); + showOnlyBoatsOfLogCheckBox = new CheckBox(stringMessages.showOnlyBoatsOfLog()); + showOnlyBoatsOfLogCheckBox.setValue(false); + showOnlyBoatsOfLogCheckBox.addClickHandler(new ClickHandler() { + @Override + public void onClick(ClickEvent event) { + if (Window.confirm(stringMessages.confirmLosingBoatEditsWhenTogglingLogBasedView())) { + refreshBoats(); + } else { + showOnlyBoatsOfLogCheckBox.setValue(!showOnlyBoatsOfLogCheckBox.getValue()); + } + } + }); + this.add(showOnlyBoatsOfLogCheckBox); + this.add(boatRegistrationPanel); + refreshBoats(); + } + + @Override + public void setBusy(boolean isBusy) { + busyIndicator.setBusy(isBusy); + } + + protected void move(BoatTableWrapper from, BoatTableWrapper to, Iterable toMove) { + from.getFilterField().removeAll(toMove); + to.getFilterField().addAll(toMove); + } + + private void moveSelected(BoatTableWrapper> from, + BoatTableWrapper> to) { + move(from, to, from.getSelectionModel().getSelectedSet()); + validateAndUpdate(); + } + + protected boolean showOnlyBoatsOfLog(){ + return showOnlyBoatsOfLogCheckBox.getValue(); + } + + protected void refreshBoats() { + registeredBoatsTable.getFilterField().removeAll(); + allBoatsTable.getFilterField().removeAll(); + setRegisterableBoatsAndRegisteredBoats(); + } + + private void setRegisterableBoatsAndRegisteredBoats() { + allBoatsTable.refreshBoatList(true, new Callback, Throwable>() { + @Override + public void onSuccess(Iterable result) { + registeredBoatsRetriever.accept(new AsyncCallback>() { + @Override + public void onSuccess(Collection registeredBoats) { + moveFromPoolToRegistered(registeredBoats); + validateAndUpdate(); + } + + @Override + public void onFailure(Throwable reason) { + errorReporter.reportError("Could not load already registered boats: " + reason.getMessage()); + } + }); + } + + @Override + public void onFailure(Throwable reason) { + errorReporter.reportError("Could not load pool of boats: " + reason.getMessage()); + } + }); + } + + public void deactivateRegistrationButtons(String tooltip){ + registerBtn.setEnabled(false); + unregisterBtn.setEnabled(false); + addBoatButton.setEnabled(false); + registerBtn.setTitle(tooltip); + unregisterBtn.setTitle(tooltip); + addBoatButton.setTitle(tooltip); + validateAndUpdate(); + } + + private void validateAndUpdate() { + if (validator != null) { + validator.run(); + } + } + + public void activateRegistrationButtons(){ + registerBtn.setEnabled(true); + unregisterBtn.setEnabled(true); + addBoatButton.setEnabled(true); + registerBtn.setTitle(""); + unregisterBtn.setTitle(""); + addBoatButton.setTitle(""); + validateAndUpdate(); + } + + public Set getResult() { + final Set registeredBoats = new HashSet<>(); + Util.addAll(registeredBoatsTable.getAllBoats(), registeredBoats); + return registeredBoats; + } + + public void grayOutCompetitorsFromRegistered(List boats) { +// registeredBoatsTable.grayOutCompetitors(boats); + } + + public void grayOutCompetitorsFromPool(List competitors) { +// allBoatsTable.grayOutCompetitors(competitors); + } + + public void moveFromPoolToRegistered(Collection registeredBoats) { + move(allBoatsTable, registeredBoatsTable, registeredBoats); + } +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/BoatTableWrapper.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/BoatTableWrapper.java new file mode 100644 index 00000000000..808ebdc3e2a --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/BoatTableWrapper.java @@ -0,0 +1,289 @@ +package com.sap.sailing.gwt.ui.adminconsole; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +import com.google.gwt.cell.client.FieldUpdater; +import com.google.gwt.cell.client.SafeHtmlCell; +import com.google.gwt.core.client.Callback; +import com.google.gwt.safehtml.shared.SafeHtml; +import com.google.gwt.safehtml.shared.SafeHtmlBuilder; +import com.google.gwt.user.cellview.client.Column; +import com.google.gwt.user.cellview.client.ColumnSortEvent.ListHandler; +import com.google.gwt.user.cellview.client.TextColumn; +import com.google.gwt.user.client.Window; +import com.google.gwt.user.client.rpc.AsyncCallback; +import com.google.gwt.user.client.ui.Label; +import com.sap.sailing.domain.common.dto.BoatDTO; +import com.sap.sailing.gwt.ui.adminconsole.ColorColumn.ColorRetriever; +import com.sap.sailing.gwt.ui.client.SailingServiceAsync; +import com.sap.sailing.gwt.ui.client.StringMessages; +import com.sap.sse.common.Color; +import com.sap.sse.common.Util; +import com.sap.sse.common.util.NaturalComparator; +import com.sap.sse.gwt.client.ErrorReporter; +import com.sap.sse.gwt.client.celltable.EntityIdentityComparator; +import com.sap.sse.gwt.client.celltable.RefreshableSelectionModel; +import com.sap.sse.gwt.client.dialog.DataEntryDialog.DialogCallback; +import com.sap.sse.gwt.client.panels.LabeledAbstractFilterablePanel; + +public class BoatTableWrapper> extends TableWrapper { + private final LabeledAbstractFilterablePanel filterField; + + public BoatTableWrapper(SailingServiceAsync sailingService, StringMessages stringMessages, + ErrorReporter errorReporter, boolean multiSelection, boolean enablePager, boolean allowActions) { + this(sailingService, stringMessages, errorReporter, multiSelection, enablePager, DEFAULT_PAGING_SIZE, + allowActions); + } + + public BoatTableWrapper(SailingServiceAsync sailingService, StringMessages stringMessages, ErrorReporter errorReporter, + boolean multiSelection, boolean enablePager, int pagingSize, boolean allowActions) { + super(sailingService, stringMessages, errorReporter, multiSelection, enablePager, pagingSize, + new EntityIdentityComparator() { + @Override + public boolean representSameEntity(BoatDTO dto1, BoatDTO dto2) { + return dto1.getIdAsString().equals(dto2.getIdAsString()); + } + @Override + public int hashCode(BoatDTO t) { + return t.getIdAsString().hashCode(); + } + }); + ListHandler boatColumnListHandler = getColumnSortHandler(); + + // boats table + TextColumn boatNameColumn = new TextColumn() { + @Override + public String getValue(BoatDTO boat) { + return boat.getName(); + } + }; + boatNameColumn.setSortable(true); + boatColumnListHandler.setComparator(boatNameColumn, new Comparator() { + private final NaturalComparator comparator = new NaturalComparator(/* caseSensitive */ false); + @Override + public int compare(BoatDTO o1, BoatDTO o2) { + return comparator.compare(o1.getName(), o2.getName()); + } + }); + + TextColumn boatClassColumn = new TextColumn() { + @Override + public String getValue(BoatDTO competitor) { + return competitor.getBoatClass() != null ? competitor.getBoatClass().getName() : ""; + } + }; + boatClassColumn.setSortable(true); + boatColumnListHandler.setComparator(boatClassColumn, new Comparator() { + private final NaturalComparator comparator = new NaturalComparator(/* caseSensitive */ false); + @Override + public int compare(BoatDTO o1, BoatDTO o2) { + return comparator.compare(o1.getBoatClass().getName(), o2.getBoatClass().getName()); + } + }); + + Column sailIdColumn = new Column(new SafeHtmlCell()) { + @Override + public SafeHtml getValue(BoatDTO competitor) { + SafeHtmlBuilder sb = new SafeHtmlBuilder(); + sb.appendEscaped(competitor.getSailId()); + return sb.toSafeHtml(); + } + }; + sailIdColumn.setSortable(true); + boatColumnListHandler.setComparator(sailIdColumn, new Comparator() { + private final NaturalComparator comparator = new NaturalComparator(/* case sensitive */ false); + @Override + public int compare(BoatDTO o1, BoatDTO o2) { + return comparator.compare(o1.getSailId(), o2.getSailId()); + } + }); + + Column boatColorColumn = new ColorColumn<>(new ColorRetriever() { + @Override + public Color getColor(BoatDTO t) { + return t.getColor(); + } + }); + boatColorColumn.setSortable(true); + boatColumnListHandler.setComparator(boatColorColumn, new Comparator() { + @Override + public int compare(BoatDTO o1, BoatDTO o2) { + if (o1.getColor() == null) { + if (o2.getColor() == null) { + return 0; + } + return -1; + } else if (o2.getColor() == null) { + return 1; + } + return o1.getColor().getAsHtml().compareTo(o2.getColor().getAsHtml()); + } + }); + + TextColumn boatIdColumn = new TextColumn() { + @Override + public String getValue(BoatDTO boat) { + return boat.getIdAsString(); + } + }; + boatIdColumn.setSortable(true); + boatColumnListHandler.setComparator(boatIdColumn, new Comparator() { + @Override + public int compare(BoatDTO o1, BoatDTO o2) { + return new NaturalComparator(false).compare(o1.getIdAsString(), o2.getIdAsString()); + } + }); + + filterField = new LabeledAbstractFilterablePanel(new Label(stringMessages.filterBoats()), + new ArrayList(), table, dataProvider) { + @Override + public Iterable getSearchableStrings(BoatDTO boat) { + List string = new ArrayList(); + string.add(boat.getName()); + string.add(boat.getSailId()); + string.add(boat.getBoatClass().getName()); + string.add(boat.getIdAsString()); + return string; + } + }; + registerSelectionModelOnNewDataProvider(filterField.getAllListDataProvider()); + + // BoatTable edit features + ImagesBarColumn boatActionColumn = new ImagesBarColumn( + new BoatConfigImagesBarCell(stringMessages)); + boatActionColumn.setFieldUpdater(new FieldUpdater() { + @Override + public void update(int index, final BoatDTO boat, String value) { + if (BoatConfigImagesBarCell.ACTION_EDIT.equals(value)) { + openEditBoatDialog(boat, /* boatClassName */ null); + } else if (BoatConfigImagesBarCell.ACTION_REFRESH.equals(value)) { + allowUpdate(Collections.singleton(boat)); + } + } + + }); + + mainPanel.insert(filterField, 0); + table.addColumnSortHandler(boatColumnListHandler); + table.addColumn(boatNameColumn, stringMessages.name()); + table.addColumn(sailIdColumn, stringMessages.sailNumber()); + table.addColumn(boatClassColumn, stringMessages.boatClass()); + table.addColumn(boatColorColumn, stringMessages.color()); + table.addColumn(boatIdColumn, stringMessages.id()); + if (allowActions) { + table.addColumn(boatActionColumn, stringMessages.actions()); + } + table.ensureDebugId("BoatsTable"); + } + + public Iterable getAllBoats() { + return filterField.getAll(); + } + + public LabeledAbstractFilterablePanel getFilterField() { + return filterField; + } + + public void filterBoats(Iterable boats) { + getFilteredBoats(boats); + } + + public void refreshBoatList(boolean loadOnlyStandaloneBoats, final Callback, Throwable> callback) { + if (loadOnlyStandaloneBoats) { + sailingService.getStandaloneBoats(new AsyncCallback>() { + @Override + public void onFailure(Throwable caught) { + errorReporter.reportError("Remote Procedure Call getBoats() - Failure: " + caught.getMessage()); + if (callback != null) { + callback.onFailure(caught); + } + } + + @Override + public void onSuccess(Iterable result) { + getFilteredBoats(result); + filterBoats(result); + if (callback != null) { + callback.onSuccess(result); + } + } + }); + } else { + sailingService.getAllBoats(new AsyncCallback>() { + @Override + public void onFailure(Throwable caught) { + errorReporter.reportError("Remote Procedure Call getBoats() - Failure: " + caught.getMessage()); + if (callback != null) { + callback.onFailure(caught); + } + } + + @Override + public void onSuccess(Iterable result) { + getFilteredBoats(result); + filterBoats(result); + if (callback != null) { + callback.onSuccess(result); + } + } + }); + } + } + + private void getFilteredBoats(Iterable result) { + filterField.updateAll(result); + } + + void openEditBoatDialog(final BoatDTO originalBoat, String boatClassName) { + final BoatEditDialog dialog = new BoatEditDialog(stringMessages, originalBoat, boatClassName, new DialogCallback() { + @Override + public void ok(BoatDTO boat) { + sailingService.addOrUpdateBoat(boat, new AsyncCallback() { + @Override + public void onFailure(Throwable caught) { + errorReporter.reportError("Error trying to update boat: " + caught.getMessage()); + } + + @Override + public void onSuccess(BoatDTO updatedBoat) { + int editedBoatIndex = getFilterField().indexOf(originalBoat); + getFilterField().remove(originalBoat); + if (editedBoatIndex >= 0){ + getFilterField().add(editedBoatIndex, updatedBoat); + } else { + //in case boat was not present --> not edit, but create + getFilterField().add(updatedBoat); + } + getDataProvider().refresh(); + } + }); + } + + @Override + public void cancel() { + } + }); + dialog.ensureDebugId("BoatEditDialog"); + dialog.show(); + } + + public void allowUpdate(final Iterable boats) { + List serializableSingletonList = new ArrayList(); + Util.addAll(boats, serializableSingletonList); + sailingService.allowBoatResetToDefaults(serializableSingletonList, new AsyncCallback() { + @Override + public void onFailure(Throwable caught) { + errorReporter.reportError("Error trying to allow resetting boats " + boats + + " to defaults: " + caught.getMessage()); + } + + @Override + public void onSuccess(Void result) { + Window.alert(stringMessages.successfullyAllowedBoatReset(boats.toString())); + } + }); + } +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompactBoatTableWrapper.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompactBoatTableWrapper.java new file mode 100644 index 00000000000..de667accc06 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompactBoatTableWrapper.java @@ -0,0 +1,214 @@ +package com.sap.sailing.gwt.ui.adminconsole; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.Map; + +import com.google.gwt.cell.client.SafeHtmlCell; +import com.google.gwt.safehtml.shared.SafeHtml; +import com.google.gwt.safehtml.shared.SafeHtmlBuilder; +import com.google.gwt.user.cellview.client.Column; +import com.google.gwt.user.cellview.client.ColumnSortEvent.ListHandler; +import com.google.gwt.user.cellview.client.TextColumn; +import com.google.gwt.user.client.rpc.AsyncCallback; +import com.google.gwt.user.client.ui.Label; +import com.sap.sailing.domain.common.dto.BoatDTO; +import com.sap.sailing.domain.common.dto.CompetitorDTO; +import com.sap.sailing.gwt.ui.adminconsole.ColorColumn.ColorRetriever; +import com.sap.sailing.gwt.ui.client.SailingServiceAsync; +import com.sap.sailing.gwt.ui.client.StringMessages; +import com.sap.sse.common.Color; +import com.sap.sse.common.util.NaturalComparator; +import com.sap.sse.gwt.client.ErrorReporter; +import com.sap.sse.gwt.client.celltable.EntityIdentityComparator; +import com.sap.sse.gwt.client.celltable.RefreshableSelectionModel; +import com.sap.sse.gwt.client.panels.LabeledAbstractFilterablePanel; + +public class CompactBoatTableWrapper> extends TableWrapper { + private final LabeledAbstractFilterablePanel filterField; + + public CompactBoatTableWrapper(SailingServiceAsync sailingService, StringMessages stringMessages,ErrorReporter errorReporter, + boolean multiSelection, boolean enablePager) { + super(sailingService, stringMessages, errorReporter, multiSelection, enablePager, + new EntityIdentityComparator() { + @Override + public boolean representSameEntity(BoatDTO dto1, BoatDTO dto2) { + return dto1.getIdAsString().equals(dto2.getIdAsString()); + } + @Override + public int hashCode(BoatDTO t) { + return t.getIdAsString().hashCode(); + } + }); + ListHandler boatColumnListHandler = getColumnSortHandler(); + + // boats table + TextColumn boatNameColumn = new TextColumn() { + @Override + public String getValue(BoatDTO boat) { + return boat.getName(); + } + }; + boatNameColumn.setSortable(true); + boatColumnListHandler.setComparator(boatNameColumn, new Comparator() { + private final NaturalComparator comparator = new NaturalComparator(/* caseSensitive */ false); + @Override + public int compare(BoatDTO o1, BoatDTO o2) { + return comparator.compare(o1.getName(), o2.getName()); + } + }); + + TextColumn boatClassColumn = new TextColumn() { + @Override + public String getValue(BoatDTO competitor) { + return competitor.getBoatClass() != null ? competitor.getBoatClass().getName() : ""; + } + }; + boatClassColumn.setSortable(true); + boatColumnListHandler.setComparator(boatClassColumn, new Comparator() { + private final NaturalComparator comparator = new NaturalComparator(/* caseSensitive */ false); + @Override + public int compare(BoatDTO o1, BoatDTO o2) { + return comparator.compare(o1.getBoatClass().getName(), o2.getBoatClass().getName()); + } + }); + + Column sailIdColumn = new Column(new SafeHtmlCell()) { + @Override + public SafeHtml getValue(BoatDTO competitor) { + SafeHtmlBuilder sb = new SafeHtmlBuilder(); + sb.appendEscaped(competitor.getSailId()); + return sb.toSafeHtml(); + } + }; + sailIdColumn.setSortable(true); + boatColumnListHandler.setComparator(sailIdColumn, new Comparator() { + private final NaturalComparator comparator = new NaturalComparator(/* case sensitive */ false); + @Override + public int compare(BoatDTO o1, BoatDTO o2) { + return comparator.compare(o1.getSailId(), o2.getSailId()); + } + }); + + Column boatColorColumn = new ColorColumn<>(new ColorRetriever() { + @Override + public Color getColor(BoatDTO t) { + return t.getColor(); + } + }); + boatColorColumn.setSortable(true); + boatColumnListHandler.setComparator(boatColorColumn, new Comparator() { + @Override + public int compare(BoatDTO o1, BoatDTO o2) { + if (o1.getColor() == null) { + if (o2.getColor() == null) { + return 0; + } + return -1; + } else if (o2.getColor() == null) { + return 1; + } + return o1.getColor().getAsHtml().compareTo(o2.getColor().getAsHtml()); + } + }); + + Column boatIdColumn = new Column(new SafeHtmlCell()) { + @Override + public SafeHtml getValue(BoatDTO competitor) { + SafeHtmlBuilder sb = new SafeHtmlBuilder(); + sb.appendEscaped(competitor.getIdAsString()); + return sb.toSafeHtml(); + } + }; + boatIdColumn.setSortable(true); + boatColumnListHandler.setComparator(boatIdColumn, new Comparator() { + private final NaturalComparator comparator = new NaturalComparator(/* case sensitive */ false); + @Override + public int compare(BoatDTO o1, BoatDTO o2) { + return comparator.compare(o1.getIdAsString(), o2.getIdAsString()); + } + }); + + filterField = new LabeledAbstractFilterablePanel(new Label(stringMessages.filterBoats()), + new ArrayList(), table, dataProvider) { + @Override + public Iterable getSearchableStrings(BoatDTO boat) { + List string = new ArrayList(); + string.add(boat.getName()); + string.add(boat.getSailId()); + string.add(boat.getIdAsString()); + string.add(boat.getBoatClass().getName()); + return string; + } + }; + registerSelectionModelOnNewDataProvider(filterField.getAllListDataProvider()); + + mainPanel.insert(filterField, 0); + table.addColumnSortHandler(boatColumnListHandler); + table.addColumn(boatNameColumn, stringMessages.name()); + table.addColumn(sailIdColumn, stringMessages.sailNumber()); + table.addColumn(boatClassColumn, stringMessages.boatClass()); + table.addColumn(boatColorColumn, stringMessages.color()); + table.addColumn(boatIdColumn, stringMessages.id()); + table.ensureDebugId("BoatsTable"); + } + + public Iterable getAllBoats() { + return filterField.getAll(); + } + + public LabeledAbstractFilterablePanel getFilterField() { + return filterField; + } + + public void filterBoats(Iterable boats) { + getFilteredBoats(boats); + } + + public void selectBoat(BoatDTO boatToSelect) { + for (BoatDTO boat: getAllBoats()) { + if (boat.getIdAsString().equals(boatToSelect.getIdAsString())) { + getSelectionModel().setSelected(boat, true); + break; + } + } + } + + public void clearSelection() { + getSelectionModel().clear(); + } + + public void refreshBoatListFromRace(String leaderboardName, String raceColumnName, String fleetName) { + final AsyncCallback> myCallback = new AsyncCallback>() { + @Override + public void onFailure(Throwable caught) { + errorReporter.reportError("Remote Procedure Call refreshBoatListFromRace() - Failure: " + caught.getMessage()); + } + + @Override + public void onSuccess(Map result) { + filterBoats(result.values()); + } + }; + sailingService.getCompetitorsAndBoatsOfRace(leaderboardName, raceColumnName, fleetName, myCallback); + } + + public void refreshBoatList() { + sailingService.getAllBoats(new AsyncCallback>() { + @Override + public void onFailure(Throwable caught) { + errorReporter.reportError("Remote Procedure Call getAllBoats() - Failure: " + caught.getMessage()); + } + + @Override + public void onSuccess(Iterable result) { + filterBoats(result); + } + }); + } + + private void getFilteredBoats(Iterable result) { + filterField.updateAll(result); + } +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompactCompetitorConfigImagesBarCell.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompactCompetitorConfigImagesBarCell.java new file mode 100644 index 00000000000..fba7426d579 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompactCompetitorConfigImagesBarCell.java @@ -0,0 +1,28 @@ +package com.sap.sailing.gwt.ui.adminconsole; + +import java.util.ArrayList; +import java.util.List; + +import com.google.gwt.core.client.GWT; +import com.sap.sailing.gwt.ui.client.StringMessages; +import com.sap.sailing.gwt.ui.client.shared.controls.ImagesBarCell; + +public class CompactCompetitorConfigImagesBarCell extends ImagesBarCell { + public final static String ACTION_UNLINK = "ACTION_UNLINK"; + + private final StringMessages stringMessages; + private static AdminConsoleResources resources = GWT.create(AdminConsoleResources.class); + + public CompactCompetitorConfigImagesBarCell(StringMessages stringMessages) { + super(); + this.stringMessages = stringMessages; + } + + @Override + protected Iterable getImageSpecs() { + List result = new ArrayList(); + result.add(new ImageSpec(ACTION_UNLINK, stringMessages.actionBoatUnlink(), makeImagePrototype(resources.unlinkIcon()))); + + return result; + } +} \ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompactCompetitorTableWrapper.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompactCompetitorTableWrapper.java new file mode 100644 index 00000000000..3dbf9202a1a --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompactCompetitorTableWrapper.java @@ -0,0 +1,188 @@ +package com.sap.sailing.gwt.ui.adminconsole; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.Map; + +import com.google.gwt.cell.client.SafeHtmlCell; +import com.google.gwt.resources.client.ImageResource; +import com.google.gwt.safehtml.shared.SafeHtml; +import com.google.gwt.safehtml.shared.SafeHtmlBuilder; +import com.google.gwt.user.cellview.client.Column; +import com.google.gwt.user.cellview.client.ColumnSortEvent.ListHandler; +import com.google.gwt.user.cellview.client.TextColumn; +import com.google.gwt.user.client.rpc.AsyncCallback; +import com.google.gwt.user.client.ui.ImageResourceRenderer; +import com.google.gwt.user.client.ui.Label; +import com.sap.sailing.domain.common.dto.BoatDTO; +import com.sap.sailing.domain.common.dto.CompetitorDTO; +import com.sap.sailing.gwt.ui.client.FlagImageResolverImpl; +import com.sap.sailing.gwt.ui.client.SailingServiceAsync; +import com.sap.sailing.gwt.ui.client.StringMessages; +import com.sap.sse.common.util.NaturalComparator; +import com.sap.sse.gwt.client.ErrorReporter; +import com.sap.sse.gwt.client.celltable.EntityIdentityComparator; +import com.sap.sse.gwt.client.celltable.RefreshableSelectionModel; +import com.sap.sse.gwt.client.panels.LabeledAbstractFilterablePanel; + +/** + * A compact filterable competitor table. The data model is managed by the {@link #getFilterField() filter field}. In + * order to set an initial set of competitors to display by this table, use {@link #refreshCompetitorList(Iterable)}. + * The selected competitors can be obtained from the {@link #getSelectionModel() selection model}. + * The competitors currently in the table (regardless of the current filter settings) are returned by {@link #getAllCompetitorsOfRace()}. + * + * @author Frank Mittag + * + * @param + */ +public class CompactCompetitorTableWrapper> extends TableWrapper { + private final LabeledAbstractFilterablePanel filterField; + + public CompactCompetitorTableWrapper(SailingServiceAsync sailingService, StringMessages stringMessages, ErrorReporter errorReporter, + boolean multiSelection, boolean enablePager) { + super(sailingService, stringMessages, errorReporter, multiSelection, enablePager, + new EntityIdentityComparator() { + @Override + public boolean representSameEntity(CompetitorDTO dto1, CompetitorDTO dto2) { + return dto1.getIdAsString().equals(dto2.getIdAsString()); + } + @Override + public int hashCode(CompetitorDTO t) { + return t.getIdAsString().hashCode(); + } + }); + ListHandler competitorColumnListHandler = getColumnSortHandler(); + + // competitors table + TextColumn competitorNameColumn = new TextColumn() { + @Override + public String getValue(CompetitorDTO competitor) { + return competitor.getName(); + } + }; + competitorNameColumn.setSortable(true); + competitorColumnListHandler.setComparator(competitorNameColumn, new Comparator() { + @Override + public int compare(CompetitorDTO o1, CompetitorDTO o2) { + return o1.getName().compareTo(o2.getName()); + } + }); + + TextColumn competitorShortNameColumn = new TextColumn() { + @Override + public String getValue(CompetitorDTO competitor) { + return competitor.getShortName(); + } + }; + competitorShortNameColumn.setSortable(true); + competitorColumnListHandler.setComparator(competitorShortNameColumn, new Comparator() { + private final NaturalComparator comparator = new NaturalComparator(/* case sensitive */ false); + @Override + public int compare(CompetitorDTO o1, CompetitorDTO o2) { + return comparator.compare(o1.getShortName(), o2.getShortName()); + } + }); + + Column flagImageColumn = new Column(new SafeHtmlCell()) { + @Override + public SafeHtml getValue(CompetitorDTO competitor) { + SafeHtmlBuilder sb = new SafeHtmlBuilder(); + ImageResourceRenderer renderer = new ImageResourceRenderer(); + final String twoLetterIsoCountryCode = competitor.getTwoLetterIsoCountryCode(); + final String flagImageURL = competitor.getFlagImageURL(); + if (flagImageURL != null && !flagImageURL.isEmpty()) { + sb.appendHtmlConstant(""); + sb.appendHtmlConstant(" "); + } else { + final ImageResource flagImageResource; + if (twoLetterIsoCountryCode==null || twoLetterIsoCountryCode.isEmpty()) { + flagImageResource = FlagImageResolverImpl.get().getEmptyFlagImageResource(); + } else { + flagImageResource = FlagImageResolverImpl.get().getFlagImageResource(twoLetterIsoCountryCode); + } + if (flagImageResource != null) { + sb.append(renderer.render(flagImageResource)); + sb.appendHtmlConstant(" "); + } + } + return sb.toSafeHtml(); + } + }; + + TextColumn competitorSearchTagColumn = new TextColumn() { + @Override + public String getValue(CompetitorDTO competitor) { + return competitor.getSearchTag(); + } + }; + competitorSearchTagColumn.setSortable(true); + competitorColumnListHandler.setComparator(competitorSearchTagColumn, new Comparator() { + @Override + public int compare(CompetitorDTO o1, CompetitorDTO o2) { + return new NaturalComparator(false).compare(o1.getSearchTag(), o2.getSearchTag()); + } + }); + + TextColumn isBoatLinkedColumn = new TextColumn() { + @Override + public String getValue(CompetitorDTO competitor) { + boolean hasBoat = competitor.getBoat() != null; + return hasBoat ? stringMessages.yes() : stringMessages.no(); + } + }; + + filterField = new LabeledAbstractFilterablePanel(new Label(stringMessages.filterCompetitors()), + new ArrayList(), table, dataProvider) { + @Override + public Iterable getSearchableStrings(CompetitorDTO t) { + List string = new ArrayList(); + string.add(t.getName()); + string.add(t.getShortName()); + string.add(t.getSearchTag()); + return string; + } + }; + registerSelectionModelOnNewDataProvider(filterField.getAllListDataProvider()); + + mainPanel.insert(filterField, 0); + table.addColumnSortHandler(competitorColumnListHandler); + table.addColumn(competitorNameColumn, stringMessages.name()); + table.addColumn(competitorShortNameColumn, stringMessages.shortName()); + table.addColumn(flagImageColumn, stringMessages.flags()); + table.addColumn(competitorSearchTagColumn, stringMessages.searchTag()); + table.addColumn(isBoatLinkedColumn, stringMessages.islinked()); + table.ensureDebugId("CompactCompetitorsTable"); + } + + public Iterable getAllCompetitors() { + return filterField.getAll(); + } + + public LabeledAbstractFilterablePanel getFilterField() { + return filterField; + } + + public void refreshCompetitorList(Iterable competitors) { + getFilteredCompetitors(competitors); + } + + public void refreshCompetitorListFromRace(String leaderboardName, String raceColumnName, String fleetName) { + final AsyncCallback> myCallback = new AsyncCallback>() { + @Override + public void onFailure(Throwable caught) { + errorReporter.reportError("Remote Procedure Call getCompetitors() - Failure: " + caught.getMessage()); + } + + @Override + public void onSuccess(Map result) { + refreshCompetitorList(result.keySet()); + } + }; + sailingService.getCompetitorsAndBoatsOfRace(leaderboardName, raceColumnName, fleetName, myCallback); + } + + private void getFilteredCompetitors(Iterable result) { + filterField.updateAll(result); + } +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompetitorEditDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompetitorEditDialog.java old mode 100755 new mode 100644 index 04f6be94f7d..28e66fcaab3 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompetitorEditDialog.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompetitorEditDialog.java @@ -10,15 +10,10 @@ import com.google.gwt.user.client.ui.FocusWidget; import com.google.gwt.user.client.ui.Grid; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.ListBox; -import com.google.gwt.user.client.ui.SuggestBox; import com.google.gwt.user.client.ui.TextBox; import com.google.gwt.user.client.ui.Widget; -import com.sap.sailing.domain.common.Distance; -import com.sap.sailing.domain.common.dto.BoatClassDTO; -import com.sap.sailing.domain.common.dto.BoatDTO; import com.sap.sailing.domain.common.dto.CompetitorDTO; import com.sap.sailing.domain.common.dto.CompetitorDTOImpl; -import com.sap.sailing.gwt.common.client.suggestion.BoatClassMasterdataSuggestOracle; import com.sap.sailing.gwt.ui.client.StringMessages; import com.sap.sse.common.Color; import com.sap.sse.common.CountryCode; @@ -41,70 +36,70 @@ import com.sap.sse.gwt.client.dialog.DataEntryDialog; public class CompetitorEditDialog extends DataEntryDialog { private final CompetitorDTO competitorToEdit; private final TextBox name; - private final SuggestBox boatClassName; + private final TextBox shortName; private final TextBox displayColorTextBox; private final ListBox threeLetterIocCountryCode; - private final TextBox sailId; private final TextBox email; private final TextBox searchTag; private final StringMessages stringMessages; + private final URLFieldWithFileUpload flagImageURL; private final URLFieldWithFileUpload imageUrlAndUploadComposite; private final DoubleBox timeOnTimeFactor; private final DoubleBox timeOnDistanceAllowanceInSecondsPerNauticalMile; + protected static class CompetitorWithoutBoatValidator implements Validator { + protected final StringMessages stringMessages; + + public CompetitorWithoutBoatValidator(StringMessages stringMessages) { + this.stringMessages = stringMessages; + } + + @Override + public String getErrorMessage(CompetitorDTO valueToValidate) { + String result = null; + if (valueToValidate.getName() == null || valueToValidate.getName().isEmpty()) { + result = stringMessages.pleaseEnterAName(); + } else if (valueToValidate.getColor() != null) { + Color displayColor = valueToValidate.getColor(); + if (displayColor instanceof CompetitorEditDialog.InvalidColor) { + result = displayColor.getAsHtml(); + } + } + return result; + } + } + + public static CompetitorEditDialog create(StringMessages stringMessages, CompetitorDTO competitorToEdit, + DialogCallback callback) { + return new CompetitorEditDialog(stringMessages.editCompetitor(), stringMessages, competitorToEdit, + new CompetitorEditDialog.CompetitorWithoutBoatValidator(stringMessages), callback); + } + /** - * The class creates the UI-dialog to type in the Data about a competitor. + * The class creates the UI-dialog to type in the data of a competitor. * * @param competitorToEdit * The 'competitorToEdit' parameter contains the competitor which should be changed or initialized. - * @param boatClass - * The boat class is the default shown boat class for new competitors. Set null if your competitor is - * already initialized or you don�t want a default boat class. */ - public CompetitorEditDialog(final StringMessages stringMessages, CompetitorDTO competitorToEdit, - DialogCallback callback, String boatClass) { - super(stringMessages.editCompetitor(), null, stringMessages.ok(), stringMessages - .cancel(), new Validator() { - @Override - public String getErrorMessage(CompetitorDTO valueToValidate) { - String result = null; - if (valueToValidate.getName() == null || valueToValidate.getName().isEmpty()) { - result = stringMessages.pleaseEnterAName(); - } else if (valueToValidate.getSailID() == null || valueToValidate.getSailID().isEmpty()) { - result = stringMessages.pleaseEnterASailNumber(); - } else if (valueToValidate.getColor() != null) { - Color displayColor = valueToValidate.getColor(); - if (displayColor instanceof InvalidColor) { - result = displayColor.getAsHtml(); - } - } else if (valueToValidate.getBoatClass().getName() == null || valueToValidate.getBoatClass().getName().isEmpty()) { - result = stringMessages.pleaseEnterABoatClass(); - } - return result; - } - }, /* animationEnabled */true, callback); + protected CompetitorEditDialog(String dialogTitle, StringMessages stringMessages, CompetitorDTO competitorToEdit, + Validator validator, DialogCallback callback) { + super(dialogTitle, null, stringMessages.ok(), stringMessages.cancel(), + validator, /* animationEnabled */true, callback); this.ensureDebugId("CompetitorEditDialog"); this.stringMessages = stringMessages; this.competitorToEdit = competitorToEdit; - - this.boatClassName = createSuggestBox(new BoatClassMasterdataSuggestOracle()); - boatClassName.ensureDebugId("BoatClassNameSuggestBox"); - int i=0; - if (competitorToEdit.getBoatClass() != null) { - boatClassName.setValue(competitorToEdit.getBoatClass().getName()); - boatClassName.setEnabled(false); - } else { - boatClassName.setValue(boatClass); // widgets have to accept null values here - } + this.name = createTextBox(competitorToEdit.getName()); name.ensureDebugId("NameTextBox"); + this.shortName = createTextBox(competitorToEdit.getShortName()); + shortName.ensureDebugId("ShortNameTextBox"); this.email = createTextBox(competitorToEdit.getEmail()); this.searchTag = createTextBox(competitorToEdit.getSearchTag()); this.displayColorTextBox = createTextBox(competitorToEdit.getColor() == null ? "" : competitorToEdit.getColor().getAsHtml()); this.threeLetterIocCountryCode = createListBox(/* isMultipleSelect */ false); CountryCodeFactory ccf = CountryCodeFactory.INSTANCE; - i=0; + int i=0; List ccs = new ArrayList(); Util.addAll(ccf.getAll(), ccs); ccs.add(null); // representing no nationality (NONE / white flag) @@ -129,8 +124,6 @@ public class CompetitorEditDialog extends DataEntryDialog { i++; } } - this.sailId = createTextBox(competitorToEdit.getSailID()); - sailId.ensureDebugId("SailIdTextBox"); this.flagImageURL = new URLFieldWithFileUpload(stringMessages); this.flagImageURL.setURL(competitorToEdit.getFlagImageURL()); this.imageUrlAndUploadComposite = new URLFieldWithFileUpload(stringMessages); @@ -151,7 +144,7 @@ public class CompetitorEditDialog extends DataEntryDialog { * @author Axel Uhl (D043530) * */ - private class InvalidColor implements Color { + protected class InvalidColor implements Color { private static final long serialVersionUID = 4012986110898149543L; private final Exception exception; @@ -173,7 +166,7 @@ public class CompetitorEditDialog extends DataEntryDialog { public String getAsHtml() { return stringMessages.invalidColor(exception.getMessage()); } - + @Override public Color invert() { return null; @@ -192,26 +185,24 @@ public class CompetitorEditDialog extends DataEntryDialog { color = new InvalidColor(iae); } } - BoatClassDTO boatClass = new BoatClassDTO(boatClassName.getValue(), Distance.NULL, Distance.NULL); - BoatDTO boat = new BoatDTO(name.getText(), sailId.getText()); - CompetitorDTO result = new CompetitorDTOImpl(name.getText(), color, email.getText(), + CompetitorDTO result = new CompetitorDTOImpl(name.getText(), shortName.getText(), color, email.getText(), /* twoLetterIsoCountryCode */ null, threeLetterIocCountryCode.getValue(threeLetterIocCountryCode.getSelectedIndex()), /* countryName */ null, competitorToEdit.getIdAsString(), - imageUrlAndUploadComposite.getURL(), flagImageURL.getURL(), boat, boatClass, + imageUrlAndUploadComposite.getURL(), flagImageURL.getURL(), timeOnTimeFactor.getValue(), timeOnDistanceAllowanceInSecondsPerNauticalMile.getValue() == null ? null : - new MillisecondsDurationImpl((long) (timeOnDistanceAllowanceInSecondsPerNauticalMile.getValue()*1000)), searchTag.getValue()); + new MillisecondsDurationImpl((long) (timeOnDistanceAllowanceInSecondsPerNauticalMile.getValue()*1000)), searchTag.getValue(), null); return result; } @Override protected Widget getAdditionalWidget() { - Grid result = new Grid(11, 2); + Grid result = new Grid(10, 2); result.setWidget(0, 0, new Label(stringMessages.name())); result.setWidget(0, 1, name); - result.setWidget(1, 0, new Label(stringMessages.sailNumber())); - result.setWidget(1, 1, sailId); + result.setWidget(1, 0, new Label(stringMessages.shortName())); + result.setWidget(1, 1, shortName); result.setWidget(2, 0, new Label(stringMessages.nationality())); result.setWidget(2, 1, threeLetterIocCountryCode); result.setWidget(3, 0, new Label(stringMessages.color())); @@ -220,17 +211,23 @@ public class CompetitorEditDialog extends DataEntryDialog { result.setWidget(4, 1, email); result.setWidget(5, 0, new Label(stringMessages.searchTag())); result.setWidget(5, 1, searchTag); - result.setWidget(6, 0, new Label(stringMessages.boatClass())); - result.setWidget(6, 1, boatClassName); - result.setWidget(7, 0, new Label(stringMessages.flagImageURL())); - result.setWidget(7, 1, flagImageURL); - result.setWidget(8, 0, new Label(stringMessages.imageURL())); - result.setWidget(8, 1, imageUrlAndUploadComposite); - result.setWidget(9, 0, new Label(stringMessages.timeOnTimeFactor())); - result.setWidget(9, 1, timeOnTimeFactor); - result.setWidget(10, 0, new Label(stringMessages.timeOnDistanceAllowanceInSecondsPerNauticalMile())); - result.setWidget(10, 1, timeOnDistanceAllowanceInSecondsPerNauticalMile); + result.setWidget(6, 0, new Label(stringMessages.flagImageURL())); + result.setWidget(6, 1, flagImageURL); + result.setWidget(7, 0, new Label(stringMessages.imageURL())); + result.setWidget(7, 1, imageUrlAndUploadComposite); + result.setWidget(8, 0, new Label(stringMessages.timeOnTimeFactor())); + result.setWidget(8, 1, timeOnTimeFactor); + result.setWidget(9, 0, new Label(stringMessages.timeOnDistanceAllowanceInSecondsPerNauticalMile())); + result.setWidget(9, 1, timeOnDistanceAllowanceInSecondsPerNauticalMile); return result; } + protected CompetitorDTO getCompetitorToEdit() { + return competitorToEdit; + } + + protected StringMessages getStringMessages() { + return stringMessages; + } + } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompetitorImportProviderSelectionDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompetitorImportProviderSelectionDialog.java index ab7392da5f0..4a81ff8c700 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompetitorImportProviderSelectionDialog.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompetitorImportProviderSelectionDialog.java @@ -222,7 +222,7 @@ public class CompetitorImportProviderSelectionDialog extends DataEntryDialog(getCompetitorDescriptorsCallback)); - sailingService.getCompetitors(new MarkedAsyncCallback<>(getCompetitorsCallback)); + sailingService.getCompetitors(false, false, new MarkedAsyncCallback<>(getCompetitorsCallback)); } } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompetitorPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompetitorPanel.java index 401e64b3fdb..9af90da4ab1 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompetitorPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompetitorPanel.java @@ -45,7 +45,8 @@ public class CompetitorPanel extends SimplePanel implements BusyDisplay { final StringMessages stringMessages, final ErrorReporter errorReporter) { super(); this.leaderboardName = leaderboardName; - this.competitorTable = new CompetitorTableWrapper<>(sailingService, stringMessages, errorReporter, /* multiSelection */ true, /* enablePager */ true); + this.competitorTable = new CompetitorTableWrapper<>(sailingService, stringMessages, errorReporter, /* multiSelection */ true, /* enablePager */ true, + /* filterCompetitorWithBoat */ false, /* filterCompetitorsWithoutBoat */ false); this.refreshableCompetitorSelectionModel = (RefreshableMultiSelectionModel) competitorTable.getSelectionModel(); busyIndicator = new SimpleBusyIndicator(false, 0.8f); @@ -175,9 +176,11 @@ public class CompetitorPanel extends SimplePanel implements BusyDisplay { } private void openAddCompetitorDialog() { - competitorTable.openEditCompetitorDialog(new CompetitorDTOImpl(), /* boat class to be used from CompetitorDTO */ null); + CompetitorDTOImpl competitorDTO = new CompetitorDTOImpl(); + competitorDTO.setBoat(null); + competitorTable.openEditCompetitorWithoutBoatDialog(competitorDTO); } - + public void refreshCompetitorList() { competitorTable.refreshCompetitorList(leaderboardName); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompetitorRegistrationsPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompetitorRegistrationsPanel.java index 85044022005..d324b1a7519 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompetitorRegistrationsPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompetitorRegistrationsPanel.java @@ -43,16 +43,15 @@ import com.sap.sse.gwt.client.controls.busyindicator.SimpleBusyIndicator; * */ public class CompetitorRegistrationsPanel extends FlowPanel implements BusyDisplay { - protected CompetitorTableWrapper> allCompetitorsTable; - protected CompetitorTableWrapper> registeredCompetitorsTable; - protected final boolean filterByLeaderBoardInitially = false; - final StringMessages stringMessages; - final ErrorReporter errorReporter; + private final CompetitorTableWrapper> allCompetitorsTable; + private final CompetitorTableWrapper> registeredCompetitorsTable; + private final ErrorReporter errorReporter; private Button registerBtn; private Button unregisterBtn; + private Button addCompetitorWithBoatButton; private Button addCompetitorButton; private CheckBox showOnlyCompetitorsOfLogCheckBox; - protected String leaderboardName; + private final String leaderboardName; private final BusyIndicator busyIndicator; private final ImportCompetitorCallback importCompetitorCallback; private final Runnable validator; @@ -78,10 +77,9 @@ public class CompetitorRegistrationsPanel extends FlowPanel implements BusyDispl */ protected CompetitorRegistrationsPanel(final SailingServiceAsync sailingService, final StringMessages stringMessages, final ErrorReporter errorReporter, boolean editable, - String leaderboardName, final String boatClass, Runnable validator, + String leaderboardName, boolean canBoatsOfCompetitorsChangePerRace, String boatClass, Runnable validator, Consumer>> registeredCompetitorsRetriever, boolean restrictPoolToLeaderboard, Widget... additionalWidgetsBeforeTables) { - this.stringMessages = stringMessages; this.errorReporter = errorReporter; this.restrictPoolToLeaderboard = restrictPoolToLeaderboard; this.validator = validator; @@ -94,7 +92,17 @@ public class CompetitorRegistrationsPanel extends FlowPanel implements BusyDispl addCompetitorButton.addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { - registeredCompetitorsTable.openEditCompetitorDialog(new CompetitorDTOImpl(), boatClass); + CompetitorDTOImpl competitorDTO = new CompetitorDTOImpl(); + competitorDTO.setBoat(null); + registeredCompetitorsTable.openEditCompetitorWithoutBoatDialog(competitorDTO); + } + }); + addCompetitorWithBoatButton = new Button(stringMessages.add(stringMessages.competitorWithBoat())); + addCompetitorWithBoatButton.addClickHandler(new ClickHandler() { + @Override + public void onClick(ClickEvent event) { + CompetitorDTOImpl competitorDTO = new CompetitorDTOImpl(); + registeredCompetitorsTable.openEditCompetitorWithBoatDialog(competitorDTO, boatClass); } }); @@ -135,9 +143,9 @@ public class CompetitorRegistrationsPanel extends FlowPanel implements BusyDispl final CaptionPanel allCompetitorsPanel = new CaptionPanel(stringMessages.competitorPool()); final CaptionPanel registeredCompetitorsPanel = new CaptionPanel(stringMessages.registeredCompetitors()); allCompetitorsTable = new CompetitorTableWrapper<>(sailingService, stringMessages, errorReporter, /* multiSelection */ - true, /* enablePager */true); + true, /* enablePager */true, /* filterCompetitorWithBoat */ canBoatsOfCompetitorsChangePerRace, /* filterCompetitorsWithoutBoat */ !canBoatsOfCompetitorsChangePerRace); registeredCompetitorsTable = new CompetitorTableWrapper<>(sailingService, stringMessages, errorReporter, /* multiSelection */ - true, /* enablePager */false); + true, /* enablePager */false, /* filterCompetitorWithBoat */ false, /* filterCompetitorsWithoutBoat */ false); allCompetitorsPanel.add(allCompetitorsTable); registeredCompetitorsPanel.add(registeredCompetitorsTable); VerticalPanel movePanel = new VerticalPanel(); @@ -163,7 +171,11 @@ public class CompetitorRegistrationsPanel extends FlowPanel implements BusyDispl competitorRegistrationPanel.add(movePanel); competitorRegistrationPanel.setCellVerticalAlignment(movePanel, HasVerticalAlignment.ALIGN_MIDDLE); competitorRegistrationPanel.add(allCompetitorsPanel); - buttonPanel.add(addCompetitorButton); + if (canBoatsOfCompetitorsChangePerRace) { + buttonPanel.add(addCompetitorButton); + } else { + buttonPanel.add(addCompetitorWithBoatButton); + } buttonPanel.add(inviteCompetitorsButton); buttonPanel.add(competitorImportButton); buttonPanel.add(busyIndicator); @@ -263,9 +275,11 @@ public class CompetitorRegistrationsPanel extends FlowPanel implements BusyDispl public void deactivateRegistrationButtons(String tooltip){ registerBtn.setEnabled(false); unregisterBtn.setEnabled(false); + addCompetitorWithBoatButton.setEnabled(false); addCompetitorButton.setEnabled(false); registerBtn.setTitle(tooltip); unregisterBtn.setTitle(tooltip); + addCompetitorWithBoatButton.setTitle(tooltip); addCompetitorButton.setTitle(tooltip); validateAndUpdate(); } @@ -279,9 +293,11 @@ public class CompetitorRegistrationsPanel extends FlowPanel implements BusyDispl public void activateRegistrationButtons(){ registerBtn.setEnabled(true); unregisterBtn.setEnabled(true); + addCompetitorWithBoatButton.setEnabled(true); addCompetitorButton.setEnabled(true); registerBtn.setTitle(""); unregisterBtn.setTitle(""); + addCompetitorWithBoatButton.setTitle(""); addCompetitorButton.setTitle(""); validateAndUpdate(); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompetitorTableWrapper.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompetitorTableWrapper.java index b531117dd50..b7280553188 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompetitorTableWrapper.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompetitorTableWrapper.java @@ -20,6 +20,7 @@ import com.google.gwt.user.client.Window; import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.user.client.ui.Label; import com.google.gwt.view.client.CellPreviewEvent; +import com.sap.sailing.domain.common.dto.BoatDTO; import com.sap.sailing.domain.common.dto.CompetitorDTO; import com.sap.sailing.domain.common.dto.CompetitorWithToolTipDTO; import com.sap.sailing.gwt.ui.adminconsole.ColorColumn.ColorRetriever; @@ -49,6 +50,8 @@ import com.sap.sse.gwt.client.panels.LabeledAbstractFilterablePanel; */ public class CompetitorTableWrapper> extends TableWrapper { private final LabeledAbstractFilterablePanel filterField; + private final boolean filterCompetitorsWithBoat; + private final boolean filterCompetitorsWithoutBoat; private static final Template TEMPLATE = GWT.create(Template.class); @@ -60,7 +63,7 @@ public class CompetitorTableWrapper() { @Override @@ -72,6 +75,8 @@ public class CompetitorTableWrapper competitorColumnListHandler = getColumnSortHandler(); // competitors table @@ -89,6 +94,21 @@ public class CompetitorTableWrapper competitorShortNameColumn = new TextColumn() { + @Override + public String getValue(CompetitorDTO competitor) { + return competitor.getShortName(); + } + }; + competitorShortNameColumn.setSortable(true); + competitorColumnListHandler.setComparator(competitorShortNameColumn, new Comparator() { + private final NaturalComparator comparator = new NaturalComparator(/* case sensitive */ false); + @Override + public int compare(CompetitorDTO o1, CompetitorDTO o2) { + return comparator.compare(o1.getShortName(), o2.getShortName()); + } + }); + TextColumn boatClassColumn = new TextColumn() { @Override public String getValue(CompetitorDTO competitor) { @@ -100,11 +120,20 @@ public class CompetitorTableWrapper sailIdColumn = new Column(new SafeHtmlCell()) { + + Column flagImageColumn = new Column(new SafeHtmlCell()) { @Override public SafeHtml getValue(CompetitorDTO competitor) { SafeHtmlBuilder sb = new SafeHtmlBuilder(); @@ -125,16 +154,35 @@ public class CompetitorTableWrapper sailIdColumn = new TextColumn() { + @Override + public String getValue(CompetitorDTO competitor) { + if (competitor.getBoat() != null) { + return competitor.getBoat().getSailId(); + } + return ""; + } + }; + sailIdColumn.setSortable(true); competitorColumnListHandler.setComparator(sailIdColumn, new Comparator() { private final NaturalComparator comparator = new NaturalComparator(/* case sensitive */ false); @Override public int compare(CompetitorDTO o1, CompetitorDTO o2) { - return comparator.compare(o1.getSailID(), o2.getSailID()); + BoatDTO boat1 = o1.getBoat(); + BoatDTO boat2 = o2.getBoat(); + if (boat1 == null && boat2 == null) { + return 0; + } else if (boat1 != null && boat2 == null) { + return 1; + } else if (boat1 == null && boat2 != null) { + return -1; + } + return comparator.compare(boat1.getSailId(), boat2.getSailId()); } }); @@ -246,10 +294,15 @@ public class CompetitorTableWrapper getSearchableStrings(CompetitorDTO t) { List string = new ArrayList(); string.add(t.getName()); - string.add(t.getSailID()); - string.add(t.getBoatClass().getName()); + string.add(t.getShortName()); string.add(t.getIdAsString()); string.add(t.getSearchTag()); + if (t.getBoatClass() != null) { + string.add(t.getBoatClass().getName()); + } + if (t.getSailID() != null) { + string.add(t.getSailID()); + } return string; } }; @@ -262,7 +315,15 @@ public class CompetitorTableWrapper result) { filterField.updateAll(result); } - - void openEditCompetitorDialog(final CompetitorDTO originalCompetitor, String boatClass) { - final CompetitorEditDialog dialog = new CompetitorEditDialog(stringMessages, originalCompetitor, new DialogCallback() { + + void openEditCompetitorWithBoatDialog(final CompetitorDTO originalCompetitor, String boatClass) { + final CompetitorWithBoatEditDialog dialog = new CompetitorWithBoatEditDialog(stringMessages, + originalCompetitor, new DialogCallback() { @Override public void ok(final CompetitorDTO competitor) { - final List competitors = new ArrayList<>(); - competitors.add(competitor); - sailingService.addOrUpdateCompetitor(competitors, new AsyncCallback>() { + sailingService.addOrUpdateCompetitorWithBoat(competitor, new AsyncCallback() { @Override public void onFailure(Throwable caught) { - errorReporter.reportError("Error trying to update competitor: " + caught.getMessage()); + errorReporter.reportError("Error trying to update competitor with boat: " + caught.getMessage()); } @Override - public void onSuccess(List updatedCompetitor) { - assert updatedCompetitor.size() == 1; + public void onSuccess(CompetitorDTO updatedCompetitor) { //only reload selected competitors reloading with refreshCompetitorList(leaderboardName) //would not work in case the list is not based on a leaderboard e.g. AbstractCompetitorRegistrationDialog int editedCompetitorIndex = getFilterField().indexOf(originalCompetitor); getFilterField().remove(originalCompetitor); if (editedCompetitorIndex >= 0){ - getFilterField().add(editedCompetitorIndex, updatedCompetitor.iterator().next()); + getFilterField().add(editedCompetitorIndex, updatedCompetitor); } else { //in case competitor was not present --> not edit, but create - getFilterField().add(updatedCompetitor.iterator().next()); + getFilterField().add(updatedCompetitor); } getDataProvider().refresh(); } @@ -369,7 +430,40 @@ public class CompetitorTableWrapper() { + @Override + public void ok(final CompetitorDTO competitor) { + sailingService.addOrUpdateCompetitorWithoutBoat(competitor, new AsyncCallback() { + @Override + public void onFailure(Throwable caught) { + errorReporter.reportError("Error trying to update competitor: " + caught.getMessage()); + } + + @Override + public void onSuccess(CompetitorDTO updatedCompetitor) { + //only reload selected competitors reloading with refreshCompetitorList(leaderboardName) + //would not work in case the list is not based on a leaderboard e.g. AbstractCompetitorRegistrationDialog + int editedCompetitorIndex = getFilterField().indexOf(originalCompetitor); + getFilterField().remove(originalCompetitor); + if (editedCompetitorIndex >= 0){ + getFilterField().add(editedCompetitorIndex, updatedCompetitor); + } else { + //in case competitor was not present --> not edit, but create + getFilterField().add(updatedCompetitor); + } + getDataProvider().refresh(); + } + }); + } + + @Override + public void cancel() { + } + }); dialog.show(); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompetitorToBoatMappingsDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompetitorToBoatMappingsDialog.java new file mode 100644 index 00000000000..2e39bee3532 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompetitorToBoatMappingsDialog.java @@ -0,0 +1,279 @@ + package com.sap.sailing.gwt.ui.adminconsole; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import com.google.gwt.cell.client.FieldUpdater; +import com.google.gwt.core.client.Scheduler; +import com.google.gwt.core.client.Scheduler.ScheduledCommand; +import com.google.gwt.event.shared.HandlerRegistration; +import com.google.gwt.user.client.Window; +import com.google.gwt.user.client.ui.CaptionPanel; +import com.google.gwt.user.client.ui.Grid; +import com.google.gwt.user.client.ui.HasVerticalAlignment; +import com.google.gwt.user.client.ui.HorizontalPanel; +import com.google.gwt.user.client.ui.VerticalPanel; +import com.google.gwt.user.client.ui.Widget; +import com.google.gwt.view.client.ListDataProvider; +import com.google.gwt.view.client.SelectionChangeEvent; +import com.sap.sailing.domain.common.dto.BoatDTO; +import com.sap.sailing.domain.common.dto.CompetitorDTO; +import com.sap.sailing.gwt.ui.client.SailingServiceAsync; +import com.sap.sailing.gwt.ui.client.StringMessages; +import com.sap.sse.gwt.client.ErrorReporter; +import com.sap.sse.gwt.client.celltable.RefreshableSelectionModel; +import com.sap.sse.gwt.client.celltable.RefreshableSingleSelectionModel; +import com.sap.sse.gwt.client.dialog.DataEntryDialog; + +/** + * Allows an administrator to map a list of given competitors to boats for a race. + * + * @author Frank Mittag + * + */ +public class CompetitorToBoatMappingsDialog extends DataEntryDialog> { + private final CompactCompetitorTableWrapper> competitorTable; + private final CompactBoatTableWrapper> boatTable; + + private final RefreshableSelectionModel refreshableBoatSelectionModel; + private final RefreshableSelectionModel refreshableCompetitorSelectionModel; + + private final StringMessages stringMessages; + private final SelectionChangeEvent.Handler boatListHandler; + private HandlerRegistration boatListHandlerRegistration; + + private final Map competitorToBoatMappings; + + protected static class CompetitorToBoatMappingValidator implements Validator> { + public CompetitorToBoatMappingValidator() { + } + + @Override + public String getErrorMessage(Map valueToValidate) { + String errorMessage = null; + for (Map.Entry competitorAndBoatEntry: valueToValidate.entrySet()) { + if (competitorAndBoatEntry.getValue() == null) { + errorMessage = "It's required that all competitors have an assigned boat."; + break; + } + } + + return errorMessage; + } + } + + public CompetitorToBoatMappingsDialog(final SailingServiceAsync sailingService, final StringMessages stringMessages, + final ErrorReporter errorReporter, Map competitorsAndBoats, DialogCallback> callback) { + super(stringMessages.actionEditCompetitorToBoatAssignments(), null, stringMessages.ok(), stringMessages.cancel(), new CompetitorToBoatMappingValidator(), callback); + + this.stringMessages = stringMessages; + this.competitorToBoatMappings = new HashMap<>(competitorsAndBoats); + + this.competitorTable = new CompactCompetitorTableWrapper<>(sailingService, stringMessages, errorReporter, /* multiSelection */ false, /* enablePager */ true); + this.boatTable = new CompactBoatTableWrapper<>(sailingService, stringMessages, errorReporter, /* multiSelection */ false, /* enablePager */ true); + + ImagesBarColumn competitorActionColumn = new ImagesBarColumn<>(new CompactCompetitorConfigImagesBarCell(stringMessages)); + competitorActionColumn.setFieldUpdater(new FieldUpdater() { + @Override + public void update(int index, CompetitorDTO competitor, String value) { + if (CompactCompetitorConfigImagesBarCell.ACTION_UNLINK.equals(value)) { + unlinkBoatFromCompetitor(competitor); + validateAndUpdate(); + } + } + }); + competitorTable.getTable().addColumn(competitorActionColumn, stringMessages.actions()); + + refreshableBoatSelectionModel = boatTable.getSelectionModel(); + refreshableCompetitorSelectionModel = competitorTable.getSelectionModel(); + + boatListHandler = new SelectionChangeEvent.Handler() { + @Override + public void onSelectionChange(SelectionChangeEvent event) { + Set selectedBoats = refreshableBoatSelectionModel.getSelectedSet(); + CompetitorDTO selectedCompetitor = getSelectedCompetitor(); + // if no competitor is selected, ignore the boat selection change + if (selectedCompetitor != null) { + if (selectedBoats.isEmpty()) { + if (hasLinkedBoat(selectedCompetitor)) { + unlinkBoatFromCompetitor(selectedCompetitor); + } + } else { + BoatDTO selectedBoat = selectedBoats.iterator().next(); + if (hasLinkedBoat(selectedCompetitor)) { + // competitor has already a linked but, but now another one is selected + if (!isLinkedToBoat(selectedCompetitor, selectedBoat) && !isBoatUsed(selectedBoat)) { + linkBoatToSelectedCompetitor(selectedCompetitor, selectedBoat); + } else { + Window.alert(stringMessages.boatAlreadyLinked()); + } + } else { + // check if boat is already used with another competitor + if (isBoatUsed(selectedBoat)) { + Window.alert(stringMessages.boatAlreadyLinked()); + } else { + linkBoatToSelectedCompetitor(selectedCompetitor, selectedBoat); + } + } + } + } + validateAndUpdate(); + } + + private boolean hasLinkedBoat(CompetitorDTO selectedCompetitor) { + return selectedCompetitor.getBoat() != null; + } + + private boolean isLinkedToBoat(CompetitorDTO selectedCompetitor, BoatDTO selectedBoat){ + return selectedBoat.equals(selectedCompetitor.getBoat()); + } + + private boolean isBoatUsed(BoatDTO selectedBoat){ + boolean boatAlreadyUsed = false; + for (BoatDTO boat: competitorToBoatMappings.values()) { + if (boat != null && boat.equals(selectedBoat)) { + boatAlreadyUsed = true; + break; + } + } + + return boatAlreadyUsed; + } + + }; + // boat selection changes should only have an effect in case the 'actions' are enabled + boatListHandlerRegistration = refreshableBoatSelectionModel.addSelectionChangeHandler(boatListHandler); + + refreshableCompetitorSelectionModel.addSelectionChangeHandler(new SelectionChangeEvent.Handler() { + public void onSelectionChange(SelectionChangeEvent event) { + // If the selection on the competitorTable changes, + // you don't want to link or unlink competitors with the + // boatListHandler. + removeBoatListHandlerTemporarily(); + competitorSelectionChanged(); + } + }); + + // Attention: We use the boatDTO property of the CompetitorDTO to make the CompetitorTable aware of linked boats + for (Map.Entry competitorAndBoatEntry: competitorToBoatMappings.entrySet()) { + CompetitorDTO c = competitorAndBoatEntry.getKey(); + if (competitorAndBoatEntry.getValue() != null) { + c.setBoat(competitorAndBoatEntry.getValue()); + } + } + + competitorTable.refreshCompetitorList(competitorToBoatMappings.keySet()); + boatTable.refreshBoatList(); + } + + private void linkBoatToSelectedCompetitor(CompetitorDTO selectedCompetitor, BoatDTO selectedBoat) { + competitorToBoatMappings.remove(selectedCompetitor); + selectedCompetitor.setBoat(selectedBoat); + competitorToBoatMappings.put(selectedCompetitor, selectedBoat); + + competitorTable.getDataProvider().refresh(); + competitorTable.getTable().redraw(); + } + + private void unlinkBoatFromCompetitor(CompetitorDTO selectedCompetitor) { + competitorToBoatMappings.remove(selectedCompetitor); + selectedCompetitor.setBoat(null); + competitorToBoatMappings.put(selectedCompetitor, null); + + boatTable.getSelectionModel().clear(); + competitorTable.getDataProvider().refresh(); + competitorTable.getTable().redraw(); + + } + + private void competitorSelectionChanged() { + CompetitorDTO selectedCompetitor = getSelectedCompetitor(); + if (selectedCompetitor != null) { + selectBoatForCompetitor(selectedCompetitor); + } else { + boatTable.getSelectionModel().clear(); + } + } + + private void selectBoatInList(BoatDTO boat) { + boatTable.selectBoat(boat); + } + + private void selectBoatForCompetitor(CompetitorDTO selectedCompetitor) { + removeBoatListHandlerTemporarily(); + // BoatDTO boat = competitorToBoatMappings.get(selectedCompetitor); + if (selectedCompetitor.getBoat() != null) { + selectBoatInList(selectedCompetitor.getBoat()); + } else { + boatTable.clearSelection(); + } + boatTable.getTable().redraw(); + } + + /** + * Removes the {@link SelectionChangeEvent.Handler} until the browser regains control. The handler will be added + * again using {@link Scheduler#scheduleDeferred(ScheduledCommand)} method. + *

+ * Use this method if you change the {@link ListDataProvider} or {@link RefreshableSelectionModel} of + * {@link BoatTableWrapper} and you don't want to trigger the + * {@link SelectionChangeEvent.Handler#onSelectionChange(SelectionChangeEvent)}. + */ + private void removeBoatListHandlerTemporarily() { + if (boatListHandlerRegistration == null) { + return; + } + boatListHandlerRegistration.removeHandler(); + boatListHandlerRegistration = null; + // It is necessary to do this with the ScheduleDeferred() method, + // because the SelectionChangeEvent isn't fired directly after + // selection changes. So an remove of SelectionChangeHandler before + // the selection change and and new registration directly after it + // isn't possible. + Scheduler.get().scheduleDeferred(new ScheduledCommand() { + @Override + public void execute() { + boatListHandlerRegistration = refreshableBoatSelectionModel.addSelectionChangeHandler(boatListHandler); + } + }); + } + + private CompetitorDTO getSelectedCompetitor() { + if (competitorTable.getSelectionModel().getSelectedSet().isEmpty()) { + return null; + } + return competitorTable.getSelectionModel().getSelectedSet().iterator().next(); + } + + @Override + protected Widget getAdditionalWidget() { + VerticalPanel mainPanel = new VerticalPanel(); + + CaptionPanel competitorsPanel = new CaptionPanel(stringMessages.competitors()); + competitorsPanel.ensureDebugId("CompetitorsSection"); + competitorsPanel.setContentWidget(this.competitorTable.asWidget()); + + CaptionPanel boatsPanel = new CaptionPanel(stringMessages.boats()); + boatsPanel.ensureDebugId("BoatsSection"); + boatsPanel.setContentWidget(this.boatTable.asWidget()); + + HorizontalPanel buttonPanel = new HorizontalPanel(); + buttonPanel.setSpacing(5); + + mainPanel.add(buttonPanel); + + Grid grid = new Grid(1,2); + grid.setWidget(0, 0, competitorsPanel); + grid.setWidget(0, 1, boatsPanel); + grid.getCellFormatter().setVerticalAlignment(0, 0, HasVerticalAlignment.ALIGN_TOP); + grid.getCellFormatter().setVerticalAlignment(0, 1, HasVerticalAlignment.ALIGN_TOP); + mainPanel.add(grid); + + return mainPanel; + } + + @Override + protected Map getResult() { + return competitorToBoatMappings; + } +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompetitorToBoatMappingsViewPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompetitorToBoatMappingsViewPanel.java new file mode 100644 index 00000000000..c0f6754140b --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompetitorToBoatMappingsViewPanel.java @@ -0,0 +1,131 @@ +package com.sap.sailing.gwt.ui.adminconsole; + +import com.google.gwt.user.client.rpc.AsyncCallback; +import com.google.gwt.user.client.ui.CaptionPanel; +import com.google.gwt.user.client.ui.Grid; +import com.google.gwt.user.client.ui.HasVerticalAlignment; +import com.google.gwt.user.client.ui.HorizontalPanel; +import com.google.gwt.user.client.ui.SimplePanel; +import com.google.gwt.user.client.ui.VerticalPanel; +import com.google.gwt.view.client.SelectionChangeEvent; +import com.sap.sailing.domain.common.dto.BoatDTO; +import com.sap.sailing.domain.common.dto.CompetitorDTO; +import com.sap.sailing.gwt.ui.client.SailingServiceAsync; +import com.sap.sailing.gwt.ui.client.StringMessages; +import com.sap.sse.gwt.client.ErrorReporter; +import com.sap.sse.gwt.client.async.MarkedAsyncCallback; +import com.sap.sse.gwt.client.celltable.RefreshableSelectionModel; +import com.sap.sse.gwt.client.celltable.RefreshableSingleSelectionModel; + +/** + * Allows an administrator to view the boats assigned to the competitors of a race. + * + * @author Frank Mittag + * + */ +public class CompetitorToBoatMappingsViewPanel extends SimplePanel { + private final CompactCompetitorTableWrapper> competitorTable; + private final CompactBoatTableWrapper> boatTable; + private final String leaderboardName; + private final String raceColumnName; + private final String fleetName; + private final SailingServiceAsync sailingService; + private final ErrorReporter errorReporter; + + private final RefreshableSelectionModel refreshableCompetitorSelectionModel; + + public CompetitorToBoatMappingsViewPanel(final SailingServiceAsync sailingService, final StringMessages stringMessages, + final ErrorReporter errorReporter, final String leaderboardName, final String raceColumnName, final String fleetName) { + super(); + this.sailingService = sailingService; + this.leaderboardName = leaderboardName; + this.raceColumnName = raceColumnName; + this.fleetName = fleetName; + this.errorReporter = errorReporter; + + this.competitorTable = new CompactCompetitorTableWrapper<>(sailingService, stringMessages, errorReporter, /* multiSelection */ false, /* enablePager */ true); + this.boatTable = new CompactBoatTableWrapper<>(sailingService, stringMessages, errorReporter, /* multiSelection */ false, /* enablePager */ true); + + refreshableCompetitorSelectionModel = competitorTable.getSelectionModel(); + + refreshableCompetitorSelectionModel.addSelectionChangeHandler(new SelectionChangeEvent.Handler() { + public void onSelectionChange(SelectionChangeEvent event) { + // If the selection on the competitorTable changes, + // you don't want to link or unlink competitors with the + // boatListHandler. + competitorSelectionChanged(); + } + }); + + VerticalPanel mainPanel = new VerticalPanel(); + this.setWidget(mainPanel); + mainPanel.setWidth("100%"); + + CaptionPanel competitorsPanel = new CaptionPanel(stringMessages.competitors()); + competitorsPanel.ensureDebugId("CompetitorsSection"); + competitorsPanel.setContentWidget(this.competitorTable.asWidget()); + + CaptionPanel boatsPanel = new CaptionPanel(stringMessages.boats()); + boatsPanel.ensureDebugId("BoatsSection"); + boatsPanel.setContentWidget(this.boatTable.asWidget()); + + HorizontalPanel buttonPanel = new HorizontalPanel(); + buttonPanel.setSpacing(5); + + mainPanel.add(buttonPanel); + + Grid grid = new Grid(1,2); + grid.setWidget(0, 0, competitorsPanel); + grid.setWidget(0, 1, boatsPanel); + grid.getCellFormatter().setVerticalAlignment(0, 0, HasVerticalAlignment.ALIGN_TOP); + grid.getCellFormatter().setVerticalAlignment(0, 1, HasVerticalAlignment.ALIGN_TOP); + mainPanel.add(grid); + + competitorTable.refreshCompetitorListFromRace(leaderboardName, raceColumnName, fleetName); + boatTable.refreshBoatListFromRace(leaderboardName, raceColumnName, fleetName); + } + + private void competitorSelectionChanged() { + CompetitorDTO selectedCompetitor = getSelectedCompetitor(); + if (selectedCompetitor != null) { + selectBoatForCompetitor(selectedCompetitor); + } else { + boatTable.getSelectionModel().clear(); + } + } + + private void selectBoatInList(BoatDTO boat) { + boatTable.selectBoat(boat); + } + + private void selectBoatForCompetitor(CompetitorDTO selectedCompetitor) { + sailingService.getBoatLinkedToCompetitorForRace(leaderboardName, + raceColumnName, fleetName, selectedCompetitor.getIdAsString(), new MarkedAsyncCallback( + new AsyncCallback() { + @Override + public void onFailure(Throwable t) { + errorReporter.reportError("Error trying to determine boat linked to competitor " + + raceColumnName + " in leaderboard " + leaderboardName + ": " + + t.getMessage()); + } + + @Override + public void onSuccess(BoatDTO boat) { + // This method should select the linked boat. + // So you don't want to link or unlink it again throw the trackedRaceListHandler. + if (boat != null) { + selectBoatInList(boat); + } else { + boatTable.clearSelection(); + } + } + })); + } + + private CompetitorDTO getSelectedCompetitor() { + if (competitorTable.getSelectionModel().getSelectedSet().isEmpty()) { + return null; + } + return competitorTable.getSelectionModel().getSelectedSet().iterator().next(); + } +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompetitorWithBoatCreateDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompetitorWithBoatCreateDialog.java new file mode 100644 index 00000000000..6a57c5aa5a9 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompetitorWithBoatCreateDialog.java @@ -0,0 +1,129 @@ +package com.sap.sailing.gwt.ui.adminconsole; + +import java.util.Set; + +import com.google.gwt.event.logical.shared.ValueChangeEvent; +import com.google.gwt.event.logical.shared.ValueChangeHandler; +import com.google.gwt.user.client.ui.Grid; +import com.google.gwt.user.client.ui.HorizontalPanel; +import com.google.gwt.user.client.ui.Label; +import com.google.gwt.user.client.ui.RadioButton; +import com.google.gwt.user.client.ui.VerticalPanel; +import com.google.gwt.user.client.ui.Widget; +import com.google.gwt.view.client.SelectionChangeEvent; +import com.sap.sailing.domain.common.Distance; +import com.sap.sailing.domain.common.dto.BoatClassDTO; +import com.sap.sailing.domain.common.dto.BoatDTO; +import com.sap.sailing.domain.common.dto.CompetitorDTO; +import com.sap.sailing.gwt.ui.client.SailingServiceAsync; +import com.sap.sailing.gwt.ui.client.StringMessages; +import com.sap.sse.gwt.client.ErrorReporter; +import com.sap.sse.gwt.client.celltable.RefreshableSingleSelectionModel; + +/** + * A creation dialog for competitors with boats + * The dialog let you decide if you want to create a new boat or use an existing boat + * + */ +public class CompetitorWithBoatCreateDialog extends AbstractCompetitorWithBoatDialog { + + private final RadioButton useExistingBoatRadioButton; + private final RadioButton useNewBoatRadioButton; + private final VerticalPanel newBoatPanel; + private final VerticalPanel existingBoatPanel; + private final BoatTableWrapper> existingBoatsTable; + + /** + * The class creates the UI-dialog to type in the Data about a competitor. + * + * @param competitorToEdit + * The 'competitorToEdit' parameter contains the competitor which should be changed or initialized. + * @param boatClass + * The boat class is the default shown boat class for new boats. Set null if your boat is + * already initialized or you don't want a default boat class. + */ + public CompetitorWithBoatCreateDialog(SailingServiceAsync sailingService, StringMessages stringMessages, ErrorReporter errorReporter, CompetitorDTO competitorToEdit, + DialogCallback callback, String boatClass) { + super("Add competitor with boat", stringMessages, competitorToEdit, callback, boatClass); + this.ensureDebugId("CompetitorWithBoatCreateDialog"); + + this.useNewBoatRadioButton = this.createRadioButton("BoatCreationSelection", "Create new boat"); + this.useExistingBoatRadioButton = this.createRadioButton("BoatCreationSelection", "Use existing boat"); + this.useNewBoatRadioButton.setValue(true); + useNewBoatRadioButton.addValueChangeHandler(new ValueChangeHandler() { + @Override + public void onValueChange(ValueChangeEvent event) { + updateBoatPanels(); + } + }); + useExistingBoatRadioButton.addValueChangeHandler(new ValueChangeHandler() { + @Override + public void onValueChange(ValueChangeEvent event) { + updateBoatPanels(); + } + }); + newBoatPanel = new VerticalPanel(); + existingBoatPanel = new VerticalPanel(); + + this.existingBoatsTable = new BoatTableWrapper<>(sailingService, stringMessages, errorReporter, /* multiSelection */ false, + /* enablePager */ true, /* pagingSize*/ 10, false); + existingBoatsTable.refreshBoatList(true, /* callback */ null); + this.existingBoatsTable.getSelectionModel().addSelectionChangeHandler(new SelectionChangeEvent.Handler() { + + @Override + public void onSelectionChange(SelectionChangeEvent event) { + CompetitorWithBoatCreateDialog.this.validateAndUpdate(); + } + }); + } + + private void updateBoatPanels() { + boolean isUseExistingBoatSelected = useExistingBoatRadioButton.getValue(); + newBoatPanel.setVisible(!isUseExistingBoatSelected); + existingBoatPanel.setVisible(isUseExistingBoatSelected); + } + + @Override + protected BoatDTO getBoat() { + BoatDTO result = null; + boolean isUseExistingBoatSelected = useExistingBoatRadioButton.getValue(); + if (isUseExistingBoatSelected) { + Set selectedSet = existingBoatsTable.getSelectionModel().getSelectedSet(); + if (!selectedSet.isEmpty()) { + result = selectedSet.iterator().next(); + } + } else { + BoatClassDTO boatClass = new BoatClassDTO(boatClassNameTextBox.getValue(), Distance.NULL, Distance.NULL); + result = new BoatDTO(getCompetitorToEdit().getBoat().getIdAsString(), boatNameTextBox.getName(), boatClass, sailIdTextBox.getText()); + } + return result; + } + + @Override + protected Widget getAdditionalWidget() { + VerticalPanel result = new VerticalPanel(); + result.add(super.getAdditionalWidget()); + result.add(createHeadlineLabel(getStringMessages().boat())); + HorizontalPanel hPanel = new HorizontalPanel(); + hPanel.add(useNewBoatRadioButton); + hPanel.add(useExistingBoatRadioButton); + result.add(hPanel); + result.add(newBoatPanel); + result.add(existingBoatPanel); + Grid grid = new Grid(4, 2); + grid.setWidget(0, 0, new Label(getStringMessages().name())); + grid.setWidget(0, 1, boatNameTextBox); + grid.setWidget(1, 0, new Label(getStringMessages().sailNumber())); + grid.setWidget(1, 1, sailIdTextBox); + grid.setWidget(2, 0, new Label(getStringMessages().color())); + grid.setWidget(2, 1, boatDisplayColorTextBox); + grid.setWidget(3, 0, new Label(getStringMessages().boatClass())); + grid.setWidget(3, 1, boatClassNameTextBox); + result.add(grid); + newBoatPanel.add(grid); + existingBoatPanel.add(existingBoatsTable); + updateBoatPanels(); + return result; + } + +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompetitorWithBoatEditDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompetitorWithBoatEditDialog.java new file mode 100644 index 00000000000..bc4171025cb --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompetitorWithBoatEditDialog.java @@ -0,0 +1,72 @@ +package com.sap.sailing.gwt.ui.adminconsole; + +import com.google.gwt.user.client.ui.Grid; +import com.google.gwt.user.client.ui.Label; +import com.google.gwt.user.client.ui.VerticalPanel; +import com.google.gwt.user.client.ui.Widget; +import com.sap.sailing.domain.common.Distance; +import com.sap.sailing.domain.common.dto.BoatClassDTO; +import com.sap.sailing.domain.common.dto.BoatDTO; +import com.sap.sailing.domain.common.dto.CompetitorDTO; +import com.sap.sailing.gwt.ui.client.StringMessages; +import com.sap.sse.common.Color; +import com.sap.sse.common.impl.RGBColor; + +/** + * An edit for a competitor with a boat + */ +public class CompetitorWithBoatEditDialog extends AbstractCompetitorWithBoatDialog { + + /** + * The class creates the UI-dialog to type in the Data about a competitor. + * + * @param competitorToEdit + * The 'competitorToEdit' parameter contains the competitor which should be changed or initialized. + * @param boatClass + * The boat class is the default shown boat class for new boats. Set null if your boat is + * already initialized or you don't want a default boat class. + */ + public CompetitorWithBoatEditDialog(StringMessages stringMessages, CompetitorDTO competitorToEdit, + DialogCallback callback, String boatClass) { + super("Edit competitor with boat", stringMessages, competitorToEdit, callback, boatClass); + this.ensureDebugId("CompetitorWithBoatEditDialog"); + } + + @Override + protected BoatDTO getBoat() { + BoatDTO result = null; + Color boatColor; + if (boatDisplayColorTextBox.getValue() == null || boatDisplayColorTextBox.getValue().isEmpty()) { + boatColor = null; + } else { + try { + boatColor = new RGBColor(boatDisplayColorTextBox.getValue()); + } catch (IllegalArgumentException iae) { + boatColor = new InvalidColor(iae); + } + } + + BoatClassDTO boatClass = new BoatClassDTO(boatClassNameTextBox.getValue(), Distance.NULL, Distance.NULL); + result = new BoatDTO(getCompetitorToEdit().getBoat().getIdAsString(), boatNameTextBox.getValue(), boatClass, sailIdTextBox.getValue(), boatColor); + return result; + } + + @Override + protected Widget getAdditionalWidget() { + VerticalPanel result = new VerticalPanel(); + result.add(super.getAdditionalWidget()); + result.add(createHeadlineLabel(getStringMessages().boat())); + Grid grid = new Grid(4, 2); + grid.setWidget(0, 0, new Label(getStringMessages().name())); + grid.setWidget(0, 1, boatNameTextBox); + grid.setWidget(1, 0, new Label(getStringMessages().sailNumber())); + grid.setWidget(1, 1, sailIdTextBox); + grid.setWidget(2, 0, new Label(getStringMessages().boatClass())); + grid.setWidget(2, 1, boatClassNameTextBox); + grid.setWidget(3, 0, new Label(getStringMessages().color())); + grid.setWidget(3, 1, boatDisplayColorTextBox); + result.add(grid); + return result; + } + +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CreateRegattaCallback.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CreateRegattaCallback.java index aeb4a68096c..f4f84e696df 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CreateRegattaCallback.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CreateRegattaCallback.java @@ -64,7 +64,7 @@ public class CreateRegattaCallback implements DialogCallback{ seriesStructure.put(seriesDTO.getName(), seriesPair); } sailingService.createRegatta(newRegatta.getName(), newRegatta.boatClass==null?null:newRegatta.boatClass.getName(), - newRegatta.startDate, newRegatta.endDate, + newRegatta.canBoatsOfCompetitorsChangePerRace, newRegatta.startDate, newRegatta.endDate, new RegattaCreationParametersDTO(seriesStructure), true, newRegatta.scoringScheme, newRegatta.defaultCourseAreaUuid, newRegatta.buoyZoneRadiusInHullLengths, newRegatta.useStartTimeInference, newRegatta.controlTrackingFromStartAndFinishTimes, diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/DeviceMappingTableWrapper.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/DeviceMappingTableWrapper.java index e4338652df8..1acbad82626 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/DeviceMappingTableWrapper.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/DeviceMappingTableWrapper.java @@ -6,7 +6,7 @@ import com.google.gwt.dom.client.Style.Unit; import com.google.gwt.user.cellview.client.ColumnSortEvent.ListHandler; import com.google.gwt.user.cellview.client.TextColumn; import com.google.gwt.user.client.ui.Widget; -import com.sap.sailing.domain.common.dto.CompetitorDTO; +import com.sap.sailing.gwt.ui.client.MappableToDeviceFormatter; import com.sap.sailing.gwt.ui.client.SailingServiceAsync; import com.sap.sailing.gwt.ui.client.StringMessages; import com.sap.sailing.gwt.ui.common.client.DateAndTimeFormatterUtil; @@ -28,8 +28,7 @@ public class DeviceMappingTableWrapper extends TableWrapper itemTypeCol = new TextColumn() { @Override public String getValue(DeviceMappingDTO mapping) { - if (mapping.mappedTo instanceof CompetitorDTO) return stringMessages.competitor(); - else return stringMessages.mark(); + return MappableToDeviceFormatter.formatType(mapping.mappedTo, stringMessages); } }; itemTypeCol.setSortable(true); @@ -44,14 +43,15 @@ public class DeviceMappingTableWrapper extends TableWrapper itemCol = new TextColumn() { @Override public String getValue(DeviceMappingDTO mapping) { - return mapping.mappedTo.toString(); + return MappableToDeviceFormatter.formatName(mapping.mappedTo); } }; itemCol.setSortable(true); listHandler.setComparator(itemCol, new Comparator() { @Override public int compare(DeviceMappingDTO o1, DeviceMappingDTO o2) { - return o1.mappedTo.toString().compareTo(o2.mappedTo.toString()); + return MappableToDeviceFormatter.formatName(o1.mappedTo) + .compareTo(MappableToDeviceFormatter.formatName(o2.mappedTo)); } }); table.addColumn(itemCol, stringMessages.mappedTo()); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/ExpeditionAllInOneAfterImportHandler.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/ExpeditionAllInOneAfterImportHandler.java index 10f92033d3c..6bc6e4a358a 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/ExpeditionAllInOneAfterImportHandler.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/ExpeditionAllInOneAfterImportHandler.java @@ -93,6 +93,7 @@ public class ExpeditionAllInOneAfterImportHandler { private void showCompetitorRegistration() { new RegattaLogCompetitorRegistrationDialog(regatta.boatClass == null ? null : regatta.boatClass.getName(), sailingService, stringMessages, errorReporter, true, leaderboard.getName(), + leaderboard.canBoatsOfCompetitorsChangePerRace, new CancelImportDialogCallback>() { @Override public void ok(final Set competitors) { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/ImportCompetitorCallback.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/ImportCompetitorCallback.java index 42e7485fee1..6fdfed9b1b3 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/ImportCompetitorCallback.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/ImportCompetitorCallback.java @@ -55,7 +55,7 @@ public class ImportCompetitorCallback implements DialogCallback(existingCompetitorsSelected), new MarkedAsyncCallback<>( + sailingService.addOrUpdateCompetitors(new ArrayList<>(existingCompetitorsSelected), new MarkedAsyncCallback<>( new AsyncCallback>() { @Override public void onFailure(Throwable caught) { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/ItemToMapToDeviceSelectionPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/ItemToMapToDeviceSelectionPanel.java index 1e3c7928a86..1c716c1b1cb 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/ItemToMapToDeviceSelectionPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/ItemToMapToDeviceSelectionPanel.java @@ -10,6 +10,7 @@ import com.google.gwt.user.client.ui.Widget; import com.google.gwt.view.client.SelectionChangeEvent; import com.google.gwt.view.client.SelectionModel; import com.google.gwt.view.client.SingleSelectionModel; +import com.sap.sailing.domain.common.dto.BoatDTO; import com.sap.sailing.domain.common.dto.CompetitorDTO; import com.sap.sailing.domain.common.racelog.tracking.MappableToDevice; import com.sap.sailing.gwt.ui.client.SailingServiceAsync; @@ -20,6 +21,7 @@ import com.sap.sse.gwt.client.celltable.RefreshableSingleSelectionModel; public class ItemToMapToDeviceSelectionPanel implements IsWidget { private final CompetitorTableWrapper> competitorTable; + private final BoatTableWrapper> boatTable; private final MarkTableWrapper> markTable; private MappableToDevice selected; private final VerticalPanel mainPanel; @@ -27,6 +29,7 @@ public class ItemToMapToDeviceSelectionPanel implements IsWidget { static interface SelectionChangedHandler { void onSelectionChange(CompetitorDTO competitor); + void onSelectionChange(BoatDTO boat); void onSelectionChange(MarkDTO mark); } @@ -34,10 +37,11 @@ public class ItemToMapToDeviceSelectionPanel implements IsWidget { ErrorReporter errorReporter, final SelectionChangedHandler handler, MappableToDevice selected) { this.selected = selected; this.errorReporter = errorReporter; - competitorTable = new CompetitorTableWrapper<>(sailingService, stringMessages, errorReporter, /* multiSelection */ false, /* enablePager */ true); + competitorTable = new CompetitorTableWrapper<>(sailingService, stringMessages, errorReporter, /* multiSelection */ false, + /* enablePager */ true, /* filterCompetitorWithBoat */ false, /* filterCompetitorsWithoutBoat */ false); + boatTable = new BoatTableWrapper<>(sailingService, stringMessages, errorReporter, /* multiSelection */ false, /* enablePager */ true, /* allowActions */ false); markTable = new MarkTableWrapper>(/* multiSelection */ false, sailingService, stringMessages, errorReporter); - final SingleSelectionModel markSelectionModel = markTable.getSelectionModel(); competitorTable.getSelectionModel().addSelectionChangeHandler(new SelectionChangeEvent.Handler() { @Override public void onSelectionChange(SelectionChangeEvent event) { @@ -45,11 +49,26 @@ public class ItemToMapToDeviceSelectionPanel implements IsWidget { CompetitorDTO selectedCompetitor = competitorTable.getSelectionModel().getSelectedSet().iterator() .next(); ItemToMapToDeviceSelectionPanel.this.selected = selectedCompetitor; + deselectAll(boatTable.getSelectionModel(), boatTable.getDataProvider().getList()); deselectAll(markTable.getSelectionModel(), markTable.getDataProvider().getList()); handler.onSelectionChange(selectedCompetitor); } } }); + boatTable.getSelectionModel().addSelectionChangeHandler(new SelectionChangeEvent.Handler() { + @Override + public void onSelectionChange(SelectionChangeEvent event) { + if (boatTable.getSelectionModel().getSelectedSet().size() == 1) { + BoatDTO selectedBoat = boatTable.getSelectionModel().getSelectedSet().iterator() + .next(); + ItemToMapToDeviceSelectionPanel.this.selected = selectedBoat; + deselectAll(competitorTable.getSelectionModel(), competitorTable.getAllCompetitors()); + deselectAll(markTable.getSelectionModel(), markTable.getDataProvider().getList()); + handler.onSelectionChange(selectedBoat); + } + } + }); + final SingleSelectionModel markSelectionModel = markTable.getSelectionModel(); markSelectionModel.addSelectionChangeHandler(new SelectionChangeEvent.Handler() { @Override public void onSelectionChange(SelectionChangeEvent event) { @@ -57,6 +76,7 @@ public class ItemToMapToDeviceSelectionPanel implements IsWidget { MarkDTO selectedMark = markSelectionModel.getSelectedSet().iterator().next(); ItemToMapToDeviceSelectionPanel.this.selected = selectedMark; deselectAll(competitorTable.getSelectionModel(), competitorTable.getAllCompetitors()); + deselectAll(boatTable.getSelectionModel(), boatTable.getDataProvider().getList()); handler.onSelectionChange(selectedMark); } } @@ -65,9 +85,12 @@ public class ItemToMapToDeviceSelectionPanel implements IsWidget { this.mainPanel = new VerticalPanel(); CaptionPanel marksPanel = new CaptionPanel(stringMessages.mark()); CaptionPanel competitorsPanel = new CaptionPanel(stringMessages.competitor()); + CaptionPanel boatsPanel = new CaptionPanel(stringMessages.boat()); marksPanel.setContentWidget(markTable.asWidget()); competitorsPanel.setContentWidget(competitorTable.asWidget()); + boatsPanel.setContentWidget(boatTable.asWidget()); mainPanel.add(marksPanel); + mainPanel.add(boatsPanel); mainPanel.add(competitorsPanel); } @@ -108,6 +131,21 @@ public class ItemToMapToDeviceSelectionPanel implements IsWidget { }; } + public AsyncCallback> getSetBoatsCallback() { + return new AsyncCallback>() { + @Override + public void onSuccess(Collection result) { + boatTable.filterBoats(result); + select(result, boatTable.getSelectionModel()); + } + + @Override + public void onFailure(Throwable caught) { + errorReporter.reportError("Could not load boats: " + caught.getMessage()); + } + }; + } + public AsyncCallback> getSetMarksCallback() { return new AsyncCallback>() { @Override diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardConfigPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardConfigPanel.java index b89221f4047..7eccbb32bf7 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardConfigPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardConfigPanel.java @@ -32,6 +32,7 @@ import com.google.gwt.user.client.ui.Button; import com.google.gwt.user.client.ui.HasHorizontalAlignment; import com.google.gwt.user.client.ui.Panel; import com.google.gwt.view.client.ListDataProvider; +import com.sap.sailing.domain.common.LeaderboardNameConstants; import com.sap.sailing.domain.common.RegattaIdentifier; import com.sap.sailing.domain.common.RegattaName; import com.sap.sailing.domain.common.ScoringSchemeType; @@ -214,7 +215,15 @@ TrackedRaceChangedListener, LeaderboardsDisplayer { return new NaturalComparator().compare(o1.getDisplayName(), o2.getDisplayName()); } }); - + + + TextColumn leaderboardCanBoatsOfCompetitorsChangePerRaceColumn = new TextColumn() { + @Override + public String getValue(StrippedLeaderboardDTO leaderboard) { + return leaderboard.canBoatsOfCompetitorsChangePerRace ? stringMessages.yes() : stringMessages.no(); + } + }; + TextColumn discardingOptionsColumn = new TextColumn() { @Override public String getValue(StrippedLeaderboardDTO leaderboard) { @@ -320,7 +329,6 @@ TrackedRaceChangedListener, LeaderboardsDisplayer { } }); editCompetitorsDialog.show(); - } else if (LeaderboardConfigImagesBarCell.ACTION_CONFIGURE_URL.equals(value)) { openLeaderboardUrlConfigDialog(leaderboardDTO); } else if (LeaderboardConfigImagesBarCell.ACTION_EXPORT_XML.equals(value)) { @@ -341,6 +349,7 @@ TrackedRaceChangedListener, LeaderboardsDisplayer { leaderboardTable.addColumn(selectionCheckboxColumn, selectionCheckboxColumn.getHeader()); leaderboardTable.addColumn(linkColumn, stringMessages.name()); leaderboardTable.addColumn(leaderboardDisplayNameColumn, stringMessages.displayName()); + leaderboardTable.addColumn(leaderboardCanBoatsOfCompetitorsChangePerRaceColumn, stringMessages.canBoatsChange()); leaderboardTable.addColumn(discardingOptionsColumn, stringMessages.discarding()); leaderboardTable.addColumn(leaderboardTypeColumn, stringMessages.type()); leaderboardTable.addColumn(scoringSystemColumn, stringMessages.scoringSystem()); @@ -484,6 +493,8 @@ TrackedRaceChangedListener, LeaderboardsDisplayer { setEndTime(object.getA(), object.getB()); } else if (LeaderboardRaceConfigImagesBarCell.ACTION_SHOW_RACELOG.equals(value)) { showRaceLog(object.getA(), object.getB()); + } else if (LeaderboardRaceConfigImagesBarCell.ACTION_EDIT_COMPETITOR_TO_BOAT_MAPPINGS.equals(value)) { + editCompetitorToBoatMappings(object.getA(), object.getB()); } } }); @@ -602,6 +613,16 @@ TrackedRaceChangedListener, LeaderboardsDisplayer { dialog.show(); } + private void editCompetitorToBoatMappings(final RaceColumnDTO raceColumnDTO, final FleetDTO fleetDTO) { + final String selectedLeaderboardName = getSelectedLeaderboardName(); + final String raceColumnName = raceColumnDTO.getName(); + final String fleetName = fleetDTO.getName(); + final String raceName = LeaderboardNameConstants.DEFAULT_FLEET_NAME.equals(fleetName) ? raceColumnName : raceColumnName + ", " + fleetName; + ShowCompetitorToBoatMappingsDialog dialog = new ShowCompetitorToBoatMappingsDialog(sailingService, + stringMessages, errorReporter, selectedLeaderboardName, raceColumnName, fleetName, raceName); + dialog.center(); + } + private void setStartTime(RaceColumnDTO raceColumnDTO, FleetDTO fleetDTO) { new SetStartTimeDialog(sailingService, errorReporter, getSelectedLeaderboardName(), raceColumnDTO.getName(), fleetDTO.getName(), stringMessages, new DialogCallback() { @@ -1029,28 +1050,24 @@ TrackedRaceChangedListener, LeaderboardsDisplayer { public void ok(PairingListTemplateDTO editedObject) { BusyDialog busyDialog = new BusyDialog(); busyDialog.show(); - try { - sailingService.calculatePairingListTemplate(editedObject.getFlightCount(), editedObject.getGroupCount(), - editedObject.getCompetitorCount(), editedObject.getFlightMultiplier(), - new AsyncCallback() { + sailingService.calculatePairingListTemplate(editedObject.getFlightCount(), editedObject.getGroupCount(), + editedObject.getCompetitorCount(), editedObject.getFlightMultiplier(), + new AsyncCallback() { - @Override - public void onFailure(Throwable caught) { - busyDialog.hide(); - System.out.println(caught); - } + @Override + public void onFailure(Throwable caught) { + busyDialog.hide(); + errorReporter.reportError(stringMessages.errorCalculatingPairingListTemplate(caught.getMessage())); + } - @Override - public void onSuccess(PairingListTemplateDTO result) { - busyDialog.hide(); - result.setSelectedFlightNames(editedObject.getSelectedFlightNames()); - openPairingListCreationDialog(leaderboardDTO, result); - } + @Override + public void onSuccess(PairingListTemplateDTO result) { + busyDialog.hide(); + result.setSelectedFlightNames(editedObject.getSelectedFlightNames()); + openPairingListCreationDialog(leaderboardDTO, result); + } - }); - } catch (Exception exception) { - // TODO show error somehow - } + }); } @Override @@ -1062,7 +1079,7 @@ TrackedRaceChangedListener, LeaderboardsDisplayer { } private void openPairingListCreationDialog(StrippedLeaderboardDTO leaderboardDTO, PairingListTemplateDTO template) { - PairingListCreationDialog dialog = new PairingListCreationDialog(leaderboardDTO, stringMessages, template, sailingService); + PairingListCreationDialog dialog = new PairingListCreationDialog(leaderboardDTO, stringMessages, template, sailingService, errorReporter); dialog.show(); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardRaceConfigImagesBarCell.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardRaceConfigImagesBarCell.java index 6b1f391d941..f8205846133 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardRaceConfigImagesBarCell.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardRaceConfigImagesBarCell.java @@ -17,6 +17,7 @@ public class LeaderboardRaceConfigImagesBarCell extends ImagesBarCell { public final static String ACTION_SET_STARTTIME = "ACTION_SET_STARTTIME"; public final static String ACTION_SET_FINISHING_AND_FINISH_TIME = "ACTION_SET_FINISHING_AND_FINISH_TIME"; public final static String ACTION_SHOW_RACELOG = "ACTION_SHOW_RACELOG"; + public final static String ACTION_EDIT_COMPETITOR_TO_BOAT_MAPPINGS = "ACTION_EDIT_COMPETITOR_TO_BOAT_MAPPINGS"; private final StringMessages stringMessages; private final SelectedLeaderboardProvider selectedLeaderboardProvider; @@ -42,6 +43,11 @@ public class LeaderboardRaceConfigImagesBarCell extends ImagesBarCell { result.add(new ImageSpec(ACTION_SET_STARTTIME, stringMessages.setStartTime(), makeImagePrototype(resources.clockIcon()))); result.add(new ImageSpec(ACTION_SET_FINISHING_AND_FINISH_TIME, stringMessages.setFinishingAndFinishTime(), makeImagePrototype(resources.blueSmall()))); result.add(new ImageSpec(ACTION_SHOW_RACELOG, stringMessages.raceLog(), makeImagePrototype(resources.flagIcon()))); + + if (selectedLeaderboardProvider.getSelectedLeaderboard().canBoatsOfCompetitorsChangePerRace) { + result.add(new ImageSpec(ACTION_EDIT_COMPETITOR_TO_BOAT_MAPPINGS, stringMessages.actionShowCompetitorToBoatAssignments(), makeImagePrototype(resources.sailboatIcon()))); + } + return result; } } \ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/MatchImportedCompetitorsDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/MatchImportedCompetitorsDialog.java index 0a35dd94399..9579c1cf36c 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/MatchImportedCompetitorsDialog.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/MatchImportedCompetitorsDialog.java @@ -91,7 +91,7 @@ public class MatchImportedCompetitorsDialog extends DataEntryDialog(sailingService, stringMessages, errorReporter, /* multiSelection */ - false, /* enablePager */true); + false, /* enablePager */true, /* filterCompetitorWithBoat */ false, /* filterCompetitorsWithoutBoat */ false); final CompetitorsToImportToExistingLinking linker = new CompetitorsToImportToExistingLinking() { @Override public void unlinkCompetitor(CompetitorDescriptor competitor) { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/PairingListCreationDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/PairingListCreationDialog.java index 2bca6439723..02eb6b1da29 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/PairingListCreationDialog.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/PairingListCreationDialog.java @@ -23,6 +23,7 @@ import com.sap.sailing.gwt.ui.client.SailingServiceAsync; import com.sap.sailing.gwt.ui.client.StringMessages; import com.sap.sailing.gwt.ui.shared.StrippedLeaderboardDTO; import com.sap.sse.common.Util; +import com.sap.sse.gwt.client.ErrorReporter; import com.sap.sse.gwt.client.dialog.DataEntryDialog; public class PairingListCreationDialog extends DataEntryDialog { @@ -38,10 +39,13 @@ public class PairingListCreationDialog extends DataEntryDialog() { @Override public void onFailure(Throwable caught) { + PairingListCreationDialog.this.errorReporter.reportError(stringMessages.errorCreatingPairingList(caught.getMessage())); pairingListDTO = null; + applyToRacelogButton.setEnabled(false); + printPreViewButton.setEnabled(false); } @Override @@ -170,12 +177,12 @@ public class PairingListCreationDialog extends DataEntryDialog() { @Override public void onSuccess(Void result) { - /* TODO: log successfull */ + Window.alert(stringMessages.successfullyFilledRaceLogsFromPairingList()); } @Override public void onFailure(Throwable caught) { - caught.printStackTrace(); + errorReporter.reportError(stringMessages.errorFillingRaceLogsFromPairingList(caught.getMessage())); } }); } @@ -187,23 +194,17 @@ public class PairingListCreationDialog extends DataEntryDialog>() { @Override public void onFailure(Throwable caught) { - try { - throw caught; - } catch (Throwable e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } + errorReporter.reportError(stringMessages.errorFetchingRaceDisplayNamedFromLeaderboard(caught.getMessage())); } - public void onSuccess(List result) { - PairingListPreviewDialog dialog = new PairingListPreviewDialog(pairingListDTO, result, stringMessages,leaderboardDTO.getName()); + public void onSuccess(List raceDisplayNames) { + PairingListPreviewDialog dialog = new PairingListPreviewDialog(pairingListDTO, raceDisplayNames, stringMessages, leaderboardDTO.getName()); dialog.show(); }; }); } }); refreshButton.addClickHandler(new ClickHandler() { - @Override public void onClick(ClickEvent event) { getDialogBox().hide(); @@ -225,7 +226,7 @@ public class PairingListCreationDialog extends DataEntryDialog { private final StringMessages stringMessages; private final PairingListDTO pairingListDTO; - private final List fleetNames; + private final List raceDisplayNames; private final Button print; private final String leaderboardName; - public PairingListPreviewDialog(PairingListDTO pairingListDTO, List fleetNames, StringMessages stringMessages,String leaderboardName) { + public PairingListPreviewDialog(PairingListDTO pairingListDTO, List raceDisplayNames, StringMessages stringMessages,String leaderboardName) { super(stringMessages.pairingList() + " " + stringMessages.printView(), "", stringMessages.ok(), stringMessages.cancel(), null, null); this.stringMessages = stringMessages; this.pairingListDTO = pairingListDTO; - this.fleetNames = fleetNames; - this.leaderboardName=leaderboardName; - this.print= new Button(stringMessages.print()); + this.raceDisplayNames = raceDisplayNames; + this.leaderboardName = leaderboardName; + this.print = new Button(stringMessages.print()); this.print.addClickHandler(new ClickHandler() { - @Override public void onClick(ClickEvent event) { Widget pairingListPanel = getPairingListGrid(); printPairingListGrid( - "

" + "
" + "" + SafeHtmlUtils.fromString(leaderboardName) .asString() @@ -71,9 +73,9 @@ public class PairingListPreviewDialog extends DataEntryDialog { return scrollPanel; } - private Widget getPairingListGrid() { - final List boats = pairingListDTO.getBoats(); - + public Widget getPairingListGrid() { + final List boats = new ArrayList<>(pairingListDTO.getBoats()); + Collections.sort(boats, getBoatsComparator()); final int flightCount = pairingListDTO.getPairingList().size(); final int groupCount = pairingListDTO.getPairingList().get(0).size(); final int boatCount = boats.size(); @@ -82,29 +84,31 @@ public class PairingListPreviewDialog extends DataEntryDialog { pairingListGrid.getElement().setId("grid"); pairingListGrid.setCellPadding(15); pairingListGrid.getElement().setAttribute("style", "border-collapse: collapse"); - int flightIndexInGrid = 1; int groupIndex = 1; int boatIndex = 0; - for (BoatDTO boat : boats) { - pairingListGrid.setWidget(0, boatIndex + 2, new Label(boat.getName())); + pairingListGrid.setWidget(0, boatIndex + 2, new Label(getBoatDisplayName(boat))); pairingListGrid.getCellFormatter().getElement(0, boatIndex + 2).getStyle().setTextAlign(TextAlign.CENTER); pairingListGrid.getCellFormatter().getElement(0, boatIndex + 2).getStyle().setPadding(10, Unit.PX); if (boat.getColor() != null) { pairingListGrid.getCellFormatter().getElement(0, boatIndex + 2).getStyle().setBackgroundColor( boat.getColor().getAsHtml()); + if (isDark(boat.getColor())) { + pairingListGrid.getCellFormatter().getElement(0, boatIndex + 2).getStyle().setColor(Color.WHITE.getAsHtml()); + } } else { pairingListGrid.getCellFormatter().getElement(0, boatIndex + 2).getStyle().setBackgroundColor( "#cecece"); } boatIndex++; } - String color = ""; - pairingListGrid.getCellFormatter().getElement(0, 0).getStyle().setBackgroundColor("#cecece"); - pairingListGrid.getCellFormatter().getElement(0, 1).getStyle().setBackgroundColor("#cecece"); - for (List>> flight : pairingListDTO.getPairingList()) { - color = (color.equals("none") ? "#cecece" : "none"); + final String BACKGROUND_SHADE = "#cecece"; + String color = BACKGROUND_SHADE; + pairingListGrid.getCellFormatter().getElement(0, 0).getStyle().setBackgroundColor(BACKGROUND_SHADE); + pairingListGrid.getCellFormatter().getElement(0, 1).getStyle().setBackgroundColor(BACKGROUND_SHADE); + for (List>> flight : pairingListDTO.getPairingList()) { + color = (color.equals("none") ? BACKGROUND_SHADE : "none"); // setting up race int currentRaceInGridCells = (((flightIndexInGrid - 1) * groupCount) + 1); pairingListGrid.setWidget(currentRaceInGridCells, 0, @@ -112,38 +116,34 @@ public class PairingListPreviewDialog extends DataEntryDialog { pairingListGrid.getCellFormatter().getElement(currentRaceInGridCells, 0).getStyle().setPadding(5, Unit.PX); pairingListGrid.getCellFormatter().getElement(currentRaceInGridCells, 0).getStyle() .setBackgroundColor(color); - for (List> group : flight) { + for (List> group : flight) { // setting up fleet pairingListGrid.getCellFormatter().getElement(groupIndex, 0).getStyle().setPadding(3, Unit.PX); pairingListGrid.getCellFormatter().getElement(groupIndex, 0).getStyle().setBackgroundColor(color); - //TODO add column for race 1-45 (default) pairingListGrid.setWidget(groupIndex, 1, - new Label(fleetNames.get(groupIndex-1))); + new Label(raceDisplayNames.get(groupIndex-1))); // setting up fleets style pairingListGrid.getCellFormatter().getElement(groupIndex, 1).getStyle().setPadding(3, Unit.PX); pairingListGrid.getCellFormatter().getElement(groupIndex, 1).getStyle().setBackgroundColor(color); - if (group.size() < boatCount) { - List boatsToRemove = new ArrayList<>(boats); - for (Pair competitorAndBoatPair : group) { - boatsToRemove.remove(competitorAndBoatPair.getB()); + List unusedBoats = new ArrayList<>(boats); + for (Pair competitorAndBoatPair : group) { + unusedBoats.remove(competitorAndBoatPair.getB()); } - for (BoatDTO boat : boatsToRemove) { - group.add(new Pair(new CompetitorDTOImpl(), boat)); + for (BoatDTO unusedBoat : unusedBoats) { + group.add(new Pair<>(null, unusedBoat)); } } - - - for (Pair competitorAndBoatPair : group) { + for (Pair competitorAndBoatPair : group) { int boatIndexInGrid = boats.indexOf(competitorAndBoatPair.getB()) + 2; - if (competitorAndBoatPair.getA().getName() == null) { + if (competitorAndBoatPair.getA() == null) { pairingListGrid.setWidget(groupIndex, boatIndexInGrid, new Label(stringMessages.empty())); pairingListGrid.getCellFormatter().getElement(groupIndex, boatIndexInGrid).getStyle() .setColor(Color.RED.toString()); } else { - // TODO change competitor name to competitor shorthand symbol + final String shortName = competitorAndBoatPair.getA().getShortName(); pairingListGrid.setWidget(groupIndex, boatIndexInGrid, - new Label(competitorAndBoatPair.getA().getSailID())); + new Label(shortName == null ? competitorAndBoatPair.getA().getName() : shortName)); } pairingListGrid.getCellFormatter().getElement(groupIndex, boatIndexInGrid).getStyle() .setFontWeight(Style.FontWeight.BOLD); @@ -172,6 +172,24 @@ public class PairingListPreviewDialog extends DataEntryDialog { return pairingListPanel; } + private String getBoatDisplayName(BoatDTO boat) { + return boat.getName()==null?boat.getSailId():boat.getName(); + } + + /** + * Compare boats such that a natural ordering in the pairing list display is achieved. This + * is based on the natural comparator principle, using the string that will be displayed for the boats. + */ + private Comparator getBoatsComparator() { + final Comparator naturalComparator = new NaturalComparator(); + return (b1, b2)->naturalComparator.compare(getBoatDisplayName(b1), getBoatDisplayName(b2)); + } + + private boolean isDark(Color color) { + final Triple rgb = color.getAsRGB(); + return rgb.getA()+rgb.getB()+rgb.getC() < 100; + } + private native void printPairingListGrid(String pageHTMLContent) /*-{ var frameID = '__gwt_historyFrame'; var frame = $doc.getElementById(frameID); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceLogCompetitorRegistrationDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceLogCompetitorRegistrationDialog.java index d9445c1de51..73df6a8cc30 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceLogCompetitorRegistrationDialog.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceLogCompetitorRegistrationDialog.java @@ -94,18 +94,20 @@ public class RaceLogCompetitorRegistrationDialog extends AbstractCompetitorRegis } public RaceLogCompetitorRegistrationDialog(String boatClass, SailingServiceAsync sailingService, - StringMessages stringMessages, ErrorReporter errorReporter, boolean editable, String leaderboardName, + StringMessages stringMessages, ErrorReporter errorReporter, boolean editable, String leaderboardName, boolean canBoatsOfCompetitorsChangePerRace, String raceColumnName, String fleetName, List fleets, com.sap.sse.gwt.client.dialog.DataEntryDialog.DialogCallback> callback) { - this(sailingService, stringMessages, errorReporter, editable, callback, leaderboardName, boatClass, + this(sailingService, stringMessages, errorReporter, editable, callback, leaderboardName, canBoatsOfCompetitorsChangePerRace, boatClass, raceColumnName, fleetName, fleets, new Validator(stringMessages)); } public RaceLogCompetitorRegistrationDialog(SailingServiceAsync sailingService, StringMessages stringMessages, ErrorReporter errorReporter, boolean editable, com.sap.sse.gwt.client.dialog.DataEntryDialog.DialogCallback> callback, - String leaderboardName, String boatClass, String raceColumnName, String fleetName, List fleets, Validator validator) { - super(sailingService, stringMessages, errorReporter, editable, callback, leaderboardName, boatClass, validator); + String leaderboardName, boolean canBoatsOfCompetitorsChangePerRace, String boatClass, String raceColumnName, String fleetName, List fleets, Validator validator) { + super(sailingService, stringMessages, errorReporter, editable, callback, leaderboardName, + canBoatsOfCompetitorsChangePerRace, boatClass, + canBoatsOfCompetitorsChangePerRace ? stringMessages.actionContinueToBoatAssignment() : stringMessages.save(), validator); this.errorReporter = errorReporter; this.stringMessages = stringMessages; this.sailingService = sailingService; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceLogTrackingEventManagementImagesBarCell.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceLogTrackingEventManagementImagesBarCell.java index 8c5f4bf10f5..1cb38402845 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceLogTrackingEventManagementImagesBarCell.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceLogTrackingEventManagementImagesBarCell.java @@ -10,6 +10,7 @@ import com.sap.sailing.gwt.ui.client.shared.controls.ImagesBarCell; public class RaceLogTrackingEventManagementImagesBarCell extends ImagesBarCell { public static final String ACTION_DENOTE_FOR_RACELOG_TRACKING = "ACTION_DENOTE_FOR_RACELOG_TRACKING"; public final static String ACTION_COMPETITOR_REGISTRATIONS = "ACTION_COMPETITOR_REGISTRATIONS"; + public final static String ACTION_BOAT_REGISTRATIONS = "ACTION_BOAT_REGISTRATIONS"; public final static String ACTION_MAP_DEVICES = "ACTION_MAP_DEVICES"; public final static String ACTION_INVITE_BUOY_TENDERS = "ACTION_INVITE_BUOY_TENDERS"; public static final String ACTION_SHOW_REGATTA_LOG = "ACTION_SHOW_REGATTA_LOG"; @@ -34,6 +35,8 @@ public class RaceLogTrackingEventManagementImagesBarCell extends ImagesBarCell { makeImagePrototype(resources.denoteForRaceLogTracking()))); result.add(new ImageSpec(ACTION_COMPETITOR_REGISTRATIONS, stringMessages.competitorRegistrations(), makeImagePrototype(resources.competitorRegistrations()))); + result.add(new ImageSpec(ACTION_BOAT_REGISTRATIONS, stringMessages.boatRegistrations(), + makeImagePrototype(resources.boatRegistrations()))); result.add(new ImageSpec(ACTION_MAP_DEVICES, stringMessages.mapDevices(), makeImagePrototype(resources.mapDevices()))); result.add(new ImageSpec(ACTION_INVITE_BUOY_TENDERS, stringMessages.inviteBuoyTenders(), diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceLogTrackingEventManagementRaceImagesBarCell.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceLogTrackingEventManagementRaceImagesBarCell.java index 633b29b0386..1fac3107ba5 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceLogTrackingEventManagementRaceImagesBarCell.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceLogTrackingEventManagementRaceImagesBarCell.java @@ -24,7 +24,7 @@ public class RaceLogTrackingEventManagementRaceImagesBarCell extends ImagesBarCe public final static String ACTION_SET_TRACKING_TIMES = "ACTION_SET_TRACKING_TIMES"; public final static String ACTION_STOP_TRACKING = "ACTION_STOP_TRACKING"; public final static String ACTION_START_TRACKING = "ACTION_START_TRACKING"; - + public final static String ACTION_EDIT_COMPETITOR_TO_BOAT_MAPPINGS = "ACTION_EDIT_COMPETITOR_TO_BOAT_MAPPINGS"; private final StringMessages stringMessages; private SmartphoneTrackingEventManagementPanel smartphoneTrackingEventManagementPanel; @@ -64,6 +64,10 @@ public class RaceLogTrackingEventManagementRaceImagesBarCell extends ImagesBarCe } } + if (smartphoneTrackingEventManagementPanel.getSelectedLeaderboard().canBoatsOfCompetitorsChangePerRace) { + result.add(new ImageSpec(ACTION_EDIT_COMPETITOR_TO_BOAT_MAPPINGS, stringMessages.actionShowCompetitorToBoatAssignments(), makeImagePrototype(resources.sailboatIcon()))); + } + return result; } } \ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaDetailsComposite.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaDetailsComposite.java index 07bbcb5ba69..630c1ad08aa 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaDetailsComposite.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaDetailsComposite.java @@ -60,6 +60,7 @@ public class RegattaDetailsComposite extends Composite { private final Label defaultCourseArea; private final Label useStartTimeInference; private final Label controlTrackingFromStartAndFinishTimes; + private final Label canBoatsOfCompetitorsChangePerRace; private final Label configuration; private final Label buoyZoneRadiusInHullLengths; @@ -80,7 +81,7 @@ public class RegattaDetailsComposite extends Composite { VerticalPanel vPanel = new VerticalPanel(); mainPanel.add(vPanel); - int rows = 12; + int rows = 13; Grid grid = new Grid(rows, 2); vPanel.add(grid); @@ -93,6 +94,7 @@ public class RegattaDetailsComposite extends Composite { defaultCourseArea = createLabelAndValueWidget(grid, currentRow++, stringMessages.courseArea(), "CourseAreaLabel"); useStartTimeInference = createLabelAndValueWidget(grid, currentRow++, stringMessages.useStartTimeInference(), "UseStartTimeInferenceLabel"); controlTrackingFromStartAndFinishTimes = createLabelAndValueWidget(grid, currentRow++, stringMessages.controlTrackingFromStartAndFinishTimes(), "UseStartTimeInferenceLabel"); + canBoatsOfCompetitorsChangePerRace = createLabelAndValueWidget(grid, currentRow++, stringMessages.canBoatsOfCompetitorsChangePerRace(), "CanBoatsOfCompetitorsChangePerRaceLabel"); buoyZoneRadiusInHullLengths = createLabelAndValueWidget(grid, currentRow++, stringMessages.buoyZoneRadiusInHullLengths(), "BuoyZoneRadiusInHullLengthsLabel"); configuration = createLabelAndValueWidget(grid, currentRow++, stringMessages.racingProcedureConfiguration(), "RacingProcedureLabel"); scoringSystem = createLabelAndValueWidget(grid, currentRow++, stringMessages.scoringSystem(), "ScoringSystemLabel"); @@ -402,6 +404,7 @@ public class RegattaDetailsComposite extends Composite { defaultCourseArea.setText(regatta.defaultCourseAreaUuid == null ? "" : regatta.defaultCourseAreaName); useStartTimeInference.setText(regatta.useStartTimeInference ? stringMessages.yes() : stringMessages.no()); controlTrackingFromStartAndFinishTimes.setText(regatta.controlTrackingFromStartAndFinishTimes ? stringMessages.yes() : stringMessages.no()); + canBoatsOfCompetitorsChangePerRace.setText(regatta.canBoatsOfCompetitorsChangePerRace ? stringMessages.yes() : stringMessages.no()); buoyZoneRadiusInHullLengths.setText(String.valueOf(regatta.buoyZoneRadiusInHullLengths)); if (regatta.configuration != null) { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaLeaderboardWithEliminationsDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaLeaderboardWithEliminationsDialog.java index d3941c878fb..a56470909f6 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaLeaderboardWithEliminationsDialog.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaLeaderboardWithEliminationsDialog.java @@ -124,7 +124,7 @@ public abstract class RegattaLeaderboardWithEliminationsDialog extends AbstractL final CompetitorRegistrationsPanel[] competitorEliminationPanel = new CompetitorRegistrationsPanel[1]; competitorEliminationPanel[0] = new CompetitorRegistrationsPanel(sailingService, stringMessages, errorReporter, /* editable */ true, regattaLeaderboardsListBox.getValue(regattaLeaderboardsListBox.getSelectedIndex()), - selectedRegattaLeaderboard.boatClassName, + selectedRegattaLeaderboard.canBoatsOfCompetitorsChangePerRace, selectedRegattaLeaderboard.boatClassName, /* "validator" updates eliminatedCompetitors */ ()->eliminatedCompetitors = competitorEliminationPanel[0].getResult(), getEliminatedCompetitorsRetriever(), /* restrictPoolToLeaderboard */ true, /* additionalWidgetsBeforeTables */ new Label(stringMessages.selectCompetitorsToEliminate())); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaListComposite.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaListComposite.java index 547852a8709..c16e13b9059 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaListComposite.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaListComposite.java @@ -163,6 +163,13 @@ public class RegattaListComposite extends Composite implements RegattasDisplayer } }); + TextColumn regattaCanBoatsOfCompetitorsChangePerRaceColumn = new TextColumn() { + @Override + public String getValue(RegattaDTO regatta) { + return regatta.canBoatsOfCompetitorsChangePerRace ? stringMessages.yes() : stringMessages.no(); + } + }; + TextColumn startEndDateColumn = new TextColumn() { @Override public String getValue(RegattaDTO regatta) { @@ -233,6 +240,7 @@ public class RegattaListComposite extends Composite implements RegattasDisplayer table.addColumn(regattaSelectionCheckboxColumn, regattaSelectionCheckboxColumn.getHeader()); table.addColumn(regattaNameColumn, stringMessages.regattaName()); + table.addColumn(regattaCanBoatsOfCompetitorsChangePerRaceColumn, stringMessages.canBoatsChange()); table.addColumn(startEndDateColumn, stringMessages.from() + "/" + stringMessages.to()); table.addColumn(regattaBoatClassColumn, stringMessages.boatClass()); table.addColumn(rankingMetricColumn, stringMessages.rankingMetric()); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaLogAddDeviceMappingDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaLogAddDeviceMappingDialog.java index aeacfe7a1c0..8fcc1aacb54 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaLogAddDeviceMappingDialog.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaLogAddDeviceMappingDialog.java @@ -15,6 +15,7 @@ import com.google.gwt.user.client.ui.ListBox; import com.google.gwt.user.client.ui.TextBox; import com.google.gwt.user.client.ui.VerticalPanel; import com.google.gwt.user.client.ui.Widget; +import com.sap.sailing.domain.common.dto.BoatDTO; import com.sap.sailing.domain.common.dto.CompetitorDTO; import com.sap.sailing.domain.common.racelog.tracking.DeviceMappingConstants; import com.sap.sailing.domain.common.racelog.tracking.MappableToDevice; @@ -126,6 +127,14 @@ public class RegattaLogAddDeviceMappingDialog extends DataEntryDialogWithDateTim competitor.getIdAsString()); validateAndUpdate(); } + + @Override + public void onSelectionChange(BoatDTO boat) { + selectedItem = boat; + qrWidget.setMappedItem(DeviceMappingConstants.URL_BOAT_ID_AS_STRING, + boat.getIdAsString()); + validateAndUpdate(); + } }, mapping != null ? mapping.mappedTo : null); if (mapping != null) { deviceId.setValue(mapping.deviceIdentifier.deviceId); @@ -135,7 +144,7 @@ public class RegattaLogAddDeviceMappingDialog extends DataEntryDialogWithDateTim qrWidget = setupQRCodeWidget(); qrWidget.generateQRCode(); this.leaderboardName = leaderboardName; - loadCompetitorsAndMarks(); + loadCompetitorsBoatsAndMarks(); events = new GenericListBox(new ValueBuilder() { @Override public String getValue(EventDTO item) { @@ -216,8 +225,9 @@ public class RegattaLogAddDeviceMappingDialog extends DataEntryDialogWithDateTim return new DeviceMappingDTO(deviceIdentifier, from.getValue(), to.getValue(), selectedItem, null); } - private void loadCompetitorsAndMarks() { + private void loadCompetitorsBoatsAndMarks() { sailingService.getCompetitorRegistrationsForLeaderboard(leaderboardName, itemSelectionPanel.getSetCompetitorsCallback()); + sailingService.getBoatRegistrationsForLeaderboard(leaderboardName, itemSelectionPanel.getSetBoatsCallback()); sailingService.getMarksInRegattaLog(leaderboardName, itemSelectionPanel.getSetMarksCallback()); } } \ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaLogBoatRegistrationDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaLogBoatRegistrationDialog.java new file mode 100644 index 00000000000..690413013c2 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaLogBoatRegistrationDialog.java @@ -0,0 +1,59 @@ +package com.sap.sailing.gwt.ui.adminconsole; + +import java.util.Collection; +import java.util.Set; +import java.util.function.Consumer; + +import com.google.gwt.user.client.rpc.AsyncCallback; +import com.google.gwt.user.client.ui.Widget; +import com.sap.sailing.domain.common.dto.BoatDTO; +import com.sap.sailing.gwt.ui.client.SailingServiceAsync; +import com.sap.sailing.gwt.ui.client.StringMessages; +import com.sap.sse.gwt.client.ErrorReporter; +import com.sap.sse.gwt.client.dialog.DataEntryDialog; + +public class RegattaLogBoatRegistrationDialog extends DataEntryDialog> { + protected final ErrorReporter errorReporter; + protected final SailingServiceAsync sailingService; + protected final StringMessages stringMessages; + protected final String leaderboardName; + protected final boolean canBoatsOfCompetitorsChangePerRace; + protected final BoatRegistrationsPanel boatRegistrationsPanel; + + public RegattaLogBoatRegistrationDialog(String boatClass, SailingServiceAsync sailingService, + StringMessages stringMessages, ErrorReporter errorReporter, boolean editable, String leaderboardName, boolean canBoatsOfCompetitorsChangePerRace, + com.sap.sse.gwt.client.dialog.DataEntryDialog.DialogCallback> callback) { + super(stringMessages.registerBoats(), /* messsage */null, stringMessages.save(), stringMessages.cancel(), + /* validator */ null, callback); + this.errorReporter = errorReporter; + this.stringMessages = stringMessages; + this.sailingService = sailingService; + this.leaderboardName = leaderboardName; + this.canBoatsOfCompetitorsChangePerRace = canBoatsOfCompetitorsChangePerRace; + this.boatRegistrationsPanel = new BoatRegistrationsPanel(sailingService, stringMessages, errorReporter, editable, leaderboardName, + canBoatsOfCompetitorsChangePerRace, boatClass, ()->validateAndUpdate(), getRegisteredBoatsRetriever(), /* restrictPoolToLeaderboard */ false); + } + + protected Consumer>> getRegisteredBoatsRetriever() { + return (callback)->getRegisteredBoats(callback); + } + + private void getRegisteredBoats(AsyncCallback> callback) { + if (boatRegistrationsPanel.showOnlyBoatsOfLog()) { + sailingService.getBoatRegistrationsInRegattaLog(leaderboardName, callback); + } else { + sailingService.getBoatRegistrationsForLeaderboard(leaderboardName, callback); + } + } + + @Override + protected Widget getAdditionalWidget() { + return boatRegistrationsPanel; + } + + @Override + protected Set getResult() { + return boatRegistrationsPanel.getResult(); + } + +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaLogCompetitorRegistrationDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaLogCompetitorRegistrationDialog.java index 74dd186eec5..bd79b45613f 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaLogCompetitorRegistrationDialog.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaLogCompetitorRegistrationDialog.java @@ -14,9 +14,9 @@ import com.sap.sse.gwt.client.ErrorReporter; public class RegattaLogCompetitorRegistrationDialog extends AbstractCompetitorRegistrationDialog { public RegattaLogCompetitorRegistrationDialog(String boatClass, SailingServiceAsync sailingService, - StringMessages stringMessages, ErrorReporter errorReporter, boolean editable, String leaderboardName, + StringMessages stringMessages, ErrorReporter errorReporter, boolean editable, String leaderboardName, boolean canBoatsOfCompetitorsChangePerRace, com.sap.sse.gwt.client.dialog.DataEntryDialog.DialogCallback> callback) { - super(sailingService, stringMessages, errorReporter, editable, callback, leaderboardName, boatClass, /* validator */ null); + super(sailingService, stringMessages, errorReporter, editable, callback, leaderboardName, canBoatsOfCompetitorsChangePerRace, boatClass, stringMessages.save(), /* validator */ null); } @Override diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaLogImportFixesAndAddMappingsDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaLogImportFixesAndAddMappingsDialog.java old mode 100644 new mode 100755 diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaLogImportSensorDataAndAddMappingsDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaLogImportSensorDataAndAddMappingsDialog.java old mode 100644 new mode 100755 index 8490485b847..0535dbc9144 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaLogImportSensorDataAndAddMappingsDialog.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaLogImportSensorDataAndAddMappingsDialog.java @@ -8,14 +8,14 @@ import com.sap.sailing.gwt.ui.shared.TypedDeviceMappingDTO; import com.sap.sse.gwt.client.ErrorReporter; public class RegattaLogImportSensorDataAndAddMappingsDialog extends AbstractRegattaLogSensorDataAddMappingsDialog { + private final SensorDataImportWidget importWidget; public RegattaLogImportSensorDataAndAddMappingsDialog(SailingServiceAsync sailingService, final ErrorReporter errorReporter, final StringMessages stringMessages, String leaderboardName, DialogCallback> callback) { super(sailingService, errorReporter, stringMessages, leaderboardName, callback); - - importWidget = new SensorDataImportWidget(deviceIdTable, stringMessages, sailingService, errorReporter); + this.importWidget = new SensorDataImportWidget(deviceIdTable, stringMessages, sailingService, errorReporter); setImportWidget(importWidget); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaLogSensorDataAddMappingsDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaLogSensorDataAddMappingsDialog.java index 7bce9fa2fe3..9082746b5b3 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaLogSensorDataAddMappingsDialog.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaLogSensorDataAddMappingsDialog.java @@ -9,6 +9,7 @@ import com.sap.sailing.gwt.ui.shared.TypedDeviceMappingDTO; import com.sap.sse.gwt.client.ErrorReporter; public class RegattaLogSensorDataAddMappingsDialog extends AbstractRegattaLogSensorDataAddMappingsDialog { + private final String importerType; public RegattaLogSensorDataAddMappingsDialog(SailingServiceAsync sailingService, @@ -17,7 +18,6 @@ public class RegattaLogSensorDataAddMappingsDialog extends AbstractRegattaLogSen DialogCallback> callback) { super(sailingService, errorReporter, stringMessages, leaderboardName, callback); this.importerType = importerType; - deviceIdTable.getDataProvider().getList().addAll(importedDeviceIds); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaLogTrackingDeviceMappingsDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaLogTrackingDeviceMappingsDialog.java index adf3fffae0f..f14ceeb5296 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaLogTrackingDeviceMappingsDialog.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaLogTrackingDeviceMappingsDialog.java @@ -35,11 +35,11 @@ import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.VerticalPanel; import com.google.gwt.user.client.ui.Widget; import com.google.gwt.view.client.CellPreviewEvent; +import com.sap.sailing.gwt.ui.client.MappableToDeviceFormatter; import com.sap.sailing.gwt.ui.client.SailingServiceAsync; import com.sap.sailing.gwt.ui.client.StringMessages; import com.sap.sailing.gwt.ui.common.client.DateAndTimeFormatterUtil; import com.sap.sailing.gwt.ui.shared.DeviceMappingDTO; -import com.sap.sailing.gwt.ui.shared.MarkDTO; import com.sap.sailing.gwt.ui.shared.TypedDeviceMappingDTO; import com.sap.sse.common.filter.Filter; import com.sap.sse.gwt.client.ErrorReporter; @@ -159,10 +159,9 @@ public class RegattaLogTrackingDeviceMappingsDialog extends DataEntryDialog" + stringMessages.device() + ": " + mapping.deviceIdentifier.deviceType + " - " + mapping.deviceIdentifier.deviceId + "
" + - "" + stringMessages.mappedTo() + ": " + itemType + " - " + mapping.mappedTo + "
" + + "" + stringMessages.mappedTo() + ": " + itemType + " - " + MappableToDeviceFormatter.formatName(mapping.mappedTo) + "
" + "" + stringMessages.from() + ": " + DateAndTimeFormatterUtil.formatDateAndTime(mapping.from) + "
" + "" + stringMessages.to() + ": " + DateAndTimeFormatterUtil.formatDateAndTime(mapping.to); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaWithSeriesAndFleetsDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaWithSeriesAndFleetsDialog.java index 34611a7d115..cda13ec5d28 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaWithSeriesAndFleetsDialog.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaWithSeriesAndFleetsDialog.java @@ -2,6 +2,7 @@ package com.sap.sailing.gwt.ui.adminconsole; import java.util.List; +import com.google.gwt.user.client.ui.CheckBox; import com.google.gwt.user.client.ui.Grid; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.SuggestBox; @@ -20,6 +21,7 @@ import com.sap.sse.gwt.client.controls.listedit.ListEditorComposite; public abstract class RegattaWithSeriesAndFleetsDialog extends AbstractRegattaWithSeriesAndFleetsDialog { protected TextBox nameEntryField; protected SuggestBox boatClassEntryField; + protected CheckBox canBoatsOfCompetitorsChangePerRaceCheckBox; public RegattaWithSeriesAndFleetsDialog(RegattaDTO regatta, Iterable series, List existingEvents, EventDTO defaultEvent, String title, String okButton, StringMessages stringMessages, @@ -35,6 +37,9 @@ public abstract class RegattaWithSeriesAndFleetsDialog extends AbstractRegattaWi if (regatta.boatClass != null) { boatClassEntryField.setText(regatta.boatClass.getName()); } + canBoatsOfCompetitorsChangePerRaceCheckBox = createCheckbox(""); + canBoatsOfCompetitorsChangePerRaceCheckBox.ensureDebugId("CanBoatsOfCompetitorsChangePerRaceCheckBox"); + canBoatsOfCompetitorsChangePerRaceCheckBox.setValue(regatta.canBoatsOfCompetitorsChangePerRace); } @Override @@ -49,16 +54,20 @@ public abstract class RegattaWithSeriesAndFleetsDialog extends AbstractRegattaWi RegattaDTO result = getRegattaDTO(); result.setName(nameEntryField.getText().trim()); // trim to particularly avoid trailing blanks result.boatClass = new BoatClassDTO(boatClassEntryField.getText(), Distance.NULL, Distance.NULL); + result.canBoatsOfCompetitorsChangePerRace = canBoatsOfCompetitorsChangePerRaceCheckBox.getValue(); return result; } @Override protected void setupAdditionalWidgetsOnPanel(VerticalPanel panel, Grid formGrid) { + formGrid.insertRow(0); formGrid.insertRow(0); formGrid.insertRow(0); formGrid.setWidget(0, 0, new Label(stringMessages.name() + ":")); formGrid.setWidget(0, 1, nameEntryField); formGrid.setWidget(1, 0, new Label(stringMessages.boatClass() + ":")); formGrid.setWidget(1, 1, boatClassEntryField); + formGrid.setWidget(2, 0, new Label(stringMessages.canBoatsOfCompetitorsChangePerRace() + ":")); + formGrid.setWidget(2, 1, canBoatsOfCompetitorsChangePerRaceCheckBox); } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaWithSeriesAndFleetsEditDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaWithSeriesAndFleetsEditDialog.java index c0a0fac5a19..f48ece808f9 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaWithSeriesAndFleetsEditDialog.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaWithSeriesAndFleetsEditDialog.java @@ -40,6 +40,7 @@ public class RegattaWithSeriesAndFleetsEditDialog extends RegattaWithSeriesAndFl nameEntryField.setEnabled(false); boatClassEntryField.setEnabled(false); + canBoatsOfCompetitorsChangePerRaceCheckBox.setEnabled(false); scoringSchemeListBox.setEnabled(false); sailingEventsListBox.setEnabled(true); courseAreaListBox.setEnabled(true); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SensorDataImportWidget.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SensorDataImportWidget.java index 5cb8f4aee30..7a10bee9100 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SensorDataImportWidget.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SensorDataImportWidget.java @@ -14,7 +14,6 @@ public class SensorDataImportWidget extends AbstractFileImportWidget implements final SailingServiceAsync sailingService, final ErrorReporter errorReporter) { super("/sailingserver/sensordata/import", stringMessages, table, sailingService, errorReporter); setMultipleFileUploadEnabled(true); - setShouldClearListOnNewUploadComplete(false); setDownsampleOptionVisible(true); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/ShowCompetitorToBoatMappingsDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/ShowCompetitorToBoatMappingsDialog.java new file mode 100644 index 00000000000..7190a033079 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/ShowCompetitorToBoatMappingsDialog.java @@ -0,0 +1,41 @@ +package com.sap.sailing.gwt.ui.adminconsole; + +import com.google.gwt.event.dom.client.ClickEvent; +import com.google.gwt.event.dom.client.ClickHandler; +import com.google.gwt.user.client.ui.Button; +import com.google.gwt.user.client.ui.DialogBox; +import com.google.gwt.user.client.ui.Label; +import com.google.gwt.user.client.ui.VerticalPanel; +import com.sap.sailing.gwt.ui.client.SailingServiceAsync; +import com.sap.sailing.gwt.ui.client.StringMessages; +import com.sap.sse.gwt.client.ErrorReporter; + +public class ShowCompetitorToBoatMappingsDialog extends DialogBox { + + public ShowCompetitorToBoatMappingsDialog(final SailingServiceAsync sailingService, final StringMessages stringMessages, + final ErrorReporter errorReporter, String leaderboardName, final String raceColumnName, final String fleetName, + String raceName) { + super(); + + setText(stringMessages.actionShowCompetitorToBoatAssignments()); + setAnimationEnabled(true); + setGlassEnabled(true); + + Button okButton = new Button(stringMessages.ok()); + okButton.addClickHandler(new ClickHandler() { + public void onClick(ClickEvent event) { + ShowCompetitorToBoatMappingsDialog.this.hide(); + } + }); + + VerticalPanel vPanel = new VerticalPanel(); + CompetitorToBoatMappingsViewPanel competitorPanel = new CompetitorToBoatMappingsViewPanel(sailingService, + stringMessages, errorReporter, leaderboardName, raceColumnName, fleetName); + + vPanel.add(new Label(stringMessages.race() + ": " + raceName)); + vPanel.add(competitorPanel); + vPanel.add(okButton); + + setWidget(vPanel); + } +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SmartphoneTrackingEventManagementPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SmartphoneTrackingEventManagementPanel.java index f76051c5ba1..362d978711d 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SmartphoneTrackingEventManagementPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SmartphoneTrackingEventManagementPanel.java @@ -9,7 +9,6 @@ import java.util.Map; import java.util.Set; import com.google.gwt.cell.client.FieldUpdater; -import com.google.gwt.core.shared.GWT; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.i18n.client.LocaleInfo; @@ -33,12 +32,14 @@ import com.google.gwt.view.client.SelectionChangeEvent; import com.google.gwt.view.client.SelectionChangeEvent.Handler; import com.sap.sailing.domain.common.CourseDesignerMode; import com.sap.sailing.domain.common.Distance; +import com.sap.sailing.domain.common.LeaderboardNameConstants; import com.sap.sailing.domain.common.PassingInstruction; import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; import com.sap.sailing.domain.common.RegattaIdentifier; import com.sap.sailing.domain.common.RegattaName; import com.sap.sailing.domain.common.TrackedRaceStatusEnum; import com.sap.sailing.domain.common.abstractlog.TimePointSpecificationFoundInLog; +import com.sap.sailing.domain.common.dto.BoatDTO; import com.sap.sailing.domain.common.dto.CompetitorDTO; import com.sap.sailing.domain.common.dto.FleetDTO; import com.sap.sailing.domain.common.dto.RaceColumnDTO; @@ -109,10 +110,6 @@ public class SmartphoneTrackingEventManagementPanel extends AbstractLeaderboardC }); } - private static interface ShowWithBoatClass { - void showWithBoatClass(String boatClassName); - } - /** * When doing race log tracking, the Remove and Stop Tracking buttons are required. */ @@ -156,6 +153,13 @@ public class SmartphoneTrackingEventManagementPanel extends AbstractLeaderboardC } }); + TextColumn leaderboardCanBoatsOfCompetitorsChangePerRaceColumn = new TextColumn() { + @Override + public String getValue(StrippedLeaderboardDTO leaderboard) { + return leaderboard.canBoatsOfCompetitorsChangePerRace ? stringMessages.yes() : stringMessages.no(); + } + }; + ImagesBarColumn leaderboardActionColumn = new ImagesBarColumn( new RaceLogTrackingEventManagementImagesBarCell(stringMessages)); @@ -163,42 +167,69 @@ public class SmartphoneTrackingEventManagementPanel extends AbstractLeaderboardC @Override public void update(int index, StrippedLeaderboardDTO leaderboardDTO, String value) { final String leaderboardName = leaderboardDTO.name; + final boolean canBoatsOfCompetitorsChangePerRace = leaderboardDTO.canBoatsOfCompetitorsChangePerRace; if (RaceLogTrackingEventManagementImagesBarCell.ACTION_DENOTE_FOR_RACELOG_TRACKING.equals(value)) { denoteForRaceLogTracking(leaderboardDTO); } else if (RaceLogTrackingEventManagementImagesBarCell.ACTION_COMPETITOR_REGISTRATIONS.equals(value)) { - ShowWithBoatClass showWithBoatClass = new ShowWithBoatClass() { - @Override - public void showWithBoatClass(String boatClassName) { - new RegattaLogCompetitorRegistrationDialog(boatClassName, sailingService, stringMessages, - errorReporter, /* editable */true, leaderboardName, - new DialogCallback>() { - @Override - public void ok(Set registeredCompetitors) { - sailingService.setCompetitorRegistrationsInRegattaLog(leaderboardName, - registeredCompetitors, new AsyncCallback() { - @Override - public void onSuccess(Void result) { - // pass - } + RegattaDTO regatta = getSelectedRegatta(); + String boatClassName = regatta.boatClass.getName(); - @Override - public void onFailure(Throwable caught) { - errorReporter - .reportError("Could not save competitor registrations: " - + caught.getMessage()); - } - }); - } + new RegattaLogCompetitorRegistrationDialog(boatClassName, sailingService, stringMessages, + errorReporter, /* editable */true, leaderboardName, canBoatsOfCompetitorsChangePerRace, + new DialogCallback>() { + @Override + public void ok(Set registeredCompetitors) { + sailingService.setCompetitorRegistrationsInRegattaLog(leaderboardName, + registeredCompetitors, new AsyncCallback() { + @Override + public void onSuccess(Void result) { + // pass + } - @Override - public void cancel() { + @Override + public void onFailure(Throwable caught) { + errorReporter + .reportError("Could not save competitor registrations: " + + caught.getMessage()); + } + }); + } - } - }).show(); - } + @Override + public void cancel() { + } + }).show(); + } else if (RaceLogTrackingEventManagementImagesBarCell.ACTION_BOAT_REGISTRATIONS.equals(value)) { + if (canBoatsOfCompetitorsChangePerRace) { + RegattaDTO regatta = getSelectedRegatta(); + String boatClassName = regatta.boatClass.getName(); + + new RegattaLogBoatRegistrationDialog(boatClassName, sailingService, stringMessages, + errorReporter, /* editable */true, leaderboardName, canBoatsOfCompetitorsChangePerRace, + new DialogCallback>() { + @Override + public void ok(Set registeredBoats) { + sailingService.setBoatRegistrationsInRegattaLog(leaderboardName, + registeredBoats, new AsyncCallback() { + @Override + public void onSuccess(Void result) { + // pass + } - }; - searchBoatClass(showWithBoatClass); + @Override + public void onFailure(Throwable caught) { + errorReporter.reportError("Could not save boat registrations: " + caught.getMessage()); + } + }); + } + + @Override + public void cancel() { + } + }).show(); + } else { + Window.alert(stringMessages.canNotRegisterBoats()); + } } else if (RaceLogTrackingEventManagementImagesBarCell.ACTION_MAP_DEVICES.equals(value)) { new RegattaLogTrackingDeviceMappingsDialog(sailingService, stringMessages, errorReporter, leaderboardName, new DialogCallback() { @@ -222,6 +253,7 @@ public class SmartphoneTrackingEventManagementPanel extends AbstractLeaderboardC leaderboardTable.addColumn(selectionCheckboxColumn, selectionCheckboxColumn.getHeader()); leaderboardTable.addColumn(leaderboardNameColumn, stringMessages.name()); leaderboardTable.addColumn(leaderboardDisplayNameColumn, stringMessages.displayName()); + leaderboardTable.addColumn(leaderboardCanBoatsOfCompetitorsChangePerRaceColumn, stringMessages.canBoatsChange()); leaderboardTable.addColumn(leaderboardActionColumn, stringMessages.actions()); leaderboardTable.addColumnSortHandler(leaderboardColumnListHandler); leaderboardTable.setSelectionModel(selectionCheckboxColumn.getSelectionModel(), selectionCheckboxColumn.getSelectionManager()); @@ -289,6 +321,7 @@ public class SmartphoneTrackingEventManagementPanel extends AbstractLeaderboardC @Override public void update(int index, final RaceColumnDTOAndFleetDTOWithNameBasedEquality raceColumnDTOAndFleetDTO, String value) { final String leaderboardName = getSelectedLeaderboardName(); + final boolean canBoatsOfCompetitorsChangePerRace = canBoatsOfCompetitorsChangePerRace(); final String raceColumnName = raceColumnDTOAndFleetDTO.getA().getName(); final String fleetName = raceColumnDTOAndFleetDTO.getB().getName(); final boolean editable = ! (doesTrackerExist(raceColumnDTOAndFleetDTO) && @@ -297,41 +330,9 @@ public class SmartphoneTrackingEventManagementPanel extends AbstractLeaderboardC denoteForRaceLogTracking(raceColumnDTOAndFleetDTO.getA(), raceColumnDTOAndFleetDTO.getB()); } else if (RaceLogTrackingEventManagementRaceImagesBarCell.ACTION_REMOVE_DENOTATION.equals(value)) { removeDenotation(raceColumnDTOAndFleetDTO.getA(), raceColumnDTOAndFleetDTO.getB()); - } else if (RaceLogTrackingEventManagementRaceImagesBarCell.ACTION_COMPETITOR_REGISTRATIONS - .equals(value)) { - ShowWithBoatClass showWithBoatClass = new ShowWithBoatClass() { - @Override - public void showWithBoatClass(String boatClassName) { - new RaceLogCompetitorRegistrationDialog(boatClassName, sailingService, stringMessages, - errorReporter, editable, leaderboardName, raceColumnName, fleetName, - raceColumnDTOAndFleetDTO.getA().getFleets(), - new DialogCallback>() { - - @Override - public void ok(final Set registeredCompetitors) { - sailingService.setCompetitorRegistrationsInRaceLog(leaderboardName, - raceColumnName, fleetName, registeredCompetitors, - new AsyncCallback() { - @Override - public void onSuccess(Void result) { - } - - @Override - public void onFailure(Throwable caught) { - errorReporter - .reportError("Could not save competitor registrations: " - + caught.getMessage()); - } - }); - } - - @Override - public void cancel() { - } - }).show(); - } - }; - searchBoatClass(showWithBoatClass); + } else if (RaceLogTrackingEventManagementRaceImagesBarCell.ACTION_COMPETITOR_REGISTRATIONS.equals(value)) { + registerCompetitorsInRaceLog(getSelectedRegatta(), editable, leaderboardName, canBoatsOfCompetitorsChangePerRace, raceColumnName, fleetName, + raceColumnDTOAndFleetDTO); } else if (RaceLogTrackingEventManagementRaceImagesBarCell.ACTION_DEFINE_COURSE.equals(value)) { new RaceLogTrackingCourseDefinitionDialog(sailingService, stringMessages, errorReporter, leaderboardName, raceColumnName, fleetName, new DialogCallback>>() { @@ -391,7 +392,9 @@ public class SmartphoneTrackingEventManagementPanel extends AbstractLeaderboardC startTracking(Collections.singleton(raceColumnDTOAndFleetDTO), trackWind.getValue(), correctWindDirectionForDeclination.getValue()); } else if (RaceLogTrackingEventManagementRaceImagesBarCell.ACTION_STOP_TRACKING.equals(value)) { stopTracking(Collections.singleton(raceColumnDTOAndFleetDTO)); - } + } else if (RaceLogTrackingEventManagementRaceImagesBarCell.ACTION_EDIT_COMPETITOR_TO_BOAT_MAPPINGS.equals(value)) { + showCompetitorToBoatMappings(raceColumnDTOAndFleetDTO.getA(), raceColumnDTOAndFleetDTO.getB()); + } } }); @@ -402,6 +405,85 @@ public class SmartphoneTrackingEventManagementPanel extends AbstractLeaderboardC racesTable.setWidth("600px"); } + private void registerCompetitorsInRaceLog(RegattaDTO selectedRegatta, boolean editable, String leaderboardName, + boolean canBoatsOfCompetitorsChangePerRace, String raceColumnName, String fleetName, + RaceColumnDTOAndFleetDTOWithNameBasedEquality raceColumnDTOAndFleetDTO) { + RegattaDTO regatta = getSelectedRegatta(); + String boatClassName = regatta.boatClass.getName(); + + RaceLogCompetitorRegistrationDialog dialog = new RaceLogCompetitorRegistrationDialog(boatClassName, sailingService, stringMessages, + errorReporter, editable, leaderboardName, canBoatsOfCompetitorsChangePerRace, raceColumnName, fleetName, + raceColumnDTOAndFleetDTO.getA().getFleets(), new DialogCallback>() { + @Override + public void ok(final Set registeredCompetitors) { + if (canBoatsOfCompetitorsChangePerRace) { + sailingService.getCompetitorAndBoatRegistrationsInRaceLog(leaderboardName, raceColumnName, fleetName, new AsyncCallback>() { + + @Override + public void onSuccess(Map existingCompetitorToBoatMappings) { + // remove the competitors which has been removed in the first dialog (competitor selection) + Map newCompetitorToBoatMappings = new HashMap<>(); + + for (CompetitorDTO competitorDTO: registeredCompetitors) { + if (existingCompetitorToBoatMappings.containsKey((competitorDTO))) { + BoatDTO boatDTO = existingCompetitorToBoatMappings.get(competitorDTO); + newCompetitorToBoatMappings.put(competitorDTO, boatDTO); + } else { + newCompetitorToBoatMappings.put(competitorDTO, null); + } + } + + new CompetitorToBoatMappingsDialog(sailingService, stringMessages, + errorReporter, newCompetitorToBoatMappings, new DialogCallback>() { + @Override + public void ok(final Map competitorToBoatMappings) { + sailingService.setCompetitorRegistrationsInRaceLog(leaderboardName, raceColumnName, + fleetName, competitorToBoatMappings, new AsyncCallback() { + @Override + public void onSuccess(Void result) { + } + + @Override + public void onFailure(Throwable caught) { + errorReporter.reportError("Could not save competitor and boat registrations: " + caught.getMessage()); + } + }); + } + @Override + public void cancel() { + } + }).show(); + } + + @Override + public void onFailure(Throwable caught) { + errorReporter.reportError("Could not read the competitor/boat assignments: " + caught.getMessage()); + } + }); + + } else { + sailingService.setCompetitorRegistrationsInRaceLog(leaderboardName, raceColumnName, + fleetName, registeredCompetitors, new AsyncCallback() { + @Override + public void onSuccess(Void result) { + } + + @Override + public void onFailure(Throwable caught) { + errorReporter.reportError("Could not save competitor registrations: " + caught.getMessage()); + } + }); + } + } + + @Override + public void cancel() { + } + }); + + dialog.show(); + } + @Override protected void addLeaderboardControls(Panel controlsPanel) {} @@ -832,6 +914,17 @@ public class SmartphoneTrackingEventManagementPanel extends AbstractLeaderboardC }).show(); } + private void showCompetitorToBoatMappings(final RaceColumnDTO raceColumnDTO, final FleetDTO fleetDTO) { + final String selectedLeaderboardName = getSelectedLeaderboardName(); + final String raceColumnName = raceColumnDTO.getName(); + final String fleetName = fleetDTO.getName(); + final String raceName = LeaderboardNameConstants.DEFAULT_FLEET_NAME.equals(fleetName) ? raceColumnName : raceColumnName + ", " + fleetName; + ShowCompetitorToBoatMappingsDialog dialog = new ShowCompetitorToBoatMappingsDialog(sailingService, + stringMessages, errorReporter, selectedLeaderboardName, raceColumnName, fleetName, + raceName); + dialog.center(); + } + private String getLocaleInfo() { return LocaleInfo.getCurrentLocale().getLocaleName(); } @@ -862,35 +955,4 @@ public class SmartphoneTrackingEventManagementPanel extends AbstractLeaderboardC } }).show(); } - - private void searchBoatClass(final ShowWithBoatClass showWithBoatClass) { - final String result; - RegattaDTO regatta = getSelectedRegatta(); - if (regatta != null) { - result = regatta.boatClass.getName(); - showWithBoatClass.showWithBoatClass(result); - } else { - sailingService.getCompetitorsOfLeaderboard(getSelectedLeaderboardName(), - new AsyncCallback>() { - @Override - public void onFailure(Throwable caught) { - GWT.log("Error while searching boat class"); - showWithBoatClass.showWithBoatClass(null); - } - - @Override - public void onSuccess(Iterable result) { - String boatClass = null; - if (result != null) { - List boatClassen = new ArrayList<>(); - for (CompetitorDTO comp : result) { - boatClassen.add(comp.getBoatClass().getName()); - } - boatClass = Util.getDominantObject(boatClassen); - } - showWithBoatClass.showWithBoatClass(boatClass); - } - }); - } - } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/TableWrapper.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/TableWrapper.java index ff891758928..20fa452e25d 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/TableWrapper.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/TableWrapper.java @@ -32,7 +32,7 @@ public abstract class TableWrapper> im * If the {@code enablePager} constructur argument is set to {@code true} then this many entries are shown * at most on one page, and users will have to flip through the pages one by one. */ - private static final int PAGING_SIZE = 100; + protected static final int DEFAULT_PAGING_SIZE = 100; protected final FlushableCellTable table; private S selectionModel; @@ -52,14 +52,25 @@ public abstract class TableWrapper> im public Widget asWidget() { return mainPanel; } - + /** * @param entityIdentityComparator * {@link EntityIdentityComparator} to create a {@link RefreshableSelectionModel} */ + public TableWrapper(SailingServiceAsync sailingService, final StringMessages stringMessages, + ErrorReporter errorReporter, boolean multiSelection, boolean enablePager, + EntityIdentityComparator entityIdentityComparator) { + this(sailingService, stringMessages, errorReporter, multiSelection, enablePager, DEFAULT_PAGING_SIZE, + entityIdentityComparator); + } - public TableWrapper(SailingServiceAsync sailingService, final StringMessages stringMessages, ErrorReporter errorReporter, - boolean multiSelection, boolean enablePager, EntityIdentityComparator entityIdentityComparator) { + /** + * @param entityIdentityComparator + * {@link EntityIdentityComparator} to create a {@link RefreshableSelectionModel} + */ + public TableWrapper(SailingServiceAsync sailingService, final StringMessages stringMessages, + ErrorReporter errorReporter, boolean multiSelection, boolean enablePager, int pagingSize, + EntityIdentityComparator entityIdentityComparator) { this.entityIdentityComparator = entityIdentityComparator; this.multiSelection = multiSelection; this.sailingService = sailingService; @@ -75,7 +86,7 @@ public abstract class TableWrapper> im dataProvider.addDataDisplay(table); mainPanel.add(table); if (enablePager) { - table.setPageSize(PAGING_SIZE); + table.setPageSize(pagingSize); SimplePager pager = new SimplePager() { protected String createText() { HasRows display = getDisplay(); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/TrackFileImportDeviceIdentifierTableWrapper.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/TrackFileImportDeviceIdentifierTableWrapper.java index b2217694978..b4944cb126b 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/TrackFileImportDeviceIdentifierTableWrapper.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/TrackFileImportDeviceIdentifierTableWrapper.java @@ -5,17 +5,20 @@ import java.util.Map; import com.google.gwt.dom.builder.shared.TableCellBuilder; import com.google.gwt.dom.builder.shared.TableRowBuilder; -import com.google.gwt.safehtml.shared.SafeHtmlBuilder; import com.google.gwt.safehtml.shared.SafeHtmlUtils; import com.google.gwt.user.cellview.client.AbstractCellTable; import com.google.gwt.user.cellview.client.AbstractCellTable.Style; import com.google.gwt.user.cellview.client.DefaultCellTableBuilder; import com.google.gwt.user.cellview.client.RowStyles; import com.google.gwt.user.cellview.client.TextColumn; +import com.sap.sailing.domain.common.dto.BoatDTO; import com.sap.sailing.domain.common.dto.CompetitorDTO; +import com.sap.sailing.domain.common.racelog.tracking.MappableToDevice; +import com.sap.sailing.gwt.ui.client.MappableToDeviceFormatter; import com.sap.sailing.gwt.ui.client.SailingServiceAsync; import com.sap.sailing.gwt.ui.client.StringMessages; import com.sap.sailing.gwt.ui.common.client.DateAndTimeFormatterUtil; +import com.sap.sailing.gwt.ui.shared.MarkDTO; import com.sap.sailing.gwt.ui.shared.TrackFileImportDeviceIdentifierDTO; import com.sap.sse.gwt.client.ErrorReporter; import com.sap.sse.gwt.client.celltable.EntityIdentityComparator; @@ -24,7 +27,7 @@ import com.sap.sse.gwt.client.celltable.RefreshableSingleSelectionModel; public class TrackFileImportDeviceIdentifierTableWrapper extends TableWrapper> { private TextColumn trackNameColumn; - private final Map mappings = new HashMap<>(); + private final Map mappings = new HashMap<>(); public TrackFileImportDeviceIdentifierTableWrapper(SailingServiceAsync sailingService, StringMessages stringMessages, ErrorReporter errorReporter) { @@ -130,35 +133,42 @@ public class TrackFileImportDeviceIdentifierTableWrapper extends TableRowBuilder tr = startRow(); tr.className(trClasses.toString()); + final MappableToDevice mappableToDevice = mappings.get(rowValue); tr.startTD().className(new StringBuilder(tdClasses).append(firstColumnStyle).toString()) - .html(SafeHtmlUtils.fromSafeConstant(" ")).end(); + .html(SafeHtmlUtils + .fromString(MappableToDeviceFormatter.formatType(mappableToDevice, stringMessages))) + .end(); TableCellBuilder td = tr.startTD(); td.className(tdClasses.toString()); td.colSpan(getColumns().size() - 1); - CompetitorDTO competitorDTO = mappings.get(rowValue); - if (competitorDTO == null) { - td.html(new SafeHtmlBuilder().appendEscaped("--").toSafeHtml()); + final String nameOfMappedItem; + if (mappableToDevice instanceof CompetitorDTO) { + final CompetitorDTO competitorDTO = (CompetitorDTO) mappableToDevice; + nameOfMappedItem = competitorDTO.getName(); + } else if (mappableToDevice instanceof BoatDTO) { + final BoatDTO boatDTO = (BoatDTO) mappableToDevice; + nameOfMappedItem = boatDTO.getDisplayName(); + } else if (mappableToDevice instanceof MarkDTO) { + final MarkDTO markDTO = (MarkDTO) mappableToDevice; + nameOfMappedItem = markDTO.getName(); } else { - td.html(new SafeHtmlBuilder().appendEscaped(competitorDTO.getName()).toSafeHtml()); + nameOfMappedItem = "--"; } + td.html(SafeHtmlUtils.fromString(nameOfMappedItem)); td.end(); tr.end(); } } - public CompetitorDTO getMappedCompetitorForCurrentSelection() { - TrackFileImportDeviceIdentifierDTO selectedObject = getSelectionModel().getSelectedObject(); - if (selectedObject != null) { - return mappings.get(selectedObject); - } - return null; + public MappableToDevice getMappedObjectForDeviceId(TrackFileImportDeviceIdentifierDTO deviceId) { + return mappings.get(deviceId); } - public void didSelectCompetitorForMapping(CompetitorDTO competitor) { + public void setMappedObjectForSelectedDevice(MappableToDevice mappableToDevice) { TrackFileImportDeviceIdentifierDTO selectedObject = getSelectionModel().getSelectedObject(); if (selectedObject != null) { - if (competitor != null) { - mappings.put(selectedObject, competitor); + if (mappableToDevice != null) { + mappings.put(selectedObject, mappableToDevice); } else { mappings.remove(selectedObject); } @@ -166,7 +176,7 @@ public class TrackFileImportDeviceIdentifierTableWrapper extends table.redraw(); } - public Map getMappings() { + public Map getMappings() { return mappings; } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/CompetitorSelectionModel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/CompetitorSelectionModel.java index f6859a5b75b..b9400ddab0a 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/CompetitorSelectionModel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/CompetitorSelectionModel.java @@ -11,7 +11,6 @@ import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; -import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; import com.sap.sailing.domain.common.dto.CompetitorDTO; import com.sap.sse.common.Color; import com.sap.sse.common.Util; @@ -19,7 +18,7 @@ import com.sap.sse.common.filter.Filter; import com.sap.sse.common.filter.FilterSet; public class CompetitorSelectionModel implements CompetitorSelectionProvider { - private final Set allCompetitors; + protected final Set allCompetitors; /** * Keys are {@link CompetitorDTO#getIdAsString()} of their values @@ -32,7 +31,7 @@ public class CompetitorSelectionModel implements CompetitorSelectionProvider { private FilterSet> competitorsFilterSet; - private final CompetitorColorProvider competitorColorProvider; + protected final CompetitorColorProvider competitorColorProvider; public CompetitorSelectionModel(boolean hasMultiSelection) { this(hasMultiSelection, new CompetitorColorProviderImpl(), null); @@ -260,11 +259,6 @@ public class CompetitorSelectionModel implements CompetitorSelectionProvider { return allCompetitors.contains(competitor) ? competitorColorProvider.getColor(competitor) : null; } - @Override - public Color getColor(CompetitorDTO competitor, RegattaAndRaceIdentifier raceIdentfier) { - return allCompetitors.contains(competitor) ? competitorColorProvider.getColor(competitor, raceIdentfier) : Color.WHITE /* safe default */; - } - @Override public FilterSet> getCompetitorsFilterSet() { return competitorsFilterSet; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/CompetitorSelectionProvider.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/CompetitorSelectionProvider.java index d2ab082026e..61f8bfd93a2 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/CompetitorSelectionProvider.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/CompetitorSelectionProvider.java @@ -1,6 +1,5 @@ package com.sap.sailing.gwt.ui.client; -import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; import com.sap.sailing.domain.common.dto.CompetitorDTO; import com.sap.sse.common.Color; import com.sap.sse.common.filter.Filter; @@ -62,8 +61,6 @@ public interface CompetitorSelectionProvider { boolean hasMultiSelection(); Color getColor(CompetitorDTO competitor); - - Color getColor(CompetitorDTO competitor, RegattaAndRaceIdentifier raceIdentfier); void addCompetitorSelectionChangeListener(CompetitorSelectionChangeListener listener); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/CompetitorWithBoatSelectionModel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/CompetitorWithBoatSelectionModel.java new file mode 100644 index 00000000000..844a3b2f6f4 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/CompetitorWithBoatSelectionModel.java @@ -0,0 +1,313 @@ +package com.sap.sailing.gwt.ui.client; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Set; + +import com.sap.sailing.domain.common.dto.CompetitorDTO; +import com.sap.sse.common.Color; +import com.sap.sse.common.Util; +import com.sap.sse.common.filter.Filter; +import com.sap.sse.common.filter.FilterSet; + +public class CompetitorWithBoatSelectionModel implements CompetitorSelectionProvider { + private final Set allCompetitors; + + /** + * Keys are {@link CompetitorDTO#getIdAsString()} of their values + */ + private final LinkedHashMap selectedCompetitors; + + private final Set listeners; + + private final boolean hasMultiSelection; + + private FilterSet> competitorsFilterSet; + + private final CompetitorColorProvider competitorColorProvider; + + public CompetitorWithBoatSelectionModel(boolean hasMultiSelection) { + this(hasMultiSelection, new CompetitorColorProviderImpl(), null); + } + + public CompetitorWithBoatSelectionModel(boolean hasMultiSelection, CompetitorColorProvider competitorColorProvider) { + this(hasMultiSelection, competitorColorProvider, null); + } + + private CompetitorWithBoatSelectionModel(boolean hasMultiSelection, CompetitorColorProvider competitorColorProvider, FilterSet> competitorsFilterSet) { + super(); + this.hasMultiSelection = hasMultiSelection; + this.competitorColorProvider = competitorColorProvider; + this.competitorsFilterSet = competitorsFilterSet; + this.allCompetitors = new LinkedHashSet(); + this.selectedCompetitors = new LinkedHashMap<>(); + this.listeners = new HashSet(); + } + + /** + * Adds a competitor to the {@link #getAllCompetitors() set of all competitors}. If the competitor was not yet + * contained, it will be deselected. + */ + public void add(CompetitorDTO competitor) { + add(competitor, true); + } + + private void add(CompetitorDTO competitor, boolean notifyListeners) { + if (competitor.getColor() != null) { + competitorColorProvider.addBlockedColor(competitor.getColor()); + } + boolean changed = allCompetitors.add(competitor); + if (notifyListeners && changed) { + fireListChanged(getAllCompetitors()); + } + } + + @Override + public boolean hasMultiSelection() { + return hasMultiSelection; + } + + /** + * Removes all competitors. Afterwards, this model is empty. Deselection events are issued for all competitors that + * were selected so far. + */ + public void clear() { + Iterator selIter = getSelectedCompetitors().iterator(); + while (selIter.hasNext()) { + CompetitorDTO selected = selIter.next(); + setSelected(selected, false); + selIter = getSelectedCompetitors().iterator(); + } + assert selectedCompetitors.isEmpty(); + allCompetitors.clear(); + fireListChanged(getAllCompetitors()); + } + + public void addAll(Iterable competitors) { + boolean changed = false; + for (CompetitorDTO competitor : competitors) { + add(competitor, false); + changed = true; + } + if (changed) { + fireListChanged(getAllCompetitors()); + } + } + + /** + * Removes the competitor from the {@link #getAllCompetitors() set of all competitor}. If the competitor was previously + * contained and selected, it is deselected first. + */ + public void remove(CompetitorDTO competitor) { + if (isSelected(competitor)) { + setSelected(competitor, false); + } + if (competitor.getColor() != null) { + competitorColorProvider.removeBlockedColor(competitor.getColor()); + } + boolean changed = allCompetitors.remove(competitor); + if (changed) { + fireListChanged(getAllCompetitors()); + } + } + + @Override + public Iterable getSelectedCompetitors() { + return Collections.unmodifiableCollection(selectedCompetitors.values()); + } + + @Override + public Iterable getSelectedFilteredCompetitors() { + Set result = new HashSet<>(selectedCompetitors.values()); + result.retainAll(getFilteredCompetitors()); + return result; + } + + @Override + public Iterable getAllCompetitors() { + return Collections.unmodifiableCollection(allCompetitors); + } + + @Override + public Collection getFilteredCompetitors() { + Set currentFilteredList = new LinkedHashSet(allCompetitors); + if (competitorsFilterSet != null) { + for (Filter filter : competitorsFilterSet.getFilters()) { + for (Iterator i=currentFilteredList.iterator(); i.hasNext(); ) { + CompetitorDTO competitorDTO = i.next(); + if (!filter.matches(competitorDTO)) { + i.remove(); + } + } + } + } + return currentFilteredList; + } + + public void setSelected(CompetitorDTO competitor, boolean selected, CompetitorSelectionChangeListener... listenersNotToNotify) { + if (selected) { + if (allCompetitors.contains(competitor) && !selectedCompetitors.containsKey(competitor.getIdAsString())) { + selectedCompetitors.put(competitor.getIdAsString(), competitor); + fireAddedToSelection(competitor, listenersNotToNotify); + } + } else { + if (selectedCompetitors.containsKey(competitor.getIdAsString())) { + selectedCompetitors.remove(competitor.getIdAsString()); + fireRemovedFromSelection(competitor, listenersNotToNotify); + } + } + } + + private void fireAddedToSelection(CompetitorDTO competitor, CompetitorSelectionChangeListener... listenersNotToNotify) { + for (CompetitorSelectionChangeListener listener : listeners) { + if (listenersNotToNotify == null || !Arrays.asList(listenersNotToNotify).contains(listener)) { + listener.addedToSelection(competitor); + } + } + } + + private void fireRemovedFromSelection(CompetitorDTO competitor, CompetitorSelectionChangeListener... listenersNotToNotify) { + for (CompetitorSelectionChangeListener listener : listeners) { + if (listenersNotToNotify == null || !Arrays.asList(listenersNotToNotify).contains(listener)) { + listener.removedFromSelection(competitor); + } + } + } + + private void fireListChanged(Iterable competitors, CompetitorSelectionChangeListener... listenersNotToNotify) { + for (CompetitorSelectionChangeListener listener : listeners) { + if (listenersNotToNotify == null || !Arrays.asList(listenersNotToNotify).contains(listener)) { + listener.competitorsListChanged(competitors); + } + } + } + + @Override + public boolean isSelected(CompetitorDTO competitor) { + return selectedCompetitors.containsKey(competitor.getIdAsString()); + } + + @Override + public void addCompetitorSelectionChangeListener(CompetitorSelectionChangeListener listener) { + listeners.add(listener); + } + + @Override + public void removeCompetitorSelectionChangeListener(CompetitorSelectionChangeListener listener) { + listeners.remove(listener); + } + + @Override + public void setSelection(Iterable newSelection, CompetitorSelectionChangeListener... listenersNotToNotify) { + Set competitorsToRemoveFromSelection = new HashSet(selectedCompetitors.values()); + for (CompetitorDTO newSelectedCompetitor : newSelection) { + setSelected(newSelectedCompetitor, true, listenersNotToNotify); + competitorsToRemoveFromSelection.remove(newSelectedCompetitor); + } + for (CompetitorDTO competitorToRemoveFromSelection : competitorsToRemoveFromSelection) { + setSelected(competitorToRemoveFromSelection, false, listenersNotToNotify); + } + } + + @Override + public void setCompetitors(Iterable newCompetitors, CompetitorSelectionChangeListener... listenersNotToNotify) { + boolean changed = false; + Map oldCompetitorsToRemove = new HashMap(); + for (final CompetitorDTO c : allCompetitors) { + oldCompetitorsToRemove.put(c.getIdAsString(), c); + } + for (CompetitorDTO newCompetitor : newCompetitors) { + // Search by equality, including all CompetitorDTO fields; if equal object is found, leave it in place; + // non-equal objects may still represent the same competitor entity; yet, replace the object to obtain the + // new, changed state; make sure the selection keeps unchanged regarding the selected competitor "entities"; + // see also bug 3413 for more background. + if (allCompetitors.contains(newCompetitor)) { + oldCompetitorsToRemove.remove(newCompetitor.getIdAsString()); // the old competitor object remains in allCompetitors; it is equal but doesn't have to be identical to newCompetitor + } else { + boolean selected = isSelected(newCompetitor); + if (selected) { + remove(oldCompetitorsToRemove.get(newCompetitor.getIdAsString())); + oldCompetitorsToRemove.remove(newCompetitor.getIdAsString()); + } + add(newCompetitor, false); // due to the equals definition of CompetitorDTOImpl that compares most fields, state changes lead to a replacement here + if (selected) { + // restore selection for replaced element + setSelected(newCompetitor, selected, listenersNotToNotify); + } + changed = true; + } + } + for (CompetitorDTO oldCompetitorToRemove : oldCompetitorsToRemove.values()) { + remove(oldCompetitorToRemove); + changed = true; + } + + if (changed) { + fireListChanged(getAllCompetitors(), listenersNotToNotify); + } + } + + @Override + public Color getColor(CompetitorDTO competitor) { + return allCompetitors.contains(competitor) ? competitorColorProvider.getColor(competitor) : null; + } + + @Override + public FilterSet> getCompetitorsFilterSet() { + return competitorsFilterSet; + } + + @Override + public FilterSet> getOrCreateCompetitorsFilterSet(String nameToAssignToNewFilterSet) { + if (competitorsFilterSet == null) { + competitorsFilterSet = new FilterSet>(nameToAssignToNewFilterSet); + } + return getCompetitorsFilterSet(); + } + + @Override + public void setCompetitorsFilterSet(FilterSet> competitorsFilterSet) { + FilterSet> oldFilterSet = this.competitorsFilterSet; + this.competitorsFilterSet = competitorsFilterSet; + if (!Util.equalsWithNull(competitorsFilterSet, oldFilterSet)) { + for (CompetitorSelectionChangeListener listener : listeners) { + listener.filterChanged(oldFilterSet, competitorsFilterSet); + } + } + for (CompetitorSelectionChangeListener listener : listeners) { + listener.filteredCompetitorsListChanged(getFilteredCompetitors()); + } + } + + @Override + public boolean hasActiveFilters() { + return (competitorsFilterSet != null && !competitorsFilterSet.getFilters().isEmpty() + && getFilteredCompetitors().size() != allCompetitors.size()); + } + + @Override + public void clearAllFilters() { + if (hasActiveFilters()) { + Iterator selIter = getSelectedCompetitors().iterator(); + while (selIter.hasNext()) { + CompetitorDTO selected = selIter.next(); + setSelected(selected, false); + selIter = getSelectedCompetitors().iterator(); + } + competitorsFilterSet = null; + fireListChanged(getAllCompetitors()); + } + } + + @Override + public int getFilteredCompetitorsListSize() { + return getFilteredCompetitors().size(); + } +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/MappableToDeviceFormatter.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/MappableToDeviceFormatter.java new file mode 100644 index 00000000000..8b6fc02d3f0 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/MappableToDeviceFormatter.java @@ -0,0 +1,48 @@ +package com.sap.sailing.gwt.ui.client; + +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.sailing.domain.common.racelog.tracking.MappableToDevice; +import com.sap.sailing.gwt.ui.shared.MarkDTO; + +public abstract class MappableToDeviceFormatter { + + /** + * Provides a {@link String} representation for the type of the given {@link MappableToDevice mappedTo} instance + * depending on the actual sub-class type. + * + * @param mappedTo + * {@link MappableToDevice} instance to get representation for + * @return the {@link String} representing of the given mappedTo instance + */ + public static String formatType(final MappableToDevice mappedTo, final StringMessages stringMessages) { + if (mappedTo instanceof CompetitorDTO || mappedTo instanceof CompetitorWithoutBoatDTO) { + return stringMessages.competitor(); + } else if (mappedTo instanceof BoatDTO) { + return stringMessages.boat(); + } else if (mappedTo instanceof MarkDTO) { + return stringMessages.mark(); + } + return ""; + } + + /** + * Provides a {@link String} representation for the given {@link MappableToDevice mappedTo} instance depending on + * the actual sub-class type. + * + * @param mappedTo + * {@link MappableToDevice} instance to get representation for + * @return the {@link String} representing of the given mappedTo instance + */ + public static String formatName(final MappableToDevice mappedTo) { + if (mappedTo instanceof CompetitorDTO || mappedTo instanceof CompetitorWithoutBoatDTO) { + return ((CompetitorWithoutBoatDTO) mappedTo).getName(); + } else if (mappedTo instanceof BoatDTO) { + return ((BoatDTO) mappedTo).getDisplayName(); + } else if (mappedTo instanceof MarkDTO) { + return ((MarkDTO) mappedTo).getName(); + } + return ""; + } +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/RaceCompetitorSelectionModel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/RaceCompetitorSelectionModel.java new file mode 100644 index 00000000000..4a17af31d65 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/RaceCompetitorSelectionModel.java @@ -0,0 +1,36 @@ +package com.sap.sailing.gwt.ui.client; + +import java.util.HashMap; +import java.util.Map; + +import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; +import com.sap.sailing.domain.common.dto.BoatDTO; +import com.sap.sailing.domain.common.dto.CompetitorDTO; +import com.sap.sse.common.Color; + +public class RaceCompetitorSelectionModel extends CompetitorSelectionModel implements RaceCompetitorSelectionProvider { + private final Map boatsOfCompetitors = new HashMap<>(); + + public RaceCompetitorSelectionModel(boolean hasMultiSelection) { + super(hasMultiSelection); + } + + public RaceCompetitorSelectionModel(boolean hasMultiSelection, CompetitorColorProvider competitorColorProvider) { + super(hasMultiSelection, competitorColorProvider); + } + + @Override + public Color getColor(CompetitorDTO competitor, RegattaAndRaceIdentifier raceIdentfier) { + return allCompetitors.contains(competitor) ? competitorColorProvider.getColor(competitor, raceIdentfier) : Color.WHITE /* safe default */; + } + + @Override + public void setBoat(CompetitorDTO competitor, BoatDTO boat) { + boatsOfCompetitors.put(competitor, boat); + } + + @Override + public BoatDTO getBoat(CompetitorDTO competitor) { + return boatsOfCompetitors.get(competitor); + } +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/RaceCompetitorSelectionProvider.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/RaceCompetitorSelectionProvider.java new file mode 100644 index 00000000000..46b00bd0eca --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/RaceCompetitorSelectionProvider.java @@ -0,0 +1,15 @@ +package com.sap.sailing.gwt.ui.client; + +import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; +import com.sap.sailing.domain.common.dto.BoatDTO; +import com.sap.sailing.domain.common.dto.CompetitorDTO; +import com.sap.sse.common.Color; + +public interface RaceCompetitorSelectionProvider extends CompetitorSelectionProvider { + + Color getColor(CompetitorDTO competitor, RegattaAndRaceIdentifier raceIdentfier); + + void setBoat(CompetitorDTO competitor, BoatDTO boat); + + BoatDTO getBoat(CompetitorDTO competitor); +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingService.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingService.java index bc103b07f64..aff2ed8f42f 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingService.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingService.java @@ -34,6 +34,7 @@ import com.sap.sailing.domain.common.abstractlog.NotRevokableException; import com.sap.sailing.domain.common.abstractlog.TimePointSpecificationFoundInLog; 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.sailing.domain.common.dto.FleetDTO; import com.sap.sailing.domain.common.dto.IncrementalOrFullLeaderboardDTO; import com.sap.sailing.domain.common.dto.PairingListDTO; @@ -225,7 +226,7 @@ public interface SailingService extends RemoteService, FileStorageManagementGwtS void moveLeaderboardColumnDown(String leaderboardName, String columnName); - RegattaDTO createRegatta(String regattaName, String boatClassName, Date startDate, Date endDate, + RegattaDTO createRegatta(String regattaName, String boatClassName, boolean canBoatsOfCompetitorsChangePerRace, Date startDate, Date endDate, RegattaCreationParametersDTO seriesNamesWithFleetNamesAndFleetOrderingAndMedal, boolean persistent, ScoringSchemeType scoringSchemeType, UUID defaultCourseAreaId, Double buoyZoneRadiusInHullLengths, boolean useStartTimeInference, boolean controlTrackingFromStartAndFinishTimes, RankingMetrics rankingMetricType); @@ -436,15 +437,35 @@ public interface SailingService extends RemoteService, FileStorageManagementGwtS DataImportProgress getImportOperationProgress(UUID id); - Iterable getCompetitors(); + Iterable getCompetitors(boolean filterCompetitorsWithBoat, boolean filterCompetitorsWithoutBoat); Iterable getCompetitorsOfLeaderboard(String leaderboardName); - List addOrUpdateCompetitor(List competitors) throws Exception; + Map getCompetitorsAndBoatsOfRace(String leaderboardName, String raceColumnName, String fleetName); + + List addOrUpdateCompetitors(List competitors) throws Exception; + + CompetitorDTO addOrUpdateCompetitorWithBoat(CompetitorDTO competitor) throws Exception; + + CompetitorDTO addOrUpdateCompetitorWithoutBoat(CompetitorDTO competitor) throws Exception; List addCompetitors(List competitorsForSaving, String searchTag) throws Exception; void allowCompetitorResetToDefaults(Iterable competitors); + + Iterable getAllBoats(); + + Iterable getStandaloneBoats(); + + BoatDTO addOrUpdateBoat(BoatDTO boat) throws Exception; + + void allowBoatResetToDefaults(Iterable boats); + + boolean linkBoatToCompetitorForRace(String leaderboardName, String raceColumnName, String fleetName, String competitorIdAsString, String boatIdAsString); + + boolean unlinkBoatFromCompetitorForRace(String leaderboardName, String raceColumnName, String fleetName, String competitorIdAsString); + + BoatDTO getBoatLinkedToCompetitorForRace(String leaderboardName, String raceColumnName, String fleetName, String competitorIdAsString); List getDeviceConfigurationMatchers(); @@ -499,8 +520,10 @@ public interface SailingService extends RemoteService, FileStorageManagementGwtS void startRaceLogTracking(List> leaderboardRaceColumnFleetNames, boolean trackWind, boolean correctWindByDeclination) throws NotDenotedForRaceLogTrackingException, Exception; - void setCompetitorRegistrationsInRaceLog(String leaderboardName, String raceColumnName, String fleetName, Set competitors) throws CompetitorRegistrationOnRaceLogDisabledException, NotFoundException; - + void setCompetitorRegistrationsInRaceLog(String leaderboardName, String raceColumnName, String fleetName, Set competitors) throws CompetitorRegistrationOnRaceLogDisabledException, NotFoundException; + + void setCompetitorRegistrationsInRaceLog(String leaderboardName, String raceColumnName, String fleetName, Map competitorsAndBoats) throws CompetitorRegistrationOnRaceLogDisabledException, NotFoundException; + /** * Adds the course definition to the racelog, while trying to reuse existing marks, controlpoints and waypoints * from the previous course definition in the racelog. @@ -566,7 +589,7 @@ public interface SailingService extends RemoteService, FileStorageManagementGwtS void updateFixedMarkPassing(String leaderboardName, String raceColumnName, String fleetName, Integer indexOfWaypoint, Date dateOfMarkPassing, CompetitorDTO competitorDTO); - void setCompetitorRegistrationsInRegattaLog(String leaderboardName, Set competitors) + void setCompetitorRegistrationsInRegattaLog(String leaderboardName, Set competitors) throws DoesNotHaveRegattaLogException, NotFoundException; /** @@ -647,6 +670,12 @@ public interface SailingService extends RemoteService, FileStorageManagementGwtS Collection getCompetitorRegistrationsInRegattaLog(String leaderboardName) throws DoesNotHaveRegattaLogException, NotFoundException; + Collection getBoatRegistrationsInRegattaLog(String leaderboardName) throws DoesNotHaveRegattaLogException, NotFoundException; + + void setBoatRegistrationsInRegattaLog(String leaderboardName, Set boats) throws DoesNotHaveRegattaLogException, NotFoundException; + + Collection getBoatRegistrationsForLeaderboard(String leaderboardName) throws NotFoundException; + Boolean areCompetitorRegistrationsEnabledForRace(String leaderboardName, String raceColumnName, String fleetName) throws NotFoundException; void disableCompetitorRegistrationsForRace(String leaderboardName, String raceColumnName, String fleetName) throws NotRevokableException, NotFoundException; @@ -659,6 +688,9 @@ public interface SailingService extends RemoteService, FileStorageManagementGwtS Collection getCompetitorRegistrationsInRaceLog(String leaderboardName, String raceColumnName, String fleetName) throws NotFoundException; + Map getCompetitorAndBoatRegistrationsInRaceLog(String leaderboardName, String raceColumnName, + String fleetName) throws NotFoundException; + Collection getCompetitorRegistrationsForLeaderboard(String leaderboardName) throws NotFoundException; Iterable getMarksInTrackedRace(String leaderboardName, String raceColumnName, String fleetName); @@ -699,7 +731,11 @@ public interface SailingService extends RemoteService, FileStorageManagementGwtS void addOrReplaceExpeditionDeviceConfiguration(ExpeditionDeviceConfiguration expeditionDeviceConfiguration); void removeExpeditionDeviceConfiguration(ExpeditionDeviceConfiguration expeditionDeviceConfiguration); - + + /** + * @throws NotFoundException + * is thrown if the leaderboard is not found by name + */ PairingListTemplateDTO calculatePairingListTemplate(final int flightCount, final int groupCount, final int competitorCount, final int flightMultiplier) throws NotFoundException, IllegalArgumentException; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingServiceAsync.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingServiceAsync.java index e96b9a2ea89..873009f07b9 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingServiceAsync.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingServiceAsync.java @@ -10,13 +10,14 @@ import java.util.Set; import java.util.UUID; import com.google.gwt.user.client.rpc.AsyncCallback; +import com.sap.sailing.domain.base.Fleet; +import com.sap.sailing.domain.base.RaceColumn; import com.sap.sailing.domain.common.CompetitorDescriptor; import com.sap.sailing.domain.common.DataImportProgress; import com.sap.sailing.domain.common.DetailType; import com.sap.sailing.domain.common.LeaderboardType; import com.sap.sailing.domain.common.LegIdentifier; import com.sap.sailing.domain.common.MaxPointsReason; -import com.sap.sailing.domain.common.NotFoundException; import com.sap.sailing.domain.common.PassingInstruction; import com.sap.sailing.domain.common.PolarSheetsXYDiagramData; import com.sap.sailing.domain.common.Position; @@ -30,6 +31,7 @@ import com.sap.sailing.domain.common.WindSource; import com.sap.sailing.domain.common.abstractlog.TimePointSpecificationFoundInLog; 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.sailing.domain.common.dto.FleetDTO; import com.sap.sailing.domain.common.dto.IncrementalOrFullLeaderboardDTO; import com.sap.sailing.domain.common.dto.PairingListDTO; @@ -450,7 +452,7 @@ public interface SailingServiceAsync extends ServerInfoRetriever, FileStorageMan void moveRaceColumnInSeriesDown(RegattaIdentifier regattaIdentifier, String seriesName, String columnName, AsyncCallback callback); - void createRegatta(String regattaName, String boatClassName, Date startDate, Date endDate, + void createRegatta(String regattaName, String boatClassName, boolean canBoatsOfCompetitorsChangePerRace, Date startDate, Date endDate, RegattaCreationParametersDTO seriesNamesWithFleetNamesAndFleetOrderingAndMedal, boolean persistent, ScoringSchemeType scoringSchemeType, UUID defaultCourseAreaId, Double buoyZoneRadiusInHullLengths, boolean useStartTimeInference, boolean controlTrackingFromStartAndFinishTimes, RankingMetrics rankingMetricType, @@ -593,17 +595,37 @@ public interface SailingServiceAsync extends ServerInfoRetriever, FileStorageMan void getLeaderboardGroupNamesFromRemoteServer(String host, AsyncCallback> leaderboardGroupNames); - void getCompetitors(AsyncCallback> asyncCallback); + void getCompetitors(boolean filterCompetitorsWithBoat, boolean filterCompetitorsWithoutBoat, AsyncCallback> asyncCallback); void getCompetitorsOfLeaderboard(String leaderboardName, AsyncCallback> asyncCallback); - void addOrUpdateCompetitor(List competitors, AsyncCallback> asyncCallback); + void getCompetitorsAndBoatsOfRace(String leaderboardName, String raceColumnName, String fleetName, AsyncCallback> asyncCallback); + + void addOrUpdateCompetitors(List competitors, AsyncCallback> asyncCallback); + void addOrUpdateCompetitorWithBoat(CompetitorDTO competitor, AsyncCallback asyncCallback); + + void addOrUpdateCompetitorWithoutBoat(CompetitorDTO competitor, AsyncCallback asyncCallback); + void addCompetitors(List competitorsForSaving, String searchTag, AsyncCallback> asyncCallback); void allowCompetitorResetToDefaults(Iterable competitors, AsyncCallback asyncCallback); + void getAllBoats(AsyncCallback> asyncCallback); + + void getStandaloneBoats(AsyncCallback> asyncCallback); + + void addOrUpdateBoat(BoatDTO boat, AsyncCallback asyncCallback); + + void allowBoatResetToDefaults(Iterable boats, AsyncCallback asyncCallback); + + void linkBoatToCompetitorForRace(String leaderboardName, String raceColumnName, String fleetName, String competitorIdAsString, String boatIdAsString, AsyncCallback asyncCallback); + + void unlinkBoatFromCompetitorForRace(String leaderboardName, String raceColumnName, String fleetName, String competitorIdAsString, AsyncCallback asyncCallback); + + void getBoatLinkedToCompetitorForRace(String leaderboardName, String raceColumnName, String fleetName, String competitorIdAsString, AsyncCallback asyncCallback); + void getDeviceConfigurationMatchers(AsyncCallback> asyncCallback); void getDeviceConfiguration(DeviceConfigurationMatcherDTO matcher, AsyncCallback callback); @@ -676,17 +698,24 @@ public interface SailingServiceAsync extends ServerInfoRetriever, FileStorageMan final boolean trackWind, final boolean correctWindByDeclination, AsyncCallback callback); /** - * Set the competitor registrations in the racelog. Unregisters formerly registered competitors that are not listed + * Set the competitor (with contained boats) registrations in the racelog. Unregisters formerly registered competitors that are not listed * in {@code competitors}. */ void setCompetitorRegistrationsInRaceLog(String leaderboardName, String raceColumnName, String fleetName, - Set competitors, AsyncCallback callback); + Set competitors, AsyncCallback callback); + + /** + * Set the competitor and boat registrations in the racelog. Unregisters formerly registered competitors that are not listed + * in {@code competitors}. + */ + void setCompetitorRegistrationsInRaceLog(String leaderboardName, String raceColumnName, String fleetName, + Map competitorsAndBoats, AsyncCallback callback); /** * Set the competitor registrations in the leaderboard. Unregisters formerly registered competitors that are not * listed in {@code competitors}. */ - void setCompetitorRegistrationsInRegattaLog(String leaderboardName, Set competitors, + void setCompetitorRegistrationsInRegattaLog(String leaderboardName, Set competitors, AsyncCallback callback); void getCompetitorRegistrationsForRace(String leaderboardName, String raceColumnName, String fleetName, @@ -805,9 +834,6 @@ public interface SailingServiceAsync extends ServerInfoRetriever, FileStorageMan void revokeMarkDefinitionEventInRegattaLog(String leaderboardName, MarkDTO markDTO, AsyncCallback callback); - void getCompetitorRegistrationsInRegattaLog(String leaderboardName, - AsyncCallback> setCompetitorsCallback); - void areCompetitorRegistrationsEnabledForRace(String leaderboardName, String raceColumnName, String fleetName, AsyncCallback asyncCallback); @@ -823,9 +849,23 @@ public interface SailingServiceAsync extends ServerInfoRetriever, FileStorageMan void getCompetitorRegistrationsInRaceLog(String leaderboardName, String raceColumnName, String fleetName, AsyncCallback> callback); + void getCompetitorAndBoatRegistrationsInRaceLog(String leaderboardName, String raceColumnName, String fleetName, + AsyncCallback> callback); + void getCompetitorRegistrationsForLeaderboard(String leaderboardName, AsyncCallback> callback); + void getCompetitorRegistrationsInRegattaLog(String leaderboardName, + AsyncCallback> setCompetitorsCallback); + + void getBoatRegistrationsInRegattaLog(String leaderboardName, + AsyncCallback> setBoatsCallback); + + void setBoatRegistrationsInRegattaLog(String leaderboardName, Set boats, + AsyncCallback callback); + + void getBoatRegistrationsForLeaderboard(String leaderboardName, AsyncCallback> callback); + void addMarkFix(String leaderboardName, String raceColumnName, String fleetName, String markIdAsString, GPSFixDTO newFix, AsyncCallback callback); @@ -888,13 +928,9 @@ public interface SailingServiceAsync extends ServerInfoRetriever, FileStorageMan * specifies how often the flights will be cloned * @param callback * returns a {@link PairingListTemplateDTO} - * @throws NotFoundException - * is thrown if the leaderboard is not found by name - * @throws IllegalArgumentException */ void calculatePairingListTemplate(final int flightCount, final int groupCount, final int competitorCount, - final int flightMultiplier, AsyncCallback callback) - throws NotFoundException, IllegalArgumentException; + final int flightMultiplier, AsyncCallback callback); /** * Creates a {@link PairingListDTO} in which the competitors will be matched to a {@link PairingList} based on the diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java index 99a1bd231c4..7af0795d10b 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java @@ -49,6 +49,9 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages, String competitor(); String competitors(); String competitorsSelected(int selectedCompetitorsCount, int competitorsCount); + String boat(); + String boats(); + String competitorWithBoat(); String noSuchLeaderboard(); String leaderboardConfiguration(); String newDotDotDot(); @@ -102,6 +105,7 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages, String windwardDistanceToGoInMeters(); String windwardDistanceToCompetitorFarthestAheadInMeters(); String name(); + String shortName(); String leaveEmptyForDefault(); String trackNewEvent(); String trackableRaces(); @@ -216,6 +220,9 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages, String actionEdit(); String actionEditScores(); String actionEditCompetitors(); + String actionShowCompetitorToBoatAssignments(); + String actionEditCompetitorToBoatAssignments(); + String actionContinueToBoatAssignment(); String actionOpenBrowser(); String actionRemove(); String actionEditSeries(); @@ -224,6 +231,7 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages, String actionRaceRemove(); String actionExportXML(); String actionOpenDashboard(); + String actionBoatUnlink(); String filterLeaderboardsByName(); String actions(); String islinked(); @@ -368,7 +376,6 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages, String showWindSpeedSeries(); String showWindDirectionSeries(); String fleet(); - String boat(); String boatClass(); String setDelayToLive(); String pleaseEnterNonEmptyVenue(); @@ -997,9 +1004,12 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages, String doYouReallyWantToRemoveCompetitors(); String doYouReallyWantToRemoveCompetitor(String name); String filterCompetitors(); + String filterBoats(); String editCompetitor(); String pleaseEnterASailNumber(); + String pleaseEnterASailNumberOrABoatName(); String successfullyAllowedCompetitorReset(String names); + String successfullyAllowedBoatReset(String names); String allowReload(); String id(); String compress(); @@ -1110,6 +1120,7 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages, String filterByLeaderboard(); String removeLeaderboardFilter(); String competitorRegistrations(); + String boatRegistrations(); String trackerStatus(); String courseStatus(); String canOnlyBeEditedBeforeStartingTracking(); @@ -1156,6 +1167,8 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages, String twoMarkControlPoint(); String registeredCompetitors(); String competitorPool(); + String registeredBoats(); + String boatPool(); String selectAtLeastOne(); String selectRaces(); String importFixes(); @@ -1217,6 +1230,9 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages, String setTimeToNow(); String useStartTimeInference(); String controlTrackingFromStartAndFinishTimes(); + String canBoatsOfCompetitorsChangePerRace(); + String canBoatsChange(); + String canNotRegisterBoats(); String numberOfQueueMessagesSent(); String totalSize(); String averageMessageSize(); @@ -1332,8 +1348,11 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages, String fileUploadResult(String status, String message); @Override String removeUploadedFile(); + String showCompetitorBoatColumn(); String showCompetitorSailIdColumn(); String showCompetitorSailIdColumnTooltip(String alwaysShowCompetitorNationalityColumnTitle); + String showCompetitorShortNameColumn(); + String showCompetitorShortNameColumnTooltip(String alwaysShowCompetitorNationalityColumnTitle); String showCompetitorFullNameColumn(); String alwaysShowCompetitorNationalityColumn(); String alwaysShowCompetitorNationalityColumnTooltip(); @@ -1519,6 +1538,7 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages, String inviteSelectedCompetitors(); String denoteAllRacesForRaceLogTrackingShorctut(); String registerCompetitors(); + String registerBoats(); String createDefaultRegattaLeaderboard(); String doYouWantToCreateADefaultRegattaLeaderboard(); String copyCourse(); @@ -1576,8 +1596,10 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages, String bufferLineStrokeWeight(); String profile(); String showOnlyCompetitorsOfLog(); + String showOnlyBoatsOfLog(); String maneuverType(); String confirmLosingCompetitorEditsWhenTogglingLogBasedView(); + String confirmLosingBoatEditsWhenTogglingLogBasedView(); String removalOfMarkDisabledMayBeUsedInRaces(String raceNames); String pleaseCreateAtLeastOneMappingBy(); String createDefaultLeaderboardGroup(); @@ -1875,6 +1897,7 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages, String selectCompetitorsToEliminate(); String noUpcomingRaceDataAvailable(); String trackedRaceAlreadyLinked(); + String boatAlreadyLinked(); String reallyRemoveSeries(String seriesName); String maneuverSpeedDetails(); String maneuverDirectionNormalization(); @@ -2050,6 +2073,11 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages, String importFinishedGotoRaceboard(); String importFinishedGotoEvent(); String chooseAName(); + String errorFillingRaceLogsFromPairingList(String message); + String successfullyFilledRaceLogsFromPairingList(); + String errorFetchingRaceDisplayNamedFromLeaderboard(String message); + String errorCalculatingPairingListTemplate(String message); + String errorCreatingPairingList(String message); String unexpectedErrorDuringFileImport(); String couldNotDownload(String url); String sliceRace(); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties index 1a1bc0f1631..f4807f01d45 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties @@ -38,6 +38,9 @@ carry=Accumulated competitor=Competitor competitors=Competitors competitorsSelected={0}/{1} competitors selected. +boat=Boat +boats=Boats +competitorWithBoat=Competitor with boat noSuchLeaderboard=No such leaderboard leaderboardConfiguration=Leaderboard Configuration newDotDotDot=New... @@ -100,6 +103,7 @@ windwardDistanceToGoInMetersTooltip=Windward/leeward distance from the competito windwardDistanceToCompetitorFarthestAheadInMeters=Gap windwardDistanceToCompetitorFarthestAheadInMetersTooltip=Windward/leeward distance of the competitor to the competitor farthest ahead in the race for upwind/downwind legs, respectively, or the along-track distance for reaching legs.\nNote that the competitor that is farthest ahead is not necessarily leading the race if handicap ranking is in place. name=Name +shortName=Short name leaveEmptyForDefault=Leave empty for default trackNewEvent=Track New event trackableRaces=Trackable Races @@ -216,6 +220,9 @@ filterMediaByName=Filter media by name trackSettings=Track settings actionEdit=Edit actionEditCompetitors=Edit competitors +actionShowCompetitorToBoatAssignments=Show boats used by competitors +actionEditCompetitorToBoatAssignments=Edit boats used by competitors +actionContinueToBoatAssignment=Continue to boat assignments actionEditScores=Edit scores actionOpenBrowser=Open in Browser actionRemove=Remove @@ -225,6 +232,7 @@ actionRaceUnlink=Unlink race actionRaceRemove=Remove race actionExportXML=Export as XML actionOpenDashboard=Open Coach Dashboard +actionBoatUnlink=Unlink boat filterLeaderboardsByName=Filter leaderboards by name actions=Actions islinked=Is linked @@ -382,7 +390,6 @@ replicatingFromMaster=Replicating from master {0}:{2}, messaging port {3}:{1}, e showWindSpeedSeries=Show wind speed data showWindDirectionSeries=Show wind directions data fleet=Fleet -boat=Boat boatClass=Boat Class setDelayToLive=Set delay to live pleaseEnterNonEmptyVenue=Please enter a venue. @@ -974,10 +981,13 @@ line=Line offset=Offset doYouReallyWantToRemoveCompetitors=Do you really want to remove the selected competitors? doYouReallyWantToRemoveCompetitor=Do you really want to remove competitor {0}? -filterCompetitors=Filter competitors by name, boat class, sail number, search tag or ID +filterCompetitors=Filter competitors by name, short name, search tag or ID +filterBoats=Filter boats by name, boat class, sail number or ID editCompetitor=Edit Competitor pleaseEnterASailNumber=Please enter a sail number +pleaseEnterASailNumberOrABoatName=Please enter a sail number or a boat name successfullyAllowedCompetitorReset=Successfully allowed reload of competitor(s) {0}. +successfullyAllowedBoatReset=Successfully allowed reload of boats(s) {0}. id=ID allowReload=Allow reload compress=Compress @@ -1082,11 +1092,12 @@ startRaceLogTracking=Start RaceLog Tracking filterByLeaderboard=Filter by Leaderboard removeLeaderboardFilter=Remove Leaderboard filter competitorRegistrations=Competitor Registrations +boatRegistrations=Boat Registrations trackerStatus=Tracker Status courseStatus=Course Status canOnlyBeEditedBeforeStartingTracking=Can only be edited before starting tracking defineCourse=Define Course Layout -mapDevices=Map Devices to Competitors and Marks +mapDevices=Map Devices to Competitors, Boats and Marks edit_what=Edit {0} add_what=Add {0} remove_what=Remove {0} @@ -1126,6 +1137,8 @@ pleaseSelectAControlPoint=Please select a control point twoMarkControlPoint=2-mark-controlpoint registeredCompetitors=Registered Competitors competitorPool=Competitor Pool +registeredBoats=Registered Boats +boatPool=Boat Pool selectAtLeastOne=Select at least one selectRaces=Select the Races importFixes=Import Fixes @@ -1192,6 +1205,9 @@ lastScoreCorrectionsComment=Last score correction comment setTimeToNow=Set time to ''now'' useStartTimeInference=Use start time inference from start mark passings controlTrackingFromStartAndFinishTimes=Control tracking from start and finish times +canBoatsOfCompetitorsChangePerRace=Can boats of competitors change per race +canBoatsChange=Can boats change +canNotRegisterBoats=Boats can only be registered here when they can change per race, otherwise they are registered together with the competitor. numberOfQueueMessagesSent=Number of queue messages sent totalSize=Total Size averageMessageSize=Average size per message in bytes @@ -1299,10 +1315,13 @@ removeResult=Remove operation: {0} {1} uploadSuccessful=Upload successful fileUploadResult=File upload: {0} {1}.\nIn case of error please check that your File Storage service is properly configured. removeUploadedFile=Remove uploaded file +showCompetitorBoatColumn=Boat of the competitor showCompetitorSailIdColumn=Sail Id showCompetitorSailIdColumnTooltip=Shows the Sail Id plus the nationality flag or competitor image. If available, a competitor image is prioritized over a nationality flag. If "{0}" is set, both icons will be displayed. +showCompetitorShortNameColumn=Competitor Short Name +showCompetitorShortNameColumnTooltip=Shows the short name plus the nationality flag or competitor image. If available, a competitor image is prioritized over a nationality flag. If "{0}" is set, both icons will be displayed. showCompetitorFullNameColumn=Competitor Full Name -alwaysShowCompetitorNationalityColumn=Always show Competitor Nationality +alwaysShowCompetitorNationalityColumn=Always show competitor nationality alwaysShowCompetitorNationalityColumnTooltip=Show both, nationality flags as well as competitor images if available loadingDimensionValues=Loading dimension values runningQuery=Running Query @@ -1491,6 +1510,7 @@ actionWatch=Watch actionAnalyze=Analyze denoteAllRacesForRaceLogTrackingShorctut=Shortcut to denote all races for racelog tracking registerCompetitors=Register Competitors +registerBoats=Register Boats doYouWantToCreateADefaultRegattaLeaderboard=Do you want to create a default Regatta Leaderboard? copyCourse=Copy Course copyCompetitors=Copy Competitors @@ -1547,10 +1567,12 @@ transparentBufferLineOnHover=Transparent buffer lines on hover of helplines bufferLineStrokeWeight=Buffer line stroke weight profile=Profile showOnlyCompetitorsOfLog=Show only competitors registered in Log +showOnlyBoatsOfLog=Show only boats registered in Log confirmLosingCompetitorEditsWhenTogglingLogBasedView=This will reset your current, unsaved changes to the competitors.\nDo you really want to continue? +confirmLosingBoatEditsWhenTogglingLogBasedView=This will reset your current, unsaved changes to the boats.\nDo you really want to continue? removalOfMarkDisabledMayBeUsedInRaces=Removal deavticated, marks are used in races {0} createDefaultLeaderboardGroup=Would you like to create a default LeaderboardGroup for your event? -pleaseCreateAtLeastOneMappingBy=Please create at least one mapping by first selecting a track on the left hand side and then selecting a mark or competitor on the right hand side +pleaseCreateAtLeastOneMappingBy=Please create at least one mapping by first selecting a track on the left hand side and then selecting a mark, boat or competitor on the right hand side matcherType=Matcher type matchingDevices=Matching devices addDevice=Add Device @@ -1845,6 +1867,7 @@ useOnlyOneOfStartTimeInferenceAndControlTrackingFromStartAndFinishTimes=Use only unknownLeaderboardType=Unknown leaderboard type {0} selectCompetitorsToEliminate=Select competitors to eliminate trackedRaceAlreadyLinked=The race is already linked to a race column. Do you really want to change the link? +boatAlreadyLinked=The boat is already linked to a competitor chooseAAutoplayConfiguration=Choose an autoplay configuration raceRankShort=Race rank: showRaceRankColumn=Show race rank @@ -2038,6 +2061,11 @@ importFinishedMessage=Successfully imported expedition data. importFinishedGotoRaceboard=Open raceboard importFinishedGotoEvent=Open event chooseAName=Choose a name +errorFillingRaceLogsFromPairingList=Error filling race logs from pairing list: {0} +successfullyFilledRaceLogsFromPairingList=Successfully filled race logs from pairing list. +errorFetchingRaceDisplayNamedFromLeaderboard=Error fetching race display names from leaderboard: {0} +errorCalculatingPairingListTemplate=Error calculating pairing list template: {0} +errorCreatingPairingList=Error creating pairing list: {0} unexpectedErrorDuringFileImport=An unexpected error occurred during file upload. couldNotDownload=Could not download file: {0} sliceRace=Slice race diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties index 9cdbd676a62..4e3a4a9f123 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties @@ -38,6 +38,9 @@ carry=übertr. competitor=Team competitors=Teams competitorsSelected={0}/{1} Teams ausgewählt. +boat=Boot +boats=Boote +competitorWithBoat=Teilnehmer mit Boot noSuchLeaderboard=Rangliste nicht gefunden leaderboardConfiguration=Ranglisten-Konfiguration newDotDotDot=Neu... @@ -101,6 +104,7 @@ windwardDistanceToGoInMetersTooltip=Windwärtige / leewärtige Distanz des Teiln windwardDistanceToCompetitorFarthestAheadInMeters=Rückstand windwardDistanceToCompetitorFarthestAheadInMetersTooltip=Windwärtiger Abstand zum Boot, das bisher am weitesten windwärts gesegelt ist.\nFür Handicap-Wertungen ist das nicht notwendigerweise der nach Verrechnung in Führung liegende Teilnehmer. name=Name +shortName=Kurzname leaveEmptyForDefault=Leer für Standardeinstellung trackNewEvent=Track New event trackableRaces=Trackable Races @@ -218,6 +222,9 @@ filterByRegatta=nach Regatta filterMediaByName=Medien nach Name filtern actionEdit=Ändern actionEditCompetitors=Teilnehmer ändern +actionShowCompetitorToBoatAssignments=Zeige die Boote für die Teilnehmer +actionEditCompetitorToBoatAssignments=Ändere die Boote für die Teilnehmer +actionContinueToBoatAssignment=Weiter zur Bootszuordnung actionEditScores=Punkte ändern actionOpenBrowser=Im Browser öffnen actionRemove=Entfernen @@ -227,6 +234,7 @@ actionRaceUnlink=Rennverknüpfung entfernen actionRaceRemove=Rennen entfernen actionExportXML=Als XML exportieren actionOpenDashboard=Coach Dashboard öffnen +actionBoatUnlink=Bootsverknüpfung entfernen filterLeaderboardsByName=Ranglisten nach Name filtern actions=Aktionen islinked=Verknüpft @@ -963,10 +971,13 @@ line=Linie offset=Ablauftonne doYouReallyWantToRemoveCompetitors=Möchten Sie wirlich die ausgewählten Teilnehmer entfernen? doYouReallyWantToRemoveCompetitor=Möchten Sie wirklicn den Teilnehmer {0} entfernen? -filterCompetitors=Filtere Teilnehmer nach Name, Bootsklasse, Segelnummer, Suchhilfe oder ID +filterCompetitors=Filtere Teilnehmer nach Name, Kurzname, Suchhilfe oder ID +filterBoats=Filtere Boote nach Name, Bootsklasse, Segelnummer oder ID editCompetitor=Teilnehmer bearbeiten -pleaseEnterASailNumber=Bitte Segelnummer eingeben +pleaseEnterASailNumber=Bitte die Segelnummer eingeben +pleaseEnterASailNumberOrABoatName=Bitte die Segelnummer oder den Bootesnamen eingeben successfullyAllowedCompetitorReset=Aktualisierung der Daten zu Teilnehmer(n) {0} erfolgreich ermöglicht.id=ID +successfullyAllowedBoatReset=Aktualisierung der Daten zu Boot(en) {0} erfolgreich ermöglicht.id=ID allowReload=Aktualisierung ermöglichen raceTimeUpwindTooltip=Diw in diesem Rennen auf Kreuz gesegelte Zeit compress=Komprimieren @@ -1071,11 +1082,12 @@ startRaceLogTracking=RaceLog Tracking starten filterByLeaderboard=Nach Rangliste filtern removeLeaderboardFilter=Nicht nach Rangliste filtern competitorRegistrations=Teamanmeldungen +boatRegistrations=Bootsanmeldungen trackerStatus=Trackerstatus courseStatus=Kursstatus canOnlyBeEditedBeforeStartingTracking=Kann nur vor Beginn des Trackings bearbeitet werden defineCourse=Kurs definieren -mapDevices=Gerätezuordnung zu Teams und Tonnen +mapDevices=Gerätezuordnung zu Teams, Booten und Tonnen edit_what={0} bearbeiten add_what={0} hinzufügen remove_what={0} entfernen @@ -1115,6 +1127,8 @@ pleaseSelectAControlPoint=Bitte eine Einzelmark/ein Gate auswählen twoMarkControlPoint=2-Marken-Kontrollpunkt registeredCompetitors=Registrierte Teams competitorPool=Team Vorrat +registeredBoats=Registrierte Boote +boatPool=Boot Vorrat importFixes=Fixes importieren noSailingServerInstancesYet=Bisher sind noch keine Remote-Server Instanzen registriert. registeredSailingServerInstances=Registrierte Remote-Server Instanzen @@ -1180,6 +1194,9 @@ lastScoreCorrectionsComment=Letzter Kommentar der Punktzahl-Korrektur setTimeToNow=Setze den Zeitpunkt auf ''Jetzt'' useStartTimeInference=Startzeit aus Startlinienüberquerungen automatisch ableiten controlTrackingFromStartAndFinishTimes=Tracking durch Start- und Ende-Zeiten automatisch steuern +canBoatsOfCompetitorsChangePerRace=Das Boot des Teilnehmers kann sich pro Wettfahrt ändern +canBoatsChange=Boot kann sich ändern +canNotRegisterBoats=Boote können hier nur registriert werden, wenn sie sich pro Wettfahrt ändern, ansonsten werden sie zusammen mit dem Teilnehmer registriert. numberOfQueueMessagesSent=Anzahl verschickter Queue Nachrichten totalSize=Gesamtgröße averageMessageSize=Durchschnittsgrösse einer Nachricht @@ -1283,8 +1300,11 @@ removeResult=Löschergebnis: {0} {1} uploadSuccessful=Hochladen erfolgreich fileUploadResult=Ergebnis des Hochladens: {0} {1}\nIm Fehlerfall bitte die Konfiguration für "Datei Speicherung" prüfen. removeUploadedFile=Hochgeladene Datei löschen +showCompetitorBoatColumn=Boat des Teilnehmers anzeigen showCompetitorSailIdColumn=Segelnummer anzeigen showCompetitorSailIdColumnTooltip=Zeigt die Segelnummer sowie eine Nationalitäten-Flagge oder ein Teilnehmerbild an. Ein Teilnehmerbild wird hierbei bevorzugt angzeigt, falls vorhanden. Falls auch "{0}" ausgewählt ist, werden stets beide Bilder angezeigt. +showCompetitorShortNameColumn=Kurzen Teilnehmernamen anzeigen +showCompetitorShortNameColumnTooltip=Zeigt den kurzen Teilnehmernamen sowie eine Nationalitäten-Flagge oder ein Teilnehmerbild an. Ein Teilnehmerbild wird hierbei bevorzugt angzeigt, falls vorhanden. Falls auch "{0}" ausgewählt ist, werden stets beide Bilder angezeigt. showCompetitorFullNameColumn=Vollständigen Teilnehmernamen anzeigen alwaysShowCompetitorNationalityColumn=Immer Nationalität des Teilnehmers anzeigen alwaysShowCompetitorNationalityColumnTooltip=Sowohl Nationalität des Teilnehmers als auch Teilnehmerbild anzeigen, sofern beides verfügbar ist @@ -1474,6 +1494,7 @@ actionWatch=Verfolgen actionAnalyze=Analysieren denoteAllRacesForRaceLogTrackingShorctut=Alle rennen für racelog tracking markieren registerCompetitors=Teilnehmer anmelden +registerBoats=Boote anmelden doYouWantToCreateADefaultRegattaLeaderboard=Möchten Sie eine Standardrangliste für die Regatta anlegen? copyCourse=Kurs kopieren copyCompetitors=Teilnehmer kopieren @@ -1530,7 +1551,9 @@ transparentBufferLineOnHover=Transparente Pufferlinien bei Maus über Hilfslinie bufferLineStrokeWeight=Linienbreite der Pufferlinien profile=Profil showOnlyCompetitorsOfLog=Nur Teilnehmer aus Log anzeigen +showOnlyCompetitorsOfLog=Nur Boote aus Log anzeigen confirmLosingCompetitorEditsWhenTogglingLogBasedView=Damit werden alle aktuellen, noch nicht gespeicherten\nÄnderungen an der Teilnehmerliste zurückgesetzt. Wirklich fortfahren? +confirmLosingBoatEditsWhenTogglingLogBasedView=Damit werden alle aktuellen, noch nicht gespeicherten\nÄnderungen an der Bootsliste zurückgesetzt. Wirklich fortfahren? removalOfMarkDisabledMayBeUsedInRaces=Löschung deaktiviert, Bahnmarke wird in folgenden anderen Wettfahrten benutzt: {0} scoringSchemeHighPointFirstGetsTwelveOrEight=High-Point System 12 Punkte scoringSchemeHighPointFirstGetsTwelveOrEightDescription=Scores als Punkte. Der Gewinner bekommt 12 Punkte. @@ -1538,7 +1561,7 @@ scoringSchemeHighPointFirstGetsTwelveOrEight2017=High-Point System 12 Punkte ESS scoringSchemeHighPointFirstGetsTwelveOrEight2017Description=Scores als Punkte. Der Gewinner bekommt 12 Punkte. scoringSchemeHighPointEssOverall12=High Point System ESS Gesamtwertung (12) scoringSchemeHighPointEssOverall12Description=High Point System, der Gewinner eines Acts erhält 12 Punkte, der Zweite 11 und so weiter. -pleaseCreateAtLeastOneMappingBy=Bitte erstellen Sie mindestens eine Zuordnung, indem Sie zunächst einen Track auf der linken Seite und anschließend einen Teilnehmer oder eine Tonne auf der rechten Seite auswählen +pleaseCreateAtLeastOneMappingBy=Bitte erstellen Sie mindestens eine Zuordnung, indem Sie zunächst einen Track auf der linken Seite und anschließend einen Teilnehmer, ein Boot oder eine Tonne auf der rechten Seite auswählen createDefaultLeaderboardGroup=Möchten Sie eine Default-Ranglisten-Gruppe für ihr Event anlegen? matcherType=Matcher-Typ matchingDevices=Passende Geräte @@ -1833,6 +1856,7 @@ showRaceRankColumn=Rennplazierungsspalte anzeigen unknownLeaderboardType=Unbekannter Ranglistentyp {0} selectCompetitorsToEliminate=Zu eliminierende Teilnehmer auswählen trackedRaceAlreadyLinked=Das Rennen ist bereits mit einer Renn-Spalte verlinkt. Wollen Sie die Verbindung wirklich ändern? +boatAlreadyLinked=Das Boot ist bereits mit einem Teilnehmer verlinkt. reallyRemoveSeries=Möchten Sie die Serie ''{0}'' wirklich entfernen? Achtung: alle Ereignisprotokolle (RaceLogs) der zugehörigen Renn-Spalten werden unwiederbringlich gelöscht! maneuverSpeedDetails=Manöver Geschwindigkeitsdetails maneuverDirectionNormalization=Manöver Richtungsnormierung @@ -2035,6 +2059,11 @@ importFinishedMessage=Erfolgreich Expeditionsdaten eingelesen importFinishedGotoRaceboard=Raceboard öffnen importFinishedGotoEvent=Event öffnen chooseAName=Namen auswählen +errorFillingRaceLogsFromPairingList=Fehler beim Befüllen der Rennprotokolle aus der Gegner-Paarungsliste: {0} +successfullyFilledRaceLogsFromPairingList=Rennprotokolle erfolgreich aus der Gegner-Paarungsliste befüllt +errorFetchingRaceDisplayNamedFromLeaderboard=Fehler beim Laden der Anzeigenamen der Rennen aus der Rangliste: {0} +errorCalculatingPairingListTemplate=Fehler beim Berechnen der Gegner-Paarungsliste: {0} +errorCreatingPairingList=Fehler beim Erstellen der Gegner-Paarungsliste: {0} unexpectedErrorDuringFileImport=Ein unerwarteter Fehler ist beim Datei-Import aufgetreten. couldNotDownload=Konnte Datei nicht laden: {0} sliceRace=Rennen schneiden diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ja.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ja.properties index 1f262f27459..248dfd42b2a 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ja.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ja.properties @@ -38,6 +38,8 @@ carry=累積 competitor=競技者 competitors=競技者 competitorsSelected={0}/{1} の競技者が選択されました。 +boat=Boat +boats=Boats noSuchLeaderboard=そのようなリーダーボードはありません leaderboardConfiguration=リーダーボード設定 newDotDotDot=新規... @@ -100,6 +102,7 @@ windwardDistanceToGoInMetersTooltip=アップウィンド/ダウンウィンド windwardDistanceToCompetitorFarthestAheadInMeters=距離差 windwardDistanceToCompetitorFarthestAheadInMetersTooltip=アップウィンド/ダウンウィンドレグに対するレース内で最も遠くの前方にいる競技者までの競技者の風上/風下距離、またはリーチングレグに対するアロングトラック距離です。\n最も遠くの前方にいる競技者は、ハンディキャップランキングが実施されている場合には必ずしもレースの首位艇ではないことに注意してください。 name=名称 +shortName=Short name leaveEmptyForDefault=デフォルトでは空のままにします trackNewEvent=新規イベント追跡 trackableRaces=追跡可能レース diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties index 0631ec9eccb..c5c5678f34b 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties @@ -38,6 +38,8 @@ carry=Накоплено competitor=Участник competitors=Участники competitorsSelected=Выбрано участников: {0}/{1}. +boat=Boat +boats=Boats noSuchLeaderboard=Нет такой таблицы лидеров leaderboardConfiguration=Конфигурация таблицы лидеров newDotDotDot=Создать... @@ -100,6 +102,7 @@ windwardDistanceToGoInMetersTooltip=Расстояние от текущей п windwardDistanceToCompetitorFarthestAheadInMeters=Отставание windwardDistanceToCompetitorFarthestAheadInMetersTooltip=Расстояние от участника до первой лодки в гонке с учетом направления ветра.\nПервая лодка не обязательно лидирует в гонке, если применяется ранжирование с гандикапом. name=Название +shortName=Short name leaveEmptyForDefault=Чтобы задать название по умолчанию, оставьте пустым trackNewEvent=Отслеживать новое событие trackableRaces=Гонки с возможностью отслеживания @@ -414,7 +417,7 @@ successfullyUpdatedScores=Оценки успешно обновлены errorUpdatingScoresForLeaderboard=Ошибка при обновлении оценок для таблицы лидеров {0}: {1} maneuverTypesToShowWhenCompetitorIsClicked=Типы маневров для отображения при щелчке участника simulateAsLiveRace=Симулировать как текущую гонку -simulateWithOffset=Сдвиг перед стартом в минутах: +simulateWithOffset=Сдвиг перед стартом в минутах: boatClassDoesNotMatchSelectedRegatta=Выбранные гонки содержат классы лодок, не совпадающие с классом лодок ''{0}'' выбранной регаты. Гонки не будут загружены. regattaExistForSelectedBoatClass=Для выбранных классов лодок имеется минимум одна регата. Создать регаты по умолчанию для соответствующих гонок? reload=Перезагрузить @@ -668,7 +671,7 @@ passingInstructions=Передача инструкций refreshNow=Обновить сейчас stopUpdating=Прекратить обновление startUpdating=Начать обновление -currentTime=Текущее время: +currentTime=Текущее время: tractracUsername=Имя пользователя TracTrac tractracPassword=Пароль TracTrac operatorEquals=Равно @@ -703,7 +706,7 @@ pleaseEnterANumber=Введите число. competitorsFilter=Фильтр участников flagStatus=Статус флага additionalInformation=Инфо -windFixListingDescription=Замеры ветра (первые 3, последние 3, истинные значения) +windFixListingDescription=Замеры ветра (первые 3, последние 3, истинные значения) noWindFixesAvailable=Нет доступных замеров ветра. distanceToStarboardEndOfStartlineWhenPassingStart=До правой стороны линии при старте distanceToStarboardEndOfStartlineWhenPassingStartTooltip=Дистанция до правой стороны линии старта или отметки старта при прохождении старта участником @@ -741,7 +744,7 @@ oscillating=Колебания amplitude=Амплитуда frequency=Частота noise=Шум -sailing=Плавание +sailing=Плавание optimality=Оптимум maximumTurns=Максимум поворотов raceDirection=Направление гонки @@ -750,12 +753,12 @@ downWind=По ветру strategies=Стратегии omniscient=Всезнайка opportunistic=Авантюрист -mixed=Смешанная +mixed=Смешанная maps=Карты -startEnd=Начало-конец -racecourse=Курс -summary=Сводка -replay=Воспроизвести +startEnd=Начало-конец +racecourse=Курс +summary=Сводка +replay=Воспроизвести display=Просмотреть simulatorPolarHeader=Полярная диаграмма show=Показать @@ -1694,7 +1697,7 @@ urlForLanguage=URL-адрес для языка ''{0}'' notificationRegattaConfigurationUpdatedUsingByMarks=Активная конфигурация регаты была настроена с использованием дизайнера курса "По отметкам". warningOverrideRegattaConfigurationCourseDesignerToByMarks=Активная конфигурация регаты уже существует, но с неверно выбранным дизайнером курса.\nПереопределить выбор дизайнера курса на "По отметкам"? allRacesHaveBeenDenoted=Отслеживание журнала всех гонок отменено. -chartSelectionHint=Совет: таблицу лидеров (на вкладке ТАБЛИЦА ЛИДЕРОВ) также можно использовать для выбора участников, отображаемых в диаграмме. +chartSelectionHint=Совет: таблицу лидеров (на вкладке ТАБЛИЦА ЛИДЕРОВ) также можно использовать для выбора участников, отображаемых в диаграмме. chooseALanguage=Выбор языка solutions=Решения blog=Блог diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_zh.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_zh.properties index b5f6b0ad8a7..f6a9f037031 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_zh.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_zh.properties @@ -38,6 +38,8 @@ carry=累积 competitor=参赛队 competitors=参赛队 competitorsSelected=已选择{0}/{1}支参赛队。 +boat=Boat +boats=Boats noSuchLeaderboard=没有积分榜 leaderboardConfiguration=积分榜配置 newDotDotDot=新建... @@ -100,6 +102,7 @@ windwardDistanceToGoInMetersTooltip=参赛队的当前位置分别与迎/顺风 windwardDistanceToCompetitorFarthestAheadInMeters=差距 windwardDistanceToCompetitorFarthestAheadInMetersTooltip=迎/顺风航段中参赛队分别与比赛轮次中最前方参赛队的上风/下风距离,或横风航段的沿轨道航行距离。\n请注意,如果设定差点排名,最前方的参赛队并不一定是比赛轮次的领先者。 name=名称 +shortName=Short name leaveEmptyForDefault=针对默认值留空 trackNewEvent=跟踪新活动 trackableRaces=可跟踪的比赛轮次 diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/charts/AbstractCompetitorRaceChart.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/charts/AbstractCompetitorRaceChart.java index 30109ea6297..28ebc6b7b4c 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/charts/AbstractCompetitorRaceChart.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/charts/AbstractCompetitorRaceChart.java @@ -46,8 +46,8 @@ import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; import com.sap.sailing.domain.common.dto.CompetitorDTO; import com.sap.sailing.gwt.ui.actions.GetCompetitorsRaceDataAction; import com.sap.sailing.gwt.ui.client.CompetitorSelectionChangeListener; -import com.sap.sailing.gwt.ui.client.CompetitorSelectionProvider; import com.sap.sailing.gwt.ui.client.DetailTypeFormatter; +import com.sap.sailing.gwt.ui.client.RaceCompetitorSelectionProvider; import com.sap.sailing.gwt.ui.client.SailingServiceAsync; import com.sap.sailing.gwt.ui.client.StringMessages; import com.sap.sailing.gwt.ui.shared.CompetitorRaceDataDTO; @@ -87,7 +87,7 @@ public abstract class AbstractCompetitorRaceChart parent, ComponentContext context, SailingServiceAsync sailingService, AsyncActionsExecutor asyncActionsExecutor, - CompetitorSelectionProvider competitorSelectionProvider, RegattaAndRaceIdentifier selectedRaceIdentifier, + RaceCompetitorSelectionProvider competitorSelectionProvider, RegattaAndRaceIdentifier selectedRaceIdentifier, Timer timer, TimeRangeWithZoomProvider timeRangeWithZoomProvider, final StringMessages stringMessages, ErrorReporter errorReporter, DetailType firstDetailType, DetailType secondDetailType, boolean compactChart, boolean allowTimeAdjust, diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/charts/MultiCompetitorRaceChart.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/charts/MultiCompetitorRaceChart.java index 28494189346..8a975f8ae94 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/charts/MultiCompetitorRaceChart.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/charts/MultiCompetitorRaceChart.java @@ -3,7 +3,7 @@ package com.sap.sailing.gwt.ui.client.shared.charts; import com.google.gwt.user.client.ui.Button; import com.sap.sailing.domain.common.DetailType; import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; -import com.sap.sailing.gwt.ui.client.CompetitorSelectionProvider; +import com.sap.sailing.gwt.ui.client.RaceCompetitorSelectionProvider; import com.sap.sailing.gwt.ui.client.SailingServiceAsync; import com.sap.sailing.gwt.ui.client.StringMessages; import com.sap.sse.gwt.client.ErrorReporter; @@ -38,7 +38,7 @@ public class MultiCompetitorRaceChart extends AbstractCompetitorRaceChart parent, ComponentContext context, MultiCompetitorRaceChartLifecycle lifecycle, SailingServiceAsync sailingService, AsyncActionsExecutor asyncActionsExecutor, - CompetitorSelectionProvider competitorSelectionProvider, RegattaAndRaceIdentifier selectedRaceIdentifier, + RaceCompetitorSelectionProvider competitorSelectionProvider, RegattaAndRaceIdentifier selectedRaceIdentifier, Timer timer, TimeRangeWithZoomProvider timeRangeWithZoomProvider, final StringMessages stringMessages, final ErrorReporter errorReporter, boolean compactChart, boolean allowTimeAdjust, final String leaderboardGroupName, String leaderboardName) { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/BoatOverlay.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/BoatOverlay.java index 68752b20a11..01729f1d788 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/BoatOverlay.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/BoatOverlay.java @@ -11,7 +11,7 @@ import com.sap.sailing.domain.common.Distance; import com.sap.sailing.domain.common.LegType; import com.sap.sailing.domain.common.Tack; import com.sap.sailing.domain.common.dto.BoatClassDTO; -import com.sap.sailing.domain.common.dto.CompetitorDTO; +import com.sap.sailing.domain.common.dto.BoatDTO; import com.sap.sailing.gwt.ui.shared.GPSFixDTOWithSpeedWindTackAndLegType; import com.sap.sailing.gwt.ui.shared.SpeedWithBearingDTO; import com.sap.sailing.gwt.ui.shared.racemap.BoatClassVectorGraphics; @@ -69,9 +69,9 @@ public class BoatOverlay extends CanvasOverlayV3 { */ private Double boatDrawingAngle; - public BoatOverlay(final MapWidget map, int zIndex, final CompetitorDTO competitorDTO, Color color, CoordinateSystem coordinateSystem) { + public BoatOverlay(final MapWidget map, int zIndex, final BoatDTO boatDTO, Color color, CoordinateSystem coordinateSystem) { super(map, zIndex, coordinateSystem); - this.boatClass = competitorDTO.getBoatClass(); + this.boatClass = boatDTO.getBoatClass(); this.color = color; boatScaleAndSizePerZoomCache = new HashMap>(); boatVectorGraphics = BoatClassVectorGraphicsResolver.resolveBoatClassVectorGraphics(boatClass.getName()); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/CompetitorInfoOverlays.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/CompetitorInfoOverlays.java index 6f98f9c4263..b2466e1e748 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/CompetitorInfoOverlays.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/CompetitorInfoOverlays.java @@ -135,7 +135,7 @@ public class CompetitorInfoOverlays implements QuickRanksListener { */ private String createInfoText(CompetitorDTO competitorDTO) { StringBuilder infoText = new StringBuilder(); - infoText.append(competitorDTO.getSailID()).append("\n"); + infoText.append(competitorDTO.getShortInfo()).append("\n"); infoText.append(NumberFormatterFactory.getDecimalFormat(1).format(getLastPosition(competitorDTO).speedWithBearing.speedInKnots)) .append(" ").append(stringMessages.knotsUnit()).append("\n"); final Integer rank = ranks.get(competitorDTO.getIdAsString()); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceCompetitorSet.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceCompetitorSet.java index 515357f8f07..f35a50c9d04 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceCompetitorSet.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceCompetitorSet.java @@ -11,6 +11,7 @@ import com.sap.sailing.domain.common.RaceCompetitorIdsAsStringWithMD5Hash; import com.sap.sailing.domain.common.dto.CompetitorDTO; import com.sap.sailing.gwt.ui.client.CompetitorSelectionChangeListener; import com.sap.sailing.gwt.ui.client.CompetitorSelectionProvider; +import com.sap.sailing.gwt.ui.client.RaceCompetitorSelectionProvider; import com.sap.sse.common.filter.Filter; import com.sap.sse.common.filter.FilterSet; @@ -33,7 +34,7 @@ import com.sap.sse.common.filter.FilterSet; public class RaceCompetitorSet extends RaceCompetitorIdsAsStringWithMD5Hash { private static final long serialVersionUID = 3357742414149799988L; - private CompetitorSelectionProvider competitorSelection; + private RaceCompetitorSelectionProvider competitorSelection; /** * A subset of the competitor selection's {@link CompetitorSelectionProvider#getAllCompetitors()} describing the @@ -61,7 +62,7 @@ public class RaceCompetitorSet extends RaceCompetitorIdsAsStringWithMD5Hash { * {@link #setIdsAsStringsOfCompetitorsInRace(Iterable)} has been received may this set become adjusted to the * actual subset participating in the race. */ - public RaceCompetitorSet(CompetitorSelectionProvider competitorSelection) { + public RaceCompetitorSet(RaceCompetitorSelectionProvider competitorSelection) { super(); this.competitorsForRaceDefinedListeners = new HashSet<>(); this.competitorSelection = competitorSelection; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java index 99af6bc397a..1b15454fcf1 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java @@ -80,6 +80,7 @@ import com.sap.sailing.domain.common.Position; import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; import com.sap.sailing.domain.common.WindSource; import com.sap.sailing.domain.common.WindSourceType; +import com.sap.sailing.domain.common.dto.BoatDTO; import com.sap.sailing.domain.common.dto.CompetitorDTO; import com.sap.sailing.domain.common.dto.LeaderboardDTO; import com.sap.sailing.domain.common.impl.DegreeBearingImpl; @@ -97,6 +98,7 @@ import com.sap.sailing.gwt.ui.client.CompetitorSelectionChangeListener; import com.sap.sailing.gwt.ui.client.CompetitorSelectionProvider; import com.sap.sailing.gwt.ui.client.ManeuverTypeFormatter; import com.sap.sailing.gwt.ui.client.NumberFormatterFactory; +import com.sap.sailing.gwt.ui.client.RaceCompetitorSelectionProvider; import com.sap.sailing.gwt.ui.client.RaceTimesInfoProviderListener; import com.sap.sailing.gwt.ui.client.RequiresDataInitialization; import com.sap.sailing.gwt.ui.client.SailingServiceAsync; @@ -315,10 +317,10 @@ public class RaceMap extends AbstractCompositeComponent impleme private Map> lastDouglasPeuckerResult; - private final CompetitorSelectionProvider competitorSelection; + private final RaceCompetitorSelectionProvider competitorSelection; private final RaceCompetitorSet raceCompetitorSet; - + /** * Used to check if the first initial zoom to the mark markers was already done. */ @@ -447,8 +449,8 @@ public class RaceMap extends AbstractCompositeComponent impleme public RaceMap(Component parent, ComponentContext context, RaceMapLifecycle raceMapLifecycle, RaceMapSettings raceMapSettings, SailingServiceAsync sailingService, AsyncActionsExecutor asyncActionsExecutor, - ErrorReporter errorReporter, Timer timer, CompetitorSelectionProvider competitorSelection, RaceCompetitorSet raceCompetitorSet, - StringMessages stringMessages, RegattaAndRaceIdentifier raceIdentifier, + ErrorReporter errorReporter, Timer timer, RaceCompetitorSelectionProvider competitorSelection, + RaceCompetitorSet raceCompetitorSet, StringMessages stringMessages, RegattaAndRaceIdentifier raceIdentifier, RaceMapResources raceMapResources, boolean showHeaderPanel, QuickRanksDTOProvider quickRanksDTOProvider) { this(parent, context, raceMapLifecycle, raceMapSettings, sailingService, asyncActionsExecutor, errorReporter, timer, competitorSelection, raceCompetitorSet, stringMessages, raceIdentifier, raceMapResources, @@ -458,7 +460,7 @@ public class RaceMap extends AbstractCompositeComponent impleme public RaceMap(Component parent, ComponentContext context, RaceMapLifecycle raceMapLifecycle, RaceMapSettings raceMapSettings, SailingServiceAsync sailingService, AsyncActionsExecutor asyncActionsExecutor, - ErrorReporter errorReporter, Timer timer, CompetitorSelectionProvider competitorSelection, RaceCompetitorSet raceCompetitorSet, + ErrorReporter errorReporter, Timer timer, RaceCompetitorSelectionProvider competitorSelection, RaceCompetitorSet raceCompetitorSet, StringMessages stringMessages, RegattaAndRaceIdentifier raceIdentifier, RaceMapResources raceMapResources, boolean showHeaderPanel, QuickRanksDTOProvider quickRanksDTOProvider, Consumer showWindChartForProvider) { super(parent, context); @@ -817,7 +819,7 @@ public class RaceMap extends AbstractCompositeComponent impleme return Collections.unmodifiableMap(boatOverlays); } - protected CompetitorSelectionProvider getCompetitorSelection() { + protected RaceCompetitorSelectionProvider getCompetitorSelection() { return competitorSelection; } @@ -1489,7 +1491,8 @@ public class RaceMap extends AbstractCompositeComponent impleme lastBoatFix = getBoatFix(visibleLeaderInfo.getB(), date); } if (isVisibleLeaderInfoComplete && isLegTypeKnown && lastBoatFix != null && lastBoatFix.speedWithBearing != null) { - Distance distanceFromBoatPositionInKm = visibleLeaderInfo.getB().getBoatClass().getHullLength(); // one hull length + BoatDTO boat = competitorSelection.getBoat(visibleLeaderInfo.getB()); + Distance distanceFromBoatPositionInKm = boat.getBoatClass().getHullLength(); // one hull length // implement and use Position.translateRhumb() double bearingOfBoatInDeg = lastBoatFix.speedWithBearing.bearingInDegrees; LatLng boatPosition = coordinateSystem.toLatLng(lastBoatFix.position); @@ -2079,7 +2082,8 @@ public class RaceMap extends AbstractCompositeComponent impleme } private BoatOverlay createBoatOverlay(int zIndex, final CompetitorDTO competitorDTO, DisplayMode displayMode) { - final BoatOverlay boatCanvas = new BoatOverlay(map, zIndex, competitorDTO, competitorSelection.getColor(competitorDTO, raceIdentifier), coordinateSystem); + final BoatDTO boatOfCompetitor = competitorSelection.getBoat(competitorDTO); + final BoatOverlay boatCanvas = new BoatOverlay(map, zIndex, boatOfCompetitor, competitorSelection.getColor(competitorDTO, raceIdentifier), coordinateSystem); boatCanvas.setDisplayMode(displayMode); boatCanvas.addClickHandler(event -> { GPSFixDTOWithSpeedWindTackAndLegType latestFixForCompetitor = getBoatFix(competitorDTO, timer.getTime()); @@ -2091,7 +2095,7 @@ public class RaceMap extends AbstractCompositeComponent impleme boatCanvas.addMouseOverHandler(new MouseOverMapHandler() { @Override public void onEvent(MouseOverMapEvent event) { - map.setTitle(competitorDTO.getSailID()); + map.setTitle(boatOfCompetitor.getSailId()); } }); boatCanvas.addMouseOutMoveHandler(new MouseOutMapHandler() { @@ -2238,7 +2242,7 @@ public class RaceMap extends AbstractCompositeComponent impleme private Widget getInfoWindowContent(CompetitorDTO competitorDTO, GPSFixDTOWithSpeedWindTackAndLegType lastFix) { final VerticalPanel vPanel = new VerticalPanel(); vPanel.add(createInfoWindowLabelAndValue(stringMessages.competitor(), competitorDTO.getName())); - vPanel.add(createInfoWindowLabelAndValue(stringMessages.sailNumber(), competitorDTO.getSailID())); + vPanel.add(createInfoWindowLabelAndValue(stringMessages.sailNumber(), competitorSelection.getBoat(competitorDTO).getSailId())); final Integer rank = getRank(competitorDTO); if (rank != null) { vPanel.add(createInfoWindowLabelAndValue(stringMessages.rank(), String.valueOf(rank))); @@ -2858,6 +2862,7 @@ public class RaceMap extends AbstractCompositeComponent impleme @Override public Polyline createTail(final CompetitorDTO competitor, List points) { + final BoatDTO boat = competitorSelection.getBoat(competitor); PolylineOptions options = createTailStyle(competitor, displayHighlighted(competitor)); Polyline result = Polyline.newInstance(options); MVCArray pointsAsArray = MVCArray.newInstance(points.toArray(new LatLng[0])); @@ -2875,7 +2880,7 @@ public class RaceMap extends AbstractCompositeComponent impleme result.addMouseOverHandler(new MouseOverMapHandler() { @Override public void onEvent(MouseOverMapEvent event) { - map.setTitle(competitor.getSailID() + ", " + competitor.getName()); + map.setTitle(boat.getSailId() + ", " + competitor.getName()); } }); resultHoverline.addMouseOutMoveHandler(new MouseOutMapHandler() { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/BoatFetcher.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/BoatFetcher.java new file mode 100644 index 00000000000..4d9466ccaa4 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/BoatFetcher.java @@ -0,0 +1,7 @@ +package com.sap.sailing.gwt.ui.leaderboard; + +import com.sap.sailing.domain.common.dto.BoatDTO; + +public interface BoatFetcher { + BoatDTO getBoat(T t); +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardPanel.java index a89acd0cf49..87870c8a5b0 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardPanel.java @@ -68,6 +68,7 @@ import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; import com.sap.sailing.domain.common.SortingOrder; import com.sap.sailing.domain.common.Tack; import com.sap.sailing.domain.common.dto.AbstractLeaderboardDTO; +import com.sap.sailing.domain.common.dto.BoatDTO; import com.sap.sailing.domain.common.dto.CompetitorDTO; import com.sap.sailing.domain.common.dto.FleetDTO; import com.sap.sailing.domain.common.dto.IncrementalOrFullLeaderboardDTO; @@ -292,8 +293,9 @@ public abstract class LeaderboardPanel extends A */ private boolean showAddedScores; - private boolean showCompetitorSailId; + private boolean showCompetitorShortName; private boolean showCompetitorFullName; + private boolean showCompetitorBoatInfo; /** * Remembers whether the auto-expand of the pre-selected race (see {@link #autoExpandPreSelectedRace}) or last @@ -678,8 +680,9 @@ public abstract class LeaderboardPanel extends A } setShowCompetitorNationality(newSettings.isShowCompetitorNationality()); setShowAddedScores(newSettings.isShowAddedScores()); - setShowCompetitorSailId(newSettings.isShowCompetitorSailIdColumn()); + setShowCompetitorShortName(newSettings.isShowCompetitorShortNameColumn()); setShowCompetitorFullName(newSettings.isShowCompetitorFullNameColumn()); + setShowCompetitorBoatInfo(newSettings.isShowCompetitorBoatInfoColumn()); final List> columnsToExpandAgain = new ArrayList>(); for (int i = 0; i < getLeaderboardTable().getColumnCount(); i++) { Column c = getLeaderboardTable().getColumn(i); @@ -740,8 +743,9 @@ public abstract class LeaderboardPanel extends A setShowCompetitorNationality(newSettings.isShowCompetitorNationality()); setShowAddedScores(newSettings.isShowAddedScores()); - setShowCompetitorSailId(newSettings.isShowCompetitorSailIdColumn()); + setShowCompetitorShortName(newSettings.isShowCompetitorShortNameColumn()); setShowCompetitorFullName(newSettings.isShowCompetitorFullNameColumn()); + setShowCompetitorBoatInfo(newSettings.isShowCompetitorBoatInfoColumn()); if (newSettings.getManeuverDetailsToShow() != null) { setValuesWithReferenceOrder(newSettings.getManeuverDetailsToShow(), @@ -846,15 +850,13 @@ public abstract class LeaderboardPanel extends A } /** - * Shows the country flag and sail ID, if present - * + * Shows the country flag and a competitor short info (short name or sailId), if present * @author Axel Uhl (d043530) - * */ - private class SailIDColumn extends LeaderboardSortableColumnWithMinMax { + private class CompetitorInfoWithFlagColumn extends LeaderboardSortableColumnWithMinMax { private final CompetitorFetcher competitorFetcher; - protected SailIDColumn(CompetitorFetcher competitorFetcher, LeaderBoardStyle style) { + protected CompetitorInfoWithFlagColumn(CompetitorFetcher competitorFetcher, LeaderBoardStyle style) { super(new TextCell(), SortingOrder.ASCENDING, LeaderboardPanel.this); this.competitorFetcher = competitorFetcher; // This style is adding to avoid contained images CSS property "max-width: 100%", which could cause @@ -867,11 +869,11 @@ public abstract class LeaderboardPanel extends A return new InvertibleComparatorAdapter() { @Override public int compare(T o1, T o2) { - return competitorFetcher.getCompetitor(o1).getSailID() == null - ? competitorFetcher.getCompetitor(o2).getSailID() == null ? 0 : -1 - : competitorFetcher.getCompetitor(o2).getSailID() == null ? 1 - : Collator.getInstance().compare(competitorFetcher.getCompetitor(o1).getSailID(), - competitorFetcher.getCompetitor(o2).getSailID()); + return competitorFetcher.getCompetitor(o1).getShortInfo() == null + ? competitorFetcher.getCompetitor(o2).getShortInfo() == null ? 0 : -1 + : competitorFetcher.getCompetitor(o2).getShortInfo() == null ? 1 + : Collator.getInstance().compare(competitorFetcher.getCompetitor(o1).getShortInfo(), + competitorFetcher.getCompetitor(o2).getShortInfo()); } }; } @@ -879,7 +881,7 @@ public abstract class LeaderboardPanel extends A @Override public SafeHtmlHeader getHeader() { return new SafeHtmlHeaderWithTooltip(SafeHtmlUtils.fromString(stringMessages.competitor()), - stringMessages.sailIdColumnTooltip()); + stringMessages.shortName()); } @Override @@ -905,7 +907,7 @@ public abstract class LeaderboardPanel extends A style.renderFlagImage(flagImageURL, sb, competitor); sb.appendHtmlConstant(" "); } - sb.appendEscaped(competitor.getSailID()); + sb.appendEscaped(competitor.getShortInfo()); if (boatColorShown) { sb.appendHtmlConstant("
"); } @@ -913,10 +915,78 @@ public abstract class LeaderboardPanel extends A @Override public String getValue(T object) { - return competitorFetcher.getCompetitor(object).getSailID(); + return competitorFetcher.getCompetitor(object).getShortInfo(); } } + /** + * A sortable leaderboard column showing boat information + * @author Frank Mittag + */ + private class BoatInfoColumn extends LeaderboardSortableColumnWithMinMax { + private final BoatFetcher boatFetcher; + private final CompetitorFetcher competitorFetcher; + + public BoatInfoColumn(CompetitorFetcher competitorFetcher, BoatFetcher boatFetcher, LeaderBoardStyle style) { + super(new TextCell(), SortingOrder.ASCENDING, LeaderboardPanel.this); + this.competitorFetcher = competitorFetcher; + this.boatFetcher = boatFetcher; + // This style is adding to avoid contained images CSS property "max-width: 100%", which could cause + // an overflow to the next column (see https://bugzilla.sapsailing.com/bugzilla/show_bug.cgi?id=3537) + setCellStyleNames(style.getTableresources().cellTableStyle().cellTableSailIdColumn()); + } + + @Override + public SafeHtmlHeader getHeader() { + return new SafeHtmlHeaderWithTooltip(SafeHtmlUtils.fromString(stringMessages.boat()), + stringMessages.boat()); + } + + @Override + public void render(Context context, T object, SafeHtmlBuilder sb) { + BoatDTO boat = boatFetcher.getBoat(object); + CompetitorDTO competitor = competitorFetcher.getCompetitor(object); + boolean boatColorShown = renderBoatColorIfNecessary(competitor, sb); + sb.appendEscaped(getShortInfo(boat)); + if (boatColorShown) { + sb.appendHtmlConstant("
"); + } + } + + private String getShortInfo(BoatDTO boat) { + final String result; + if (boat.getSailId() != null) { + result = boat.getSailId(); + } else if (boat.getName() != null) { + result = boat.getName(); + } else { + result = null; + } + return result; + } + + @Override + public InvertibleComparator getComparator() { + return new InvertibleComparatorAdapter() { + @Override + public int compare(T o1, T o2) { + return getShortInfo(boatFetcher.getBoat(o1)) == null + ? getShortInfo(boatFetcher.getBoat(o2)) == null ? 0 : -1 + : getShortInfo(boatFetcher.getBoat(o2)) == null ? 1 + : Collator.getInstance().compare(getShortInfo(boatFetcher.getBoat(o1)), + getShortInfo(boatFetcher.getBoat(o2))); + } + }; + } + + @Override + public String getValue(T object) { + BoatDTO boat = boatFetcher.getBoat(object); + return boat != null ? boat.getName() : null; + } + } + + protected void processStyleForRaceColumnWithReasonForMaxPoints(boolean isDiscarded, SafeStylesBuilder ssb) { ssb.opacity(0.5d); } @@ -2159,12 +2229,12 @@ public abstract class LeaderboardPanel extends A this.showAddedScores = showAddedScores; } - protected boolean isShowCompetitorSailId() { - return showCompetitorSailId; + protected boolean isShowCompetitorShortName() { + return showCompetitorShortName; } - private void setShowCompetitorSailId(boolean showCompetitorSailId) { - this.showCompetitorSailId = showCompetitorSailId; + private void setShowCompetitorShortName(boolean showCompetitorShortName) { + this.showCompetitorShortName = showCompetitorShortName; } protected boolean isShowCompetitorFullName() { @@ -2175,6 +2245,16 @@ public abstract class LeaderboardPanel extends A this.showCompetitorFullName = showCompetitorFullName; } + protected boolean isShowCompetitorBoatInfo() { + return showCompetitorBoatInfo; + } + + protected abstract boolean canShowCompetitorBoatInfo(); + + private void setShowCompetitorBoatInfo(boolean showCompetitorBoatInfo) { + this.showCompetitorBoatInfo = showCompetitorBoatInfo; + } + /** * The time point for which the leaderboard currently shows results. In {@link PlayModes#Replay replay mode} this is * the {@link #timer}'s time point. In {@link PlayModes#Live live mode} the {@link #timer}'s time is quantized to @@ -2356,7 +2436,7 @@ public abstract class LeaderboardPanel extends A for (RaceColumn columnToCollapseAndExpandAgain : columnsToCollapseAndExpandAgain) { columnToCollapseAndExpandAgain.changeExpansionState(/* expand */ false); } - competitorSelectionProvider.setCompetitors(leaderboard.competitors, /* listenersNotToNotify */this); + updateCompetitors(leaderboard); if (!initialCompetitorFilterHasBeenApplied) { applyTop30FilterIfCompetitorSizeGreaterEqual40(leaderboard); initialCompetitorFilterHasBeenApplied = true; @@ -2481,6 +2561,13 @@ public abstract class LeaderboardPanel extends A } } + /** + * Updates the competitors in the competitorSelectionProvider with the competitors received from the {@link LeaderboardDTO} + */ + protected void updateCompetitors(LeaderboardDTO leaderboard) { + competitorSelectionProvider.setCompetitors(leaderboard.competitors, /* listenersNotToNotify */this); + } + /** * Due to a course change, a race may change its number of legs. All expanded race columns that show leg columns and * whose leg count changed need to be collapsed before the leaderboard is replaced, and expanded afterwards again. @@ -2603,7 +2690,7 @@ public abstract class LeaderboardPanel extends A columnIndex = ensureRaceRankColumn(columnIndex); columnIndex = ensureSelectionCheckboxColumn(columnIndex); columnIndex = ensureRankColumn(columnIndex); - columnIndex = ensureSailIDAndCompetitorColumn(columnIndex); + columnIndex = ensureCompetitorInfoWithFlagColumnAndCompetitorColumn(columnIndex); columnIndex = updateCarryColumn(leaderboard, columnIndex); adjustOverallDetailColumns(leaderboard, columnIndex); // first remove race columns no longer needed: @@ -2929,23 +3016,19 @@ public abstract class LeaderboardPanel extends A /** * @return the 0-based index for the next column */ - private int ensureSailIDAndCompetitorColumn(int columnIndexWhereToInsertTheNextColumn) { - if (isShowCompetitorSailId()) { + private int ensureCompetitorInfoWithFlagColumnAndCompetitorColumn(int columnIndexWhereToInsertTheNextColumn) { + if (isShowCompetitorShortName()) { + CompetitorInfoWithFlagColumn competitorInfoWithFlagColumn; + CompetitorFetcher competitorFetcher = (LeaderboardRowDTO row) -> row.competitor; + competitorInfoWithFlagColumn = new CompetitorInfoWithFlagColumn(competitorFetcher, style); if (getLeaderboardTable().getColumnCount() <= columnIndexWhereToInsertTheNextColumn - || !(getLeaderboardTable() - .getColumn(columnIndexWhereToInsertTheNextColumn) instanceof SailIDColumn)) { - insertColumn(columnIndexWhereToInsertTheNextColumn, - new SailIDColumn(new CompetitorFetcher() { - @Override - public CompetitorDTO getCompetitor(LeaderboardRowDTO t) { - return t.competitor; - } - }, style)); + || !(getLeaderboardTable().getColumn(columnIndexWhereToInsertTheNextColumn) instanceof CompetitorInfoWithFlagColumn)) { + insertColumn(columnIndexWhereToInsertTheNextColumn, competitorInfoWithFlagColumn); } columnIndexWhereToInsertTheNextColumn++; } else { - if (getLeaderboardTable().getColumnCount() > columnIndexWhereToInsertTheNextColumn - && getLeaderboardTable().getColumn(columnIndexWhereToInsertTheNextColumn) instanceof SailIDColumn) { + if (getLeaderboardTable().getColumnCount() > columnIndexWhereToInsertTheNextColumn && getLeaderboardTable() + .getColumn(columnIndexWhereToInsertTheNextColumn) instanceof CompetitorInfoWithFlagColumn) { removeColumn(columnIndexWhereToInsertTheNextColumn); } } @@ -2962,6 +3045,23 @@ public abstract class LeaderboardPanel extends A removeColumn(columnIndexWhereToInsertTheNextColumn); } } + if (canShowCompetitorBoatInfo() && isShowCompetitorBoatInfo()) { + if (getLeaderboardTable().getColumnCount() <= columnIndexWhereToInsertTheNextColumn + || !(getLeaderboardTable() + .getColumn(columnIndexWhereToInsertTheNextColumn) instanceof LeaderboardPanel.BoatInfoColumn)) { + CompetitorFetcher competitorFetcher = (LeaderboardRowDTO row) -> row.competitor; + BoatFetcher boatFetcher = (LeaderboardRowDTO row) -> row.boat; + BoatInfoColumn boatInfoColumn = new BoatInfoColumn(competitorFetcher, boatFetcher, style); + insertColumn(columnIndexWhereToInsertTheNextColumn, boatInfoColumn); + } + columnIndexWhereToInsertTheNextColumn++; + } else { + if (getLeaderboardTable().getColumnCount() > columnIndexWhereToInsertTheNextColumn && getLeaderboardTable() + .getColumn(columnIndexWhereToInsertTheNextColumn) instanceof LeaderboardPanel.BoatInfoColumn) { + removeColumn(columnIndexWhereToInsertTheNextColumn); + } + } + return columnIndexWhereToInsertTheNextColumn; } @@ -2974,7 +3074,7 @@ public abstract class LeaderboardPanel extends A } })); } - + private void ensureTotalsColumn() { // add a totals column on the right if (getLeaderboardTable().getColumnCount() == 0 || !(getLeaderboardTable().getColumn( diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/MultiRaceLeaderboardPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/MultiRaceLeaderboardPanel.java index 97e25388fec..990e50b2a59 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/MultiRaceLeaderboardPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/MultiRaceLeaderboardPanel.java @@ -6,6 +6,7 @@ import java.util.List; import java.util.Map; import com.google.gwt.safehtml.shared.SafeHtmlBuilder; +import com.sap.sailing.domain.common.LeaderboardType; import com.sap.sailing.domain.common.dto.AbstractLeaderboardDTO; import com.sap.sailing.domain.common.dto.CompetitorDTO; import com.sap.sailing.domain.common.dto.LeaderboardDTO; @@ -87,9 +88,8 @@ public class MultiRaceLeaderboardPanel extends LeaderboardPanel getSettingsDialogComponent( MultiRaceLeaderboardSettings useTheseSettings) { return new MultiRaceLeaderboardSettingsDialogComponent((MultiRaceLeaderboardSettings) useTheseSettings, - leaderboard.getNamesOfRaceColumns(), stringMessages); + leaderboard.getNamesOfRaceColumns(), stringMessages, !leaderboard.canBoatsOfCompetitorsChangePerRace); } @Override diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/SingleRaceLeaderboardPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/SingleRaceLeaderboardPanel.java index 0fa781bbcd6..256b096dfe1 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/SingleRaceLeaderboardPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/SingleRaceLeaderboardPanel.java @@ -12,6 +12,7 @@ import com.sap.sailing.domain.common.InvertibleComparator; import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; import com.sap.sailing.domain.common.SortingOrder; import com.sap.sailing.domain.common.dto.AbstractLeaderboardDTO; +import com.sap.sailing.domain.common.dto.BoatDTO; import com.sap.sailing.domain.common.dto.CompetitorDTO; import com.sap.sailing.domain.common.dto.LeaderboardDTO; import com.sap.sailing.domain.common.dto.LeaderboardRowDTO; @@ -20,7 +21,7 @@ import com.sap.sailing.domain.common.impl.InvertibleComparatorAdapter; import com.sap.sailing.gwt.settings.client.leaderboard.LeaderboardSettings; import com.sap.sailing.gwt.settings.client.leaderboard.SingleRaceLeaderboardSettings; import com.sap.sailing.gwt.settings.client.leaderboard.SingleRaceLeaderboardSettingsDialogComponent; -import com.sap.sailing.gwt.ui.client.CompetitorSelectionProvider; +import com.sap.sailing.gwt.ui.client.RaceCompetitorSelectionProvider; import com.sap.sailing.gwt.ui.client.FlagImageResolver; import com.sap.sailing.gwt.ui.client.RaceTimesInfoProvider; import com.sap.sailing.gwt.ui.client.SailingServiceAsync; @@ -60,7 +61,7 @@ public class SingleRaceLeaderboardPanel extends LeaderboardPanel parent, ComponentContext context, SailingServiceAsync sailingService, AsyncActionsExecutor asyncActionsExecutor, SingleRaceLeaderboardSettings settings, boolean isEmbedded, RegattaAndRaceIdentifier preSelectedRace, - CompetitorSelectionProvider competitorSelectionProvider, Timer timer, String leaderboardGroupName, + RaceCompetitorSelectionProvider competitorSelectionProvider, Timer timer, String leaderboardGroupName, String leaderboardName, ErrorReporter errorReporter, StringMessages stringMessages, boolean showRaceDetails, CompetitorFilterPanel competitorSearchTextBox, boolean showSelectionCheckbox, RaceTimesInfoProvider optionalRaceTimesInfoProvider, boolean autoExpandLastRaceColumn, @@ -110,18 +111,46 @@ public class SingleRaceLeaderboardPanel extends LeaderboardPanel"); } return showBoatColor; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/pairinglist/PairingListEntryPoint.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/pairinglist/PairingListEntryPoint.java index 2da19932a28..d89e67d7570 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/pairinglist/PairingListEntryPoint.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/pairinglist/PairingListEntryPoint.java @@ -1,10 +1,7 @@ package com.sap.sailing.gwt.ui.pairinglist; -import java.util.ArrayList; import java.util.List; -import com.google.gwt.dom.client.Style; -import com.google.gwt.dom.client.Style.TextAlign; import com.google.gwt.dom.client.Style.Unit; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; @@ -12,31 +9,27 @@ import com.google.gwt.safehtml.shared.SafeHtmlUtils; import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.user.client.ui.Button; import com.google.gwt.user.client.ui.DockLayoutPanel; -import com.google.gwt.user.client.ui.Grid; import com.google.gwt.user.client.ui.HTML; import com.google.gwt.user.client.ui.HasHorizontalAlignment; -import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.RootLayoutPanel; import com.google.gwt.user.client.ui.ScrollPanel; import com.google.gwt.user.client.ui.VerticalPanel; -import com.sap.sailing.domain.common.dto.BoatDTO; -import com.sap.sailing.domain.common.dto.CompetitorDTO; -import com.sap.sailing.domain.common.dto.CompetitorDTOImpl; +import com.google.gwt.user.client.ui.Widget; import com.sap.sailing.domain.common.dto.PairingListDTO; import com.sap.sailing.gwt.common.authentication.FixedSailingAuthentication; import com.sap.sailing.gwt.common.authentication.SAPSailingHeaderWithAuthentication; +import com.sap.sailing.gwt.ui.adminconsole.PairingListPreviewDialog; import com.sap.sailing.gwt.ui.client.AbstractSailingEntryPoint; import com.sap.sailing.gwt.ui.client.StringMessages; import com.sap.sailing.gwt.ui.shared.StrippedLeaderboardDTO; import com.sap.sse.common.Color; -import com.sap.sse.common.Util.Pair; import com.sap.sse.gwt.settings.SettingsToUrlSerializer; public class PairingListEntryPoint extends AbstractSailingEntryPoint { private PairingListContextDefinition pairingListContextDefinition; - private StringMessages stringmessages = StringMessages.INSTANCE; + private StringMessages stringMessages = StringMessages.INSTANCE; private StrippedLeaderboardDTO strippedLeaderboardDTO; @Override @@ -69,7 +62,6 @@ public class PairingListEntryPoint extends AbstractSailingEntryPoint { pairingListContextDefinition.getLeaderboardName()); new FixedSailingAuthentication(getUserService(), header.getAuthenticationMenuView()); mainPanel.addNorth(header, 75); - VerticalPanel contentPanel = new VerticalPanel(); contentPanel.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_CENTER); contentPanel.setWidth("100%"); @@ -78,7 +70,6 @@ public class PairingListEntryPoint extends AbstractSailingEntryPoint { scrollPanel.add(contentPanel); sailingService.getPairingListFromRaceLogs(pairingListContextDefinition.getLeaderboardName(), new AsyncCallback() { - @Override public void onSuccess(PairingListDTO result) { if (strippedLeaderboardDTO != null) { @@ -87,22 +78,22 @@ public class PairingListEntryPoint extends AbstractSailingEntryPoint { @Override public void onFailure(Throwable caught) { HTML lbl = new HTML( - "

" + stringmessages.noPairingListAvailable() + "

"); + "

" + stringMessages.noPairingListAvailable() + "

"); lbl.getElement().getStyle().setColor(Color.BLACK.toString()); contentPanel.add(lbl); } @Override - public void onSuccess(List names) { + public void onSuccess(List raceDisplayNames) { Button btn = new Button(getStringMessages().print()); contentPanel.add(btn); - VerticalPanel pairingListPanel = createPairingListPanel(result, names); + Widget pairingListPanel = createPairingListPanel(result, raceDisplayNames); contentPanel.add(pairingListPanel); btn.addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { printPairingListGrid( - "
" + "
" + "" + SafeHtmlUtils.fromString(pairingListContextDefinition.getLeaderboardName()) .asString() @@ -129,97 +120,10 @@ public class PairingListEntryPoint extends AbstractSailingEntryPoint { mainPanel.add(scrollPanel); } - private VerticalPanel createPairingListPanel(PairingListDTO pairingListDTO, final List fleetnames) { - final List boats = pairingListDTO.getBoats(); - - final int flightCount = pairingListDTO.getPairingList().size(); - final int groupCount = pairingListDTO.getPairingList().get(0).size(); - final int boatCount = boats.size(); - - Grid pairingListGrid = new Grid(flightCount * groupCount + 1, (boatCount + 2)); - pairingListGrid.getElement().setId("grid"); - pairingListGrid.setCellPadding(15); - - int flightIndexInGrid = 1; - int groupIndex = 1; - int boatIndex = 0; - - for (BoatDTO boat : boats) { - pairingListGrid.setWidget(0, boatIndex + 2, new Label(boat.getName())); - pairingListGrid.getCellFormatter().getElement(0, boatIndex + 2).getStyle().setTextAlign(TextAlign.CENTER); - pairingListGrid.getCellFormatter().getElement(0, boatIndex + 2).getStyle().setPadding(10, Unit.PX); - if (boat.getColor() != null) { - pairingListGrid.getCellFormatter().getElement(0, boatIndex + 2).getStyle() - .setBackgroundColor(boat.getColor().getAsHtml()); - } else { - pairingListGrid.getCellFormatter().getElement(0, boatIndex + 2).getStyle() - .setBackgroundColor("#cecece"); - } - boatIndex++; - } - String color = ""; - pairingListGrid.getCellFormatter().getElement(0, 0).getStyle().setBackgroundColor("#cecece"); - pairingListGrid.getCellFormatter().getElement(0, 1).getStyle().setBackgroundColor("#cecece"); - for (List>> flight : pairingListDTO.getPairingList()) { - color = (color.equals("none") ? "#cecece" : "none"); - // setting up race - int currentRaceInGridCells = (((flightIndexInGrid - 1) * groupCount) + 1); - pairingListGrid.setWidget(currentRaceInGridCells, 0, - new Label(pairingListDTO.getRaceColumnNames().get(flightIndexInGrid - 1))); - pairingListGrid.getCellFormatter().getElement(currentRaceInGridCells, 0).getStyle().setPadding(5, Unit.PX); - pairingListGrid.getCellFormatter().getElement(currentRaceInGridCells, 0).getStyle() - .setBackgroundColor(color); - for (List> group : flight) { - // setting up fleet - pairingListGrid.getCellFormatter().getElement(groupIndex, 0).getStyle().setPadding(3, Unit.PX); - pairingListGrid.getCellFormatter().getElement(groupIndex, 0).getStyle().setBackgroundColor(color); - pairingListGrid.setWidget(groupIndex, 1, new Label(fleetnames.get(groupIndex - 1))); - // setting up fleets style - pairingListGrid.getCellFormatter().getElement(groupIndex, 1).getStyle().setPadding(3, Unit.PX); - pairingListGrid.getCellFormatter().getElement(groupIndex, 1).getStyle().setBackgroundColor(color); - - if (group.size() < boatCount) { - List boatsToRemove = new ArrayList<>(boats); - for (Pair competitorAndBoatPair : group) { - boatsToRemove.remove(competitorAndBoatPair.getB()); - } - for (BoatDTO boat : boatsToRemove) { - group.add(new Pair(new CompetitorDTOImpl(), boat)); - } - } - for (Pair competitorAndBoatPair : group) { - int boatIndexInGrid = boats.indexOf(competitorAndBoatPair.getB()) + 2; - if (competitorAndBoatPair.getA().getName() == null) { - pairingListGrid.setWidget(groupIndex, boatIndexInGrid, new Label(getStringMessages().empty())); - pairingListGrid.getCellFormatter().getElement(groupIndex, boatIndexInGrid).getStyle() - .setColor(Color.RED.toString()); - } else { - // TODO change competitor name to competitor shorthand symbol ( bug2822 ) - pairingListGrid.setWidget(groupIndex, boatIndexInGrid, - new Label(competitorAndBoatPair.getA().getSailID())); - } - pairingListGrid.getCellFormatter().getElement(groupIndex, boatIndexInGrid).getStyle() - .setFontWeight(Style.FontWeight.BOLD); - pairingListGrid.getCellFormatter().getElement(groupIndex, boatIndexInGrid).getStyle() - .setTextAlign(TextAlign.CENTER); - pairingListGrid.getCellFormatter().getElement(groupIndex, boatIndexInGrid).getStyle().setPadding(5, - Unit.PX); - pairingListGrid.getCellFormatter().getElement(groupIndex, boatIndexInGrid).getStyle() - .setBackgroundColor(color); - } - - groupIndex++; - } - flightIndexInGrid++; - } - - VerticalPanel pairingListPanel = new VerticalPanel(); - pairingListPanel.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_CENTER); - pairingListPanel.add(pairingListGrid); - pairingListPanel.setWidth("100%"); - pairingListPanel.ensureDebugId("PairingListPanel"); + private Widget createPairingListPanel(PairingListDTO pairingListDTO, final List raceDisplayNames) { + final Widget pairingListPanel = new PairingListPreviewDialog(pairingListDTO, raceDisplayNames, stringMessages, pairingListContextDefinition.getLeaderboardName()). + getPairingListGrid(); pairingListPanel.getElement().getStyle().setProperty("marginTop", "15px"); - return pairingListPanel; } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/EmbeddedMapAndWindChartEntryPoint.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/EmbeddedMapAndWindChartEntryPoint.java index 25af06fa8b1..9452d65a809 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/EmbeddedMapAndWindChartEntryPoint.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/EmbeddedMapAndWindChartEntryPoint.java @@ -29,8 +29,8 @@ import com.sap.sailing.gwt.settings.client.raceboard.RaceBoardPerspectiveOwnSett import com.sap.sailing.gwt.ui.client.AbstractSailingEntryPoint; 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.CompetitorSelectionProvider; +import com.sap.sailing.gwt.ui.client.RaceCompetitorSelectionModel; +import com.sap.sailing.gwt.ui.client.RaceCompetitorSelectionProvider; import com.sap.sailing.gwt.ui.client.RaceTimesInfoProvider; import com.sap.sailing.gwt.ui.client.RaceTimesInfoProviderListener; import com.sap.sailing.gwt.ui.client.TimePanel; @@ -162,7 +162,7 @@ public class EmbeddedMapAndWindChartEntryPoint extends AbstractSailingEntryPoint // because of the root font-size. Adjustments are postponed because they might affect the hole page content. } - private void createEmbeddedMap(final RegattaAndRaceIdentifier selectedRaceIdentifier, Map competitorBoats, + private void createEmbeddedMap(final RegattaAndRaceIdentifier selectedRaceIdentifier, Map competitorsAndBoats, final RaceBoardPerspectiveOwnSettings raceboardPerspectiveSettings, final RaceMapSettings raceMapSettings, final boolean showCompetitors, final boolean play) { final StringBuilder title = new StringBuilder(regattaLikeName); @@ -199,10 +199,10 @@ public class EmbeddedMapAndWindChartEntryPoint extends AbstractSailingEntryPoint final Button backToLivePlayButton = timePanel.getBackToLiveButton(); timePanel.updateSettings(new TimePanelSettings(refreshInterval)); raceMapResources.raceMapStyle().ensureInjected(); - final CompetitorColorProvider colorProvider = new CompetitorColorProviderImpl(selectedRaceIdentifier, competitorBoats); - final CompetitorSelectionProvider competitorSelection; + final CompetitorColorProvider colorProvider = new CompetitorColorProviderImpl(selectedRaceIdentifier, competitorsAndBoats); + final RaceCompetitorSelectionProvider competitorSelection; if (showCompetitors) { - competitorSelection = new CompetitorSelectionModel(/* hasMultiSelection */ true, colorProvider); + competitorSelection = new RaceCompetitorSelectionModel(/* hasMultiSelection */ true, colorProvider); } else { competitorSelection = createEmptyFilterCompetitorModel(colorProvider); // show no competitors } @@ -232,8 +232,8 @@ public class EmbeddedMapAndWindChartEntryPoint extends AbstractSailingEntryPoint timer.setTime(timer.getTime().getTime()-1000l); } - private CompetitorSelectionProvider createEmptyFilterCompetitorModel(CompetitorColorProvider colorProvider) { - final CompetitorSelectionModel result = new CompetitorSelectionModel(/* hasMultiSelection */ true, colorProvider); + private RaceCompetitorSelectionProvider createEmptyFilterCompetitorModel(CompetitorColorProvider colorProvider) { + final RaceCompetitorSelectionModel result = new RaceCompetitorSelectionModel(/* hasMultiSelection */ true, colorProvider); final FilterSet> filterSet = result.getOrCreateCompetitorsFilterSet("Empty"); filterSet.addFilter(new Filter() { @Override public boolean matches(CompetitorDTO object) { return false; } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardEntryPoint.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardEntryPoint.java index 75f9dc8c9aa..9b2de380af0 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardEntryPoint.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardEntryPoint.java @@ -24,7 +24,7 @@ import com.sap.sailing.gwt.ui.client.MediaServiceAsync; import com.sap.sailing.gwt.ui.client.RaceTimesInfoProvider; import com.sap.sailing.gwt.ui.client.RemoteServiceMappingConstants; import com.sap.sailing.gwt.ui.client.StringMessages; -import com.sap.sailing.gwt.ui.shared.RaceWithCompetitorsDTO; +import com.sap.sailing.gwt.ui.shared.RaceWithCompetitorsAndBoatsDTO; import com.sap.sailing.gwt.ui.shared.RaceboardDataDTO; import com.sap.sse.gwt.client.EntryPointHelper; import com.sap.sse.gwt.client.async.AsyncActionsExecutor; @@ -40,7 +40,7 @@ import com.sap.sse.security.ui.settings.ComponentContextWithSettingsStorage; import com.sap.sse.security.ui.settings.StoredSettingsLocation; public class RaceBoardEntryPoint extends AbstractSailingEntryPoint { - private RaceWithCompetitorsDTO selectedRace; + private RaceWithCompetitorsAndBoatsDTO selectedRace; /** * Controls the predefined mode into which to switch or configure the race viewer. @@ -102,9 +102,9 @@ public class RaceBoardEntryPoint extends AbstractSailingEntryPoint { final StoredSettingsLocation storageDefinition = StoredSettingsLocationFactory .createStoredSettingsLocatorForRaceBoard(raceboardContextDefinition, finalMode != null ? finalMode.name() : null); + // TODO bug2822: also determine regatta's boat change parameter and parameterize Leaderboard settings --> show boat info column yes/no? sailingService.determineDetailTypesForCompetitorChart(raceboardContextDefinition.getLeaderboardGroupName(), raceboardData.getRace().getRaceIdentifier(), new AsyncCallback>() { - @Override public void onFailure(Throwable caught) { reportError("Error trying to create the raceboard: " + caught.getMessage()); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardPanel.java index d92d6f877ce..f0ad825a4b3 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardPanel.java @@ -44,12 +44,13 @@ import com.sap.sailing.gwt.settings.client.leaderboard.SingleRaceLeaderboardSett import com.sap.sailing.gwt.settings.client.raceboard.RaceBoardPerspectiveOwnSettings; 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.CompetitorSelectionProvider; import com.sap.sailing.gwt.ui.client.EntryPointLinkFactory; import com.sap.sailing.gwt.ui.client.FlagImageResolverImpl; import com.sap.sailing.gwt.ui.client.LeaderboardUpdateListener; import com.sap.sailing.gwt.ui.client.MediaServiceAsync; +import com.sap.sailing.gwt.ui.client.RaceCompetitorSelectionModel; +import com.sap.sailing.gwt.ui.client.RaceCompetitorSelectionProvider; import com.sap.sailing.gwt.ui.client.RaceTimePanel; import com.sap.sailing.gwt.ui.client.RaceTimePanelLifecycle; import com.sap.sailing.gwt.ui.client.RaceTimePanelSettings; @@ -124,7 +125,7 @@ public class RaceBoardPanel private final RaceTimePanel racetimePanel; private final Timer timer; private final UserAgentDetails userAgent; - private final CompetitorSelectionProvider competitorSelectionProvider; + private final RaceCompetitorSelectionProvider competitorSelectionProvider; private final TimeRangeWithZoomModel timeRangeWithZoomModel; private final RegattaAndRaceIdentifier selectedRaceIdentifier; @@ -210,7 +211,7 @@ public class RaceBoardPanel timeRangeWithZoomModel = new TimeRangeWithZoomModel(); final CompetitorColorProvider colorProvider = new CompetitorColorProviderImpl(selectedRaceIdentifier, competitorsAndTheirBoats); - competitorSelectionProvider = new CompetitorSelectionModel(/* hasMultiSelection */ true, colorProvider); + competitorSelectionProvider = new RaceCompetitorSelectionModel(/* hasMultiSelection */ true, colorProvider); raceMapResources.raceMapStyle().ensureInjected(); RaceMapLifecycle raceMapLifecycle = lifecycle.getRaceMapLifecycle(); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/SingleRaceLeaderboardPanelLifecycle.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/SingleRaceLeaderboardPanelLifecycle.java index fa8056a8f51..0b43ae11446 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/SingleRaceLeaderboardPanelLifecycle.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/SingleRaceLeaderboardPanelLifecycle.java @@ -32,14 +32,17 @@ public class SingleRaceLeaderboardPanelLifecycle extends LeaderboardPanelLifecyc raceDetails.add(DetailType.DISPLAY_LEGS); List overallDetails = new ArrayList<>(); SingleRaceLeaderboardSettings defaultSettings = new SingleRaceLeaderboardSettings(); + SingleRaceLeaderboardSettings settings = new SingleRaceLeaderboardSettings( defaultSettings.getManeuverDetailsToShow(), defaultSettings.getLegDetailsToShow(), defaultSettings.getRaceDetailsToShow(), overallDetails, DEFAULT_REFRESH_INTERVAL, - defaultSettings.isShowAddedScores(), /* showCompetitorSailIdColumn */ true, + defaultSettings.isShowAddedScores(), + /* showCompetitorShortNameColumn */ true, /* * don't showCompetitorFullNameColumn in case screen is so small that we don't even display the * leaderboard initially - */ isScreenLargeEnoughToInitiallyDisplayLeaderboard, false, false); + */ isScreenLargeEnoughToInitiallyDisplayLeaderboard, + /* showCompetitorBoatInfoColumn */ true, false, false); SettingsUtil.copyDefaultsFromValues(settings, settings); return settings; @@ -57,8 +60,9 @@ public class SingleRaceLeaderboardPanelLifecycle extends LeaderboardPanelLifecyc currentLeaderboardSettings.getRaceDetailsToShow(), currentLeaderboardSettings.getOverallDetailsToShow(), currentLeaderboardSettings.getDelayBetweenAutoAdvancesInMilliseconds(), currentLeaderboardSettings.isShowAddedScores(), - currentLeaderboardSettings.isShowCompetitorSailIdColumn(), + currentLeaderboardSettings.isShowCompetitorShortNameColumn(), currentLeaderboardSettings.isShowCompetitorFullNameColumn(), + currentLeaderboardSettings.isShowCompetitorBoatInfoColumn(), currentLeaderboardSettings.isShowRaceRankColumn(), currentLeaderboardSettings.isShowCompetitorNationality()); return SettingsUtil.copyValues(contextSpecificLeaderboardSettings, defaultLeaderboardSettings); @@ -72,8 +76,9 @@ public class SingleRaceLeaderboardPanelLifecycle extends LeaderboardPanelLifecyc currentLeaderboardSettings.getRaceDetailsToShow(), currentLeaderboardSettings.getOverallDetailsToShow(), currentLeaderboardSettings.getDelayBetweenAutoAdvancesInMilliseconds(), currentLeaderboardSettings.isShowAddedScores(), - currentLeaderboardSettings.isShowCompetitorSailIdColumn(), + currentLeaderboardSettings.isShowCompetitorShortNameColumn(), currentLeaderboardSettings.isShowCompetitorFullNameColumn(), + currentLeaderboardSettings.isShowCompetitorBoatInfoColumn(), currentLeaderboardSettings.isShowCompetitorNationality(),currentLeaderboardSettings.isShowRaceRankColumn()); return SettingsUtil.copyValues(globalLeaderboardSettings, defaultLeaderboardSettings); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java index 39b51a32e7c..877c3df2677 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java @@ -1,5 +1,7 @@ package com.sap.sailing.gwt.ui.server; +import java.io.File; +import java.io.FileOutputStream; import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; @@ -129,6 +131,7 @@ import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogCloseOpenEnde import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDefineMarkEvent; import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogRegisterCompetitorEvent; import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogDefineMarkEventImpl; +import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogDeviceBoatMappingEventImpl; import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogDeviceCompetitorExpeditionExtendedMappingEventImpl; import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogDeviceCompetitorMappingEventImpl; import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogDeviceMarkMappingEventImpl; @@ -139,6 +142,8 @@ import com.sap.sailing.domain.abstractlog.regatta.tracking.analyzing.impl.Regatt 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.CompetitorStore; +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.Course; @@ -178,7 +183,7 @@ import com.sap.sailing.domain.base.configuration.impl.RegattaConfigurationImpl; import com.sap.sailing.domain.base.configuration.impl.SWCStartConfigurationImpl; import com.sap.sailing.domain.base.configuration.procedures.ConfigurableStartModeFlagRacingProcedureConfiguration; import com.sap.sailing.domain.base.impl.BoatImpl; -import com.sap.sailing.domain.base.impl.CompetitorImpl; +import com.sap.sailing.domain.base.impl.CompetitorWithBoatImpl; import com.sap.sailing.domain.base.impl.CourseDataImpl; import com.sap.sailing.domain.base.impl.CourseImpl; import com.sap.sailing.domain.base.impl.DynamicBoat; @@ -232,7 +237,7 @@ import com.sap.sailing.domain.common.abstractlog.TimePointSpecificationFoundInLo import com.sap.sailing.domain.common.dto.BoatClassDTO; import com.sap.sailing.domain.common.dto.BoatDTO; import com.sap.sailing.domain.common.dto.CompetitorDTO; -import com.sap.sailing.domain.common.dto.CompetitorDTOImpl; +import com.sap.sailing.domain.common.dto.CompetitorWithoutBoatDTO; import com.sap.sailing.domain.common.dto.FleetDTO; import com.sap.sailing.domain.common.dto.FullLeaderboardDTO; import com.sap.sailing.domain.common.dto.IncrementalLeaderboardDTO; @@ -404,7 +409,7 @@ import com.sap.sailing.gwt.ui.shared.RaceLogSetFinishingAndFinishTimeDTO; import com.sap.sailing.gwt.ui.shared.RaceLogSetStartTimeAndProcedureDTO; import com.sap.sailing.gwt.ui.shared.RaceMapDataDTO; import com.sap.sailing.gwt.ui.shared.RaceTimesInfoDTO; -import com.sap.sailing.gwt.ui.shared.RaceWithCompetitorsDTO; +import com.sap.sailing.gwt.ui.shared.RaceWithCompetitorsAndBoatsDTO; import com.sap.sailing.gwt.ui.shared.RaceboardDataDTO; import com.sap.sailing.gwt.ui.shared.RegattaDTO; import com.sap.sailing.gwt.ui.shared.RegattaLogDTO; @@ -450,6 +455,7 @@ import com.sap.sailing.server.operationaltransformation.AddColumnToSeries; import com.sap.sailing.server.operationaltransformation.AddCourseAreas; import com.sap.sailing.server.operationaltransformation.AddRemoteSailingServerReference; import com.sap.sailing.server.operationaltransformation.AddSpecificRegatta; +import com.sap.sailing.server.operationaltransformation.AllowBoatResetToDefaults; import com.sap.sailing.server.operationaltransformation.AllowCompetitorResetToDefaults; import com.sap.sailing.server.operationaltransformation.ConnectTrackedRaceToLeaderboardColumn; import com.sap.sailing.server.operationaltransformation.CreateEvent; @@ -480,6 +486,7 @@ import com.sap.sailing.server.operationaltransformation.SetRaceIsKnownToStartUpw import com.sap.sailing.server.operationaltransformation.SetSuppressedFlagForCompetitorInLeaderboard; import com.sap.sailing.server.operationaltransformation.SetWindSourcesToExclude; import com.sap.sailing.server.operationaltransformation.StopTrackingRace; +import com.sap.sailing.server.operationaltransformation.UpdateBoat; import com.sap.sailing.server.operationaltransformation.UpdateCompetitor; import com.sap.sailing.server.operationaltransformation.UpdateCompetitorDisplayNameInLeaderboard; import com.sap.sailing.server.operationaltransformation.UpdateEliminatedCompetitorsInLeaderboard; @@ -852,12 +859,12 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S } // Un-comment the following lines if you need to update the file used by LeaderboardDTODiffingTest, set a breakpoint // and toggle the storeLeaderboardForTesting flag if you found a good version. See also bug 1417. -// boolean storeLeaderboardForTesting = false; -// if (storeLeaderboardForTesting) { -// ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File("c:/data/SAP/sailing/workspace/java/com.sap.sailing.domain.test/resources/IncrementalLeaderboardDTO.ser"))); -// oos.writeObject(leaderboardDTO); -// oos.close(); -// } + boolean storeLeaderboardForTesting = false; + if (storeLeaderboardForTesting) { + ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File("C:/Projekte/sailing/dev/git/java/com.sap.sailing.domain.test/resources/IncrementalLeaderboardDTO.ser"))); + oos.writeObject(leaderboardDTO); + oos.close(); + } final IncrementalLeaderboardDTO cachedDiff; if (previousLeaderboardId != null) { synchronized (leaderboardDifferenceCacheByIdPair) { @@ -951,7 +958,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S regattaDTO.endDate = regatta.getEndDate() != null ? regatta.getEndDate().asDate() : null; BoatClass boatClass = regatta.getBoatClass(); if (boatClass != null) { - regattaDTO.boatClass = new BoatClassDTO(boatClass.getName(), boatClass.getDisplayName(), boatClass.getHullLength(), boatClass.getHullBeam()); + regattaDTO.boatClass = convertToBoatClassDTO(boatClass); } if (regatta.getDefaultCourseArea() != null) { regattaDTO.defaultCourseAreaUuid = regatta.getDefaultCourseArea().getId(); @@ -960,11 +967,16 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S regattaDTO.buoyZoneRadiusInHullLengths = regatta.getBuoyZoneRadiusInHullLengths(); regattaDTO.useStartTimeInference = regatta.useStartTimeInference(); regattaDTO.controlTrackingFromStartAndFinishTimes = regatta.isControlTrackingFromStartAndFinishTimes(); + regattaDTO.canBoatsOfCompetitorsChangePerRace = regatta.canBoatsOfCompetitorsChangePerRace(); regattaDTO.configuration = convertToRegattaConfigurationDTO(regatta.getRegattaConfiguration()); regattaDTO.rankingMetricType = regatta.getRankingMetricType(); return regattaDTO; } + private BoatClassDTO convertToBoatClassDTO(BoatClass boatClass) { + return new BoatClassDTO(boatClass.getName(), boatClass.getDisplayName(), boatClass.getHullLength(), boatClass.getHullBeam()); + } + private List convertToSeriesDTOs(Regatta regatta) { List result = new ArrayList(); for (Series series : regatta.getSeries()) { @@ -1120,8 +1132,8 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S raceInfoDTO.startProcedureDTO = info; } - private List convertToRaceDTOs(Regatta regatta) { - List result = new ArrayList(); + private List convertToRaceDTOs(Regatta regatta) { + List result = new ArrayList(); for (RaceDefinition r : regatta.getAllRaces()) { RegattaAndRaceIdentifier raceIdentifier = new RegattaNameAndRaceName(regatta.getName(), r.getName()); TrackedRace trackedRace = getService().getExistingTrackedRace(raceIdentifier); @@ -1129,7 +1141,8 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S if (trackedRace != null) { trackedRaceDTO = getBaseDomainFactory().createTrackedRaceDTO(trackedRace); } - RaceWithCompetitorsDTO raceDTO = new RaceWithCompetitorsDTO(raceIdentifier, convertToCompetitorDTOs(r.getCompetitors()), + Map competitorAndBoatDTOs = baseDomainFactory.convertToCompetitorAndBoatDTOs(r.getCompetitorsAndTheirBoats()); + RaceWithCompetitorsAndBoatsDTO raceDTO = new RaceWithCompetitorsAndBoatsDTO(raceIdentifier, competitorAndBoatDTOs, trackedRaceDTO, getService().isRaceBeingTracked(regatta, r)); if (trackedRace != null) { getBaseDomainFactory().updateRaceDTOWithTrackedRaceData(trackedRace, raceDTO); @@ -1141,18 +1154,31 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S } /** - * Converts the {@link Competitor} objects passed as {@code iterable} to {@link CompetitorDTO} objects. + * Converts the {@link Competitor} objects passed as {@code iterable} to {@link CompetitorDTO} objects with an empty boat. * The iteration order in the result matches that of the {@code iterable} passed. */ private List convertToCompetitorDTOs(Iterable iterable) { List result = new ArrayList(); for (Competitor c : iterable) { - CompetitorDTO competitorDTO = baseDomainFactory.convertToCompetitorDTO(c); + CompetitorDTO competitorDTO = baseDomainFactory.convertToCompetitorWithOptionalBoatDTO(c); result.add(competitorDTO); } return result; } + /** + * Converts the {@link Boat} objects passed as {@code iterable} to {@link BoatDTO} objects. + * The iteration order in the result matches that of the {@code iterable} passed. + */ + private List convertToBoatDTOs(Iterable iterable) { + List result = new ArrayList(); + for (Boat b : iterable) { + BoatDTO boatDTO = baseDomainFactory.convertToBoatDTO(b); + result.add(boatDTO); + } + return result; + } + @Override public com.sap.sse.common.Util.Pair> listTracTracRacesInEvent(String eventJsonURL, boolean listHiddenRaces) throws MalformedURLException, IOException, ParseException, org.json.simple.parser.ParseException, URISyntaxException { com.sap.sse.common.Util.Pair> raceRecords; @@ -1708,8 +1734,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S Map result = null; TrackedRace trackedRace = getService().getExistingTrackedRace(raceIdentifier); if(trackedRace != null) { - List competitors = convertToCompetitorDTOs(trackedRace.getRace().getCompetitors()); - result = getCompetitorBoatsForRace(trackedRace.getRace(), competitors); + result = baseDomainFactory.convertToCompetitorAndBoatDTOs(trackedRace.getRace().getCompetitorsAndTheirBoats()); } return result; } @@ -1717,21 +1742,19 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S @Override public RaceboardDataDTO getRaceboardData(String regattaName, String raceName, String leaderboardName, String leaderboardGroupName, UUID eventId) { - RaceWithCompetitorsDTO raceDTO = null; - Map competitorBoats = null; + RaceWithCompetitorsAndBoatsDTO raceDTO = null; Regatta regatta = getService().getRegattaByName(regattaName); if (regatta != null) { RaceDefinition race = regatta.getRaceByName(raceName); if (race != null) { RegattaAndRaceIdentifier raceIdentifier = new RegattaNameAndRaceName(regatta.getName(), race.getName()); TrackedRace trackedRace = getService().getExistingTrackedRace(raceIdentifier); - List competitors = convertToCompetitorDTOs(race.getCompetitors()); - competitorBoats = getCompetitorBoatsForRace(race, competitors); + Map competitorsAndBoats = baseDomainFactory.convertToCompetitorAndBoatDTOs(race.getCompetitorsAndTheirBoats()); TrackedRaceDTO trackedRaceDTO = null; if (trackedRace != null) { trackedRaceDTO = getBaseDomainFactory().createTrackedRaceDTO(trackedRace); } - raceDTO = new RaceWithCompetitorsDTO(raceIdentifier, competitors, trackedRaceDTO, getService().isRaceBeingTracked(regatta, race)); + raceDTO = new RaceWithCompetitorsAndBoatsDTO(raceIdentifier, competitorsAndBoats, trackedRaceDTO, getService().isRaceBeingTracked(regatta, race)); if (trackedRace != null) { getBaseDomainFactory().updateRaceDTOWithTrackedRaceData(trackedRace, raceDTO); } @@ -1762,7 +1785,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S } } } - RaceboardDataDTO result = new RaceboardDataDTO(raceDTO, competitorBoats, isValidLeaderboard, isValidLeaderboardGroup, isValidEvent); + RaceboardDataDTO result = new RaceboardDataDTO(raceDTO, isValidLeaderboard, isValidLeaderboardGroup, isValidEvent); return result; } @@ -1816,29 +1839,6 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S return estimatedDuration; } - private Map getCompetitorBoatsForRace(RaceDefinition race, List competitorDTOs) { - try { - Map competitorBoats = new HashMap(); - HashMap competitorDTOsMap = new HashMap<>(); - for (CompetitorDTO competitorDTO : competitorDTOs) { - competitorDTOsMap.put(competitorDTO.getIdAsString(), competitorDTO); - } - for (Competitor competitor : race.getCompetitors()) { - Boat boatOfCompetitor = race.getBoatOfCompetitor(competitor); - if (boatOfCompetitor != null) { - BoatDTO boatDTO = new BoatDTO(boatOfCompetitor.getName(), boatOfCompetitor.getSailID(), - boatOfCompetitor.getColor()); - competitorBoats.put(competitorDTOsMap.get(competitor.getId().toString()), boatDTO); - } - } - return competitorBoats; - } catch (Exception e) { - e.printStackTrace(); - } - return null; - } - - @Override public CompactBoatPositionsDTO getBoatPositions(RegattaAndRaceIdentifier raceIdentifier, Map fromPerCompetitorIdAsString, Map toPerCompetitorIdAsString, @@ -1873,9 +1873,10 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S Map> result = new HashMap>(); TrackedRace trackedRace = getExistingTrackedRace(raceIdentifier); if (trackedRace != null) { - for (Competitor competitor : trackedRace.getRace().getCompetitors()) { + for (Entry competitorAndBoat : trackedRace.getRace().getCompetitorsAndTheirBoats().entrySet()) { + Competitor competitor = competitorAndBoat.getKey(); if (fromPerCompetitorIdAsString.containsKey(competitor.getId().toString())) { - CompetitorDTO competitorDTO = baseDomainFactory.convertToCompetitorDTO(competitor); + CompetitorDTO competitorDTO = baseDomainFactory.convertToCompetitorDTO(competitor, competitorAndBoat.getValue()); List fixesForCompetitor = new ArrayList(); result.put(competitorDTO, fixesForCompetitor); GPSFixTrack track = trackedRace.getTrack(competitor); @@ -2319,7 +2320,8 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S TrackedLegOfCompetitor trackedLeg = trackedRace.getTrackedLeg(competitor, actualTimePoint); if (trackedLeg != null) { int legNumberOneBased = race.getCourse().getLegs().indexOf(trackedLeg.getLeg()) + 1; - QuickRankDTO quickRankDTO = new QuickRankDTO(baseDomainFactory.convertToCompetitorDTO(competitor), + Boat boatOfCompetitor = trackedRace.getBoatOfCompetitor(competitor); + QuickRankDTO quickRankDTO = new QuickRankDTO(baseDomainFactory.convertToCompetitorDTO(competitor, boatOfCompetitor), oneBasedRank, legNumberOneBased); result.add(quickRankDTO); } @@ -2520,8 +2522,10 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S Regatta regatta = regattaLeaderboard.getRegatta(); leaderboardDTO.regattaName = regatta.getName(); leaderboardDTO.scoringScheme = regatta.getScoringScheme().getType(); + leaderboardDTO.canBoatsOfCompetitorsChangePerRace = regatta.canBoatsOfCompetitorsChangePerRace(); } else { leaderboardDTO.scoringScheme = leaderboard.getScoringScheme().getType(); + leaderboardDTO.canBoatsOfCompetitorsChangePerRace = false; } if (leaderboard.getDefaultCourseArea() != null) { leaderboardDTO.defaultCourseAreaId = leaderboard.getDefaultCourseArea().getId(); @@ -2559,7 +2563,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S final RaceLogTrackingState raceLogTrackingState = raceLog == null ? RaceLogTrackingState.NOT_A_RACELOG_TRACKED_RACE : new RaceLogTrackingStateAnalyzer(raceLog).analyze(); final boolean raceLogTrackerExists = raceLog == null ? false : getService().getRaceTrackerById(raceLog.getId()) != null; - final boolean competitorRegistrationsExist = raceLog == null ? false : !Util.isEmpty(raceColumn.getAllCompetitors(fleet)); + final boolean competitorRegistrationsExist = raceLog == null ? false : !Util.isEmpty(raceColumn.getAllCompetitorsAndTheirBoats(fleet).keySet()); final boolean courseExist = raceLog == null ? false : !Util.isEmpty(raceColumn.getCourseMarks(fleet)); final RaceLogTrackingInfoDTO raceLogTrackingInfo = new RaceLogTrackingInfoDTO(raceLogTrackerExists, competitorRegistrationsExist, courseExist, raceLogTrackingState); @@ -3256,7 +3260,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S List competitorDTOs = new ArrayList<>(); List pointSums = new ArrayList<>(); for (Entry e2 : e.getValue().entrySet()) { - competitorDTOs.add(baseDomainFactory.convertToCompetitorDTO(e2.getKey())); + competitorDTOs.add(baseDomainFactory.convertToCompetitorWithOptionalBoatDTO(e2.getKey())); pointSums.add(e2.getValue()); } result.add(new Triple, List>(e.getKey().getName(), competitorDTOs, pointSums)); @@ -3272,7 +3276,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S List competitorDTOs = new ArrayList(); for (Competitor competitor : e.getValue()) { values.add(new Double(rank)); - competitorDTOs.add(baseDomainFactory.convertToCompetitorDTO(competitor)); + competitorDTOs.add(baseDomainFactory.convertToCompetitorWithOptionalBoatDTO(competitor)); rank++; } result.add(new Triple, List>(e.getKey().getName(), competitorDTOs, values)); @@ -3325,8 +3329,9 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S TrackedRace trackedRace = getExistingTrackedRace(raceIdentifier); if (trackedRace != null) { MeterDistance maxDistance = new MeterDistance(meters); - for (Competitor competitor : trackedRace.getRace().getCompetitors()) { - CompetitorDTO competitorDTO = baseDomainFactory.convertToCompetitorDTO(competitor); + for (Entry competitorAndBoat : trackedRace.getRace().getCompetitorsAndTheirBoats().entrySet()) { + Competitor competitor = competitorAndBoat.getKey(); + CompetitorDTO competitorDTO = baseDomainFactory.convertToCompetitorDTO(competitor, competitorAndBoat.getValue()); if (from.containsKey(competitorDTO)) { // get Track of competitor GPSFixTrack gpsFixTrack = trackedRace.getTrack(competitor); @@ -3382,8 +3387,9 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S final TrackedRace trackedRace = getExistingTrackedRace(raceIdentifier); if (trackedRace != null) { Map>> futures = new HashMap>>(); - for (final Competitor competitor : trackedRace.getRace().getCompetitors()) { - CompetitorDTO competitorDTO = baseDomainFactory.convertToCompetitorDTO(competitor); + for (Entry competitorAndBoat : trackedRace.getRace().getCompetitorsAndTheirBoats().entrySet()) { + Competitor competitor = competitorAndBoat.getKey(); + CompetitorDTO competitorDTO = baseDomainFactory.convertToCompetitorDTO(competitor, competitorAndBoat.getValue()); if (from.containsKey(competitorDTO)) { final TimePoint timePointFrom = new MillisecondsTimePoint(from.get(competitorDTO)); final TimePoint timePointTo = new MillisecondsTimePoint(to.get(competitorDTO)); @@ -4065,11 +4071,9 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S FleetDTO fleetDTO = new FleetDTO(LeaderboardNameConstants.DEFAULT_FLEET_NAME, 0, null); seriesDTO.getFleets().add(fleetDTO); seriesDTO.getRaceColumns().addAll(convertToRaceColumnDTOs(leaderboard.getRaceColumns())); - for(Competitor c: leaderboard.getCompetitors()) { - if(c.getBoat() != null && c.getBoat().getBoatClass() != null) { - raceGroup.boatClass = c.getBoat().getBoatClass().getDisplayName(); - } - } + + BoatClass boatClass = leaderboard.getBoatClass(); + raceGroup.boatClass = boatClass != null ? boatClass.getDisplayName(): null; } raceGroups.add(raceGroup); } @@ -4204,7 +4208,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S } @Override - public RegattaDTO createRegatta(String regattaName, String boatClassName, Date startDate, Date endDate, + public RegattaDTO createRegatta(String regattaName, String boatClassName, boolean canBoatsOfCompetitorsChangePerRace, Date startDate, Date endDate, RegattaCreationParametersDTO seriesNamesWithFleetNamesAndFleetOrderingAndMedal, boolean persistent, ScoringSchemeType scoringSchemeType, UUID defaultCourseAreaId, Double buoyZoneRadiusInHullLengths, boolean useStartTimeInference, boolean controlTrackingFromStartAndFinishTimes, RankingMetrics rankingMetricType) { @@ -4213,7 +4217,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S TimePoint endTimePoint = endDate != null ? new MillisecondsTimePoint(endDate) : null; Regatta regatta = getService().apply( new AddSpecificRegatta( - regattaName, boatClassName, startTimePoint, endTimePoint, UUID.randomUUID(), + regattaName, boatClassName, canBoatsOfCompetitorsChangePerRace, startTimePoint, endTimePoint, UUID.randomUUID(), seriesNamesWithFleetNamesAndFleetOrderingAndMedal, persistent, baseDomainFactory.createScoringScheme(scoringSchemeType), defaultCourseAreaId, buoyZoneRadiusInHullLengths, useStartTimeInference, controlTrackingFromStartAndFinishTimes, rankingMetricType)); @@ -4464,7 +4468,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S private void createRegattaFromRegattaDTO(RegattaDTO regatta) { SecurityUtils.getSubject().checkPermission(Permission.REGATTA.getStringPermissionForObjects(Mode.CREATE, regatta.getName())); - this.createRegatta(regatta.getName(), regatta.boatClass.getName(), regatta.startDate, regatta.endDate, + this.createRegatta(regatta.getName(), regatta.boatClass.getName(), regatta.canBoatsOfCompetitorsChangePerRace, regatta.startDate, regatta.endDate, new RegattaCreationParametersDTO(getSeriesCreationParameters(regatta)), true, regatta.scoringScheme, regatta.defaultCourseAreaUuid, regatta.buoyZoneRadiusInHullLengths, regatta.useStartTimeInference, regatta.controlTrackingFromStartAndFinishTimes, regatta.rankingMetricType); @@ -4933,85 +4937,152 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S } @Override - public Iterable getCompetitors() { - return convertToCompetitorDTOs(getService().getBaseDomainFactory().getCompetitorStore().getCompetitors()); + public Iterable getCompetitors(boolean filterCompetitorsWithBoat, boolean filterCompetitorsWithoutBoat) { + Iterable result; + CompetitorStore competitorStore = getService().getBaseDomainFactory().getCompetitorStore(); + if (filterCompetitorsWithBoat == false && filterCompetitorsWithoutBoat == false) { + result = convertToCompetitorDTOs(competitorStore.getAllCompetitors()); + } else if (filterCompetitorsWithBoat == true && filterCompetitorsWithoutBoat == false) { + result = convertToCompetitorDTOs(competitorStore.getCompetitorsWithoutBoat()); + } else if (filterCompetitorsWithBoat == false && filterCompetitorsWithoutBoat == true) { + result = convertToCompetitorDTOs(competitorStore.getCompetitorsWithBoat()); + } else { + result = Collections.emptyList(); + } + return result; } @Override public Iterable getCompetitorsOfLeaderboard(String leaderboardName) { - Leaderboard leaderboard = getService().getLeaderboardByName(leaderboardName); - return convertToCompetitorDTOs(leaderboard.getAllCompetitors()); + Leaderboard leaderboard = getService().getLeaderboardByName(leaderboardName); + return convertToCompetitorDTOs(leaderboard.getAllCompetitors()); } @Override - public List addOrUpdateCompetitor(List competitors) throws URISyntaxException { - final List results = new ArrayList<>(); + public Map getCompetitorsAndBoatsOfRace(String leaderboardName, String raceColumnName, String fleetName) { + Map competitorsAndBoats = getService().getCompetitorToBoatMappingsForRace(leaderboardName, raceColumnName, fleetName); + return baseDomainFactory.convertToCompetitorAndBoatDTOs(competitorsAndBoats); + } + + @Override + public List addOrUpdateCompetitors(List competitors) throws URISyntaxException { + final List result = new ArrayList<>(); for (final CompetitorDTO competitor : competitors) { - Competitor existingCompetitor = getService().getCompetitorStore().getExistingCompetitorByIdAsString(competitor.getIdAsString()); - Nationality nationality = (competitor.getThreeLetterIocCountryCode() == null - || competitor.getThreeLetterIocCountryCode().isEmpty()) ? null - : getBaseDomainFactory().getOrCreateNationality(competitor.getThreeLetterIocCountryCode()); - final CompetitorDTO result; - // new competitor - if (competitor.getIdAsString() == null || competitor.getIdAsString().isEmpty() || existingCompetitor == null) { - BoatClass boatClass = getBaseDomainFactory().getOrCreateBoatClass(competitor.getBoatClass().getName()); - DynamicPerson sailor = new PersonImpl(competitor.getName(), nationality, null, null); - DynamicTeam team = new TeamImpl(competitor.getName() + " team", Collections.singleton(sailor), null); - DynamicBoat boat = new BoatImpl(competitor.getName() + " boat", boatClass, competitor.getSailID()); - result = getBaseDomainFactory().convertToCompetitorDTO( - getBaseDomainFactory().getOrCreateCompetitor(UUID.randomUUID(), competitor.getName(), - competitor.getColor(), competitor.getEmail(), - competitor.getFlagImageURL() == null ? null : new URI(competitor.getFlagImageURL()), team, boat, - competitor.getTimeOnTimeFactor(), - competitor.getTimeOnDistanceAllowancePerNauticalMile(), competitor.getSearchTag())); - } else { - result = getBaseDomainFactory().convertToCompetitorDTO( - getService().apply( - new UpdateCompetitor(competitor.getIdAsString(), competitor.getName(), competitor - .getColor(), competitor.getEmail(), competitor.getSailID(), nationality, - competitor.getImageURL() == null ? null : new URI(competitor.getImageURL()), - competitor.getFlagImageURL() == null ? null : new URI(competitor.getFlagImageURL()), - competitor.getTimeOnTimeFactor(), - competitor.getTimeOnDistanceAllowancePerNauticalMile(), - competitor.getSearchTag()))); - } - results.add(result); + result.add(addOrUpdateCompetitorWithBoat(competitor)); } - return results; + return result; + } + + private CompetitorWithBoat addOrUpdateCompetitorWithBoatInternal(CompetitorDTO competitor) throws URISyntaxException { + CompetitorWithBoat result; + CompetitorWithBoat existingCompetitor = getService().getCompetitorStore().getExistingCompetitorWithBoatByIdAsString(competitor.getIdAsString()); + Nationality nationality = (competitor.getThreeLetterIocCountryCode() == null || competitor.getThreeLetterIocCountryCode().isEmpty()) ? null : + getBaseDomainFactory().getOrCreateNationality(competitor.getThreeLetterIocCountryCode()); + if (competitor.getIdAsString() == null || competitor.getIdAsString().isEmpty() || existingCompetitor == null) { + // new competitor + UUID competitorUUID = UUID.randomUUID(); + DynamicPerson sailor = new PersonImpl(competitor.getName(), nationality, null, null); + DynamicTeam team = new TeamImpl(competitor.getName() + " team", Collections.singleton(sailor), null); + // new boat + DynamicBoat boat = (DynamicBoat) addOrUpdateBoatInternal(competitor.getBoat()); + + result = getBaseDomainFactory().getOrCreateCompetitorWithBoat(competitorUUID, competitor.getName(), competitor.getShortName(), + competitor.getColor(), competitor.getEmail(), + competitor.getFlagImageURL() == null ? null : new URI(competitor.getFlagImageURL()), team, + competitor.getTimeOnTimeFactor(), + competitor.getTimeOnDistanceAllowancePerNauticalMile(), competitor.getSearchTag(), boat); + + } else { + Competitor updatedCompetitor = getService().apply( + new UpdateCompetitor(competitor.getIdAsString(), competitor.getName(), competitor.getShortName(), + competitor.getColor(), competitor.getEmail(), nationality, + competitor.getImageURL() == null ? null : new URI(competitor.getImageURL()), + competitor.getFlagImageURL() == null ? null : new URI(competitor.getFlagImageURL()), + competitor.getTimeOnTimeFactor(), + competitor.getTimeOnDistanceAllowancePerNauticalMile(), + competitor.getSearchTag())); + DynamicBoat updatedBoat = (DynamicBoat) addOrUpdateBoatInternal(competitor.getBoat()); + result = new CompetitorWithBoatImpl(updatedCompetitor, updatedBoat); + } + return result; + } + + + private Competitor addOrUpdateCompetitorWithoutBoatInternal(CompetitorDTO competitor) throws URISyntaxException { + Competitor result; + Competitor existingCompetitor = getService().getCompetitorStore().getExistingCompetitorByIdAsString(competitor.getIdAsString()); + Nationality nationality = (competitor.getThreeLetterIocCountryCode() == null || competitor.getThreeLetterIocCountryCode().isEmpty()) ? null : + getBaseDomainFactory().getOrCreateNationality(competitor.getThreeLetterIocCountryCode()); + if (competitor.getIdAsString() == null || competitor.getIdAsString().isEmpty() || existingCompetitor == null) { + // new competitor + UUID competitorUUID = UUID.randomUUID(); + DynamicPerson sailor = new PersonImpl(competitor.getName(), nationality, null, null); + DynamicTeam team = new TeamImpl(competitor.getName() + " team", Collections.singleton(sailor), null); + result = getBaseDomainFactory().getOrCreateCompetitor(competitorUUID, competitor.getName(), competitor.getShortName(), + competitor.getColor(), competitor.getEmail(), + competitor.getFlagImageURL() == null ? null : new URI(competitor.getFlagImageURL()), team, + competitor.getTimeOnTimeFactor(), + competitor.getTimeOnDistanceAllowancePerNauticalMile(), competitor.getSearchTag()); + + } else { + result = getService().apply( + new UpdateCompetitor(competitor.getIdAsString(), competitor.getName(), competitor.getShortName(), + competitor.getColor(), competitor.getEmail(), nationality, + competitor.getImageURL() == null ? null : new URI(competitor.getImageURL()), + competitor.getFlagImageURL() == null ? null : new URI(competitor.getFlagImageURL()), + competitor.getTimeOnTimeFactor(), + competitor.getTimeOnDistanceAllowancePerNauticalMile(), + competitor.getSearchTag())); + } + return result; + } + + @Override + public CompetitorDTO addOrUpdateCompetitorWithoutBoat(CompetitorDTO competitorDTO) throws URISyntaxException { + Competitor competitor = addOrUpdateCompetitorWithoutBoatInternal(competitorDTO); + return getBaseDomainFactory().convertToCompetitorWithOptionalBoatDTO(competitor); + } + + @Override + public CompetitorDTO addOrUpdateCompetitorWithBoat(CompetitorDTO competitorDTO) throws URISyntaxException { + CompetitorWithBoat competitor = addOrUpdateCompetitorWithBoatInternal(competitorDTO); + return getBaseDomainFactory().convertToCompetitorWithOptionalBoatDTO(competitor); } @Override public List addCompetitors(List competitorDescriptors, String searchTag) throws URISyntaxException { - List competitorsForSaving = new ArrayList<>(); + List competitorsForSaving = new ArrayList<>(); for (final CompetitorDescriptor competitorDescriptor : competitorDescriptors) { - Competitor competitor = convertCompetitorDescriptorToCompetitor(competitorDescriptor, searchTag); - competitorsForSaving.add(competitor); + competitorsForSaving.add(convertCompetitorDescriptorToCompetitorWithBoat(competitorDescriptor, searchTag)); } - getBaseDomainFactory().getCompetitorStore().addNewCompetitors(competitorsForSaving); + getBaseDomainFactory().getCompetitorStore().addNewCompetitorsWithBoat(competitorsForSaving); return convertToCompetitorDTOs(competitorsForSaving); } /** - * Creates a new {@link Competitor} object from a {@link CompetitorDescriptor} with a new random {@link UUID}. + * Creates a new {@link CompetitorWithBoat} objects from a {@link CompetitorDescriptor}. * * @param searchTag * set as the {@link Competitor#getSearchTag() searchTag} property of all new competitors */ - private Competitor convertCompetitorDescriptorToCompetitor(CompetitorDescriptor competitorDescriptor, String searchTag) throws URISyntaxException { + private CompetitorWithBoat convertCompetitorDescriptorToCompetitorWithBoat(CompetitorDescriptor competitorDescriptor, String searchTag) throws URISyntaxException { Nationality nationality = (competitorDescriptor.getCountryCode() == null || competitorDescriptor.getCountryCode().getThreeLetterIOCCode() == null || competitorDescriptor.getCountryCode().getThreeLetterIOCCode().isEmpty()) ? null : getBaseDomainFactory().getOrCreateNationality(competitorDescriptor.getCountryCode().getThreeLetterIOCCode()); - BoatClass boatClass = getBaseDomainFactory().getOrCreateBoatClass(competitorDescriptor.getBoatClassName()); + UUID competitorUUID = competitorDescriptor.getCompetitorUUID() != null ? competitorDescriptor.getCompetitorUUID() : UUID.randomUUID(); + UUID boatUUID = competitorDescriptor.getBoatUUID() != null ? competitorDescriptor.getBoatUUID() : UUID.randomUUID(); DynamicPerson sailor = new PersonImpl(competitorDescriptor.getName(), nationality, null, null); DynamicTeam team = new TeamImpl(competitorDescriptor.getName(), Collections.singleton(sailor), null); - DynamicBoat boat = new BoatImpl(competitorDescriptor.getSailNumber(), boatClass, competitorDescriptor.getSailNumber()); - Competitor competitor = new CompetitorImpl(UUID.randomUUID(), competitorDescriptor.getName(), + BoatClass boatClass = getBaseDomainFactory().getOrCreateBoatClass(competitorDescriptor.getBoatClassName()); + DynamicBoat boat = new BoatImpl(competitorUUID, competitorDescriptor.getBoatName(), boatClass, competitorDescriptor.getSailNumber()); + CompetitorWithBoat competitorWithBoat = new CompetitorWithBoatImpl(boatUUID, competitorDescriptor.getName(), competitorDescriptor.getSailNumber(), /* color */ null, /* eMail */ null, /* flag image */ null, team, - boat, competitorDescriptor.getTimeOnTimeFactor(), - competitorDescriptor.getTimeOnDistanceAllowancePerNauticalMile(), searchTag); - return competitor; + competitorDescriptor.getTimeOnTimeFactor(), + competitorDescriptor.getTimeOnDistanceAllowancePerNauticalMile(), searchTag, boat); + + return competitorWithBoat; } @Override @@ -5022,7 +5093,99 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S } getService().apply(new AllowCompetitorResetToDefaults(competitorIdsAsStrings)); } - + + @Override + public Iterable getAllBoats() { + return convertToBoatDTOs(getService().getBaseDomainFactory().getCompetitorStore().getBoats()); + } + + @Override + public Iterable getStandaloneBoats() { + return convertToBoatDTOs(getService().getBaseDomainFactory().getCompetitorStore().getStandaloneBoats()); + } + + @Override + public BoatDTO addOrUpdateBoat(BoatDTO boat) { + return getBaseDomainFactory().convertToBoatDTO(addOrUpdateBoatInternal(boat)); + } + + private Boat addOrUpdateBoatInternal(BoatDTO boat) { + Boat existingBoat = getService().getCompetitorStore().getExistingBoatByIdAsString(boat.getIdAsString()); + final Boat result; + if (boat.getIdAsString() == null || boat.getIdAsString().isEmpty() || existingBoat == null) { + // new boat + UUID boatUUID = UUID.randomUUID(); + BoatClass boatClass = getBaseDomainFactory().getOrCreateBoatClass(boat.getBoatClass().getName()); + result = getBaseDomainFactory().getOrCreateBoat(boatUUID, boat.getName(), boatClass, boat.getSailId(), boat.getColor()); + } else { + result = getService().apply( + new UpdateBoat(boat.getIdAsString(), boat.getName(), boat.getColor(), boat.getSailId())); + } + return result; + } + + @Override + public void allowBoatResetToDefaults(Iterable boats) { + List boatIdsAsStrings = new ArrayList(); + for (BoatDTO boat : boats) { + boatIdsAsStrings.add(boat.getIdAsString()); + } + getService().apply(new AllowBoatResetToDefaults(boatIdsAsStrings)); + } + + @Override + public boolean linkBoatToCompetitorForRace(String leaderboardName, String raceColumnName, String fleetName, String competitorIdAsString, String boatIdAsString) { + boolean result = false; + Boat existingBoat = getService().getCompetitorStore().getExistingBoatByIdAsString(boatIdAsString); + Competitor existingCompetitor = getService().getCompetitorStore().getExistingCompetitorByIdAsString(competitorIdAsString); + RaceLog raceLog = getService().getRaceLog(leaderboardName, raceColumnName, fleetName); + if (raceLog != null && existingCompetitor != null && existingBoat != null) { + raceLog.add(new RaceLogRegisterCompetitorEventImpl(MillisecondsTimePoint.now(), + getService().getServerAuthor(), raceLog.getCurrentPassId(), existingCompetitor, existingBoat)); + result = true; + } + return result; + } + + @Override + public boolean unlinkBoatFromCompetitorForRace(String leaderboardName, String raceColumnName, String fleetName, String competitorIdAsString) { + boolean result = false; + Competitor existingCompetitor = getService().getCompetitorStore().getExistingCompetitorByIdAsString(competitorIdAsString); + RaceLog raceLog = getService().getRaceLog(leaderboardName, raceColumnName, fleetName); + if (raceLog != null && existingCompetitor != null) { + List linkEventsToRevoke = new ArrayList<>(); + for (RaceLogEvent event : raceLog.getUnrevokedEventsDescending()) { + if (event instanceof RaceLogRegisterCompetitorEvent) { + linkEventsToRevoke.add((RaceLogRegisterCompetitorEvent) event); + } + } + try { + for (RaceLogRegisterCompetitorEvent eventToRevoke : linkEventsToRevoke) { + raceLog.revokeEvent(getService().getServerAuthor(), eventToRevoke, "unlink competitor from boat"); + } + } catch (NotRevokableException e) { + logger.log(Level.WARNING, "Could not unlink competitor from boat by adding RevokeEvent", e); + } + + result = true; + } + return result; + } + + @Override + public BoatDTO getBoatLinkedToCompetitorForRace(String leaderboardName, String raceColumnName, String fleetName, String competitorIdAsString) { + BoatDTO result = null; + Competitor existingCompetitor = getService().getCompetitorStore().getExistingCompetitorByIdAsString(competitorIdAsString); + Map competitorToBoatMappingsForRace = getService().getCompetitorToBoatMappingsForRace(leaderboardName, raceColumnName, fleetName); + if (existingCompetitor != null) { + Boat boatOfCompetitor = competitorToBoatMappingsForRace.get(existingCompetitor); + if (boatOfCompetitor != null) { + result = baseDomainFactory.convertToBoatDTO(boatOfCompetitor); + } + } + return result; + } + @Override public List getDeviceConfigurationMatchers() { List configs = new ArrayList(); @@ -5487,31 +5650,69 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S return raceColumn.getRaceLog(fleet); } - private Competitor getCompetitor(CompetitorDTO dto) { + private Competitor getCompetitor(CompetitorWithoutBoatDTO dto) { return getService().getCompetitorStore().getExistingCompetitorByIdAsString(dto.getIdAsString()); } - + + private Boat getBoat(BoatDTO dto) { + return getService().getCompetitorStore().getExistingBoatByIdAsString(dto.getIdAsString()); + } + @Override public void setCompetitorRegistrationsInRaceLog(String leaderboardName, String raceColumnName, String fleetName, - Set competitorDTOs) throws CompetitorRegistrationOnRaceLogDisabledException, NotFoundException { - Set competitorsToRegister = new HashSet(); - for (CompetitorDTO dto : competitorDTOs) { - competitorsToRegister.add(getCompetitor(dto)); + Map competitorAndBoatDTOs) + throws CompetitorRegistrationOnRaceLogDisabledException, NotFoundException { + Set competitorsToRegister = new HashSet<>(); + Map competitorToBoatMapping = new HashMap<>(); + for (Entry competitorAndBoatEntry: competitorAndBoatDTOs.entrySet()) { + Competitor competitor = getCompetitor(competitorAndBoatEntry.getKey()); + competitorsToRegister.add(competitor); + competitorToBoatMapping.put(competitor, getBoat(competitorAndBoatEntry.getValue())); } RaceColumn raceColumn = getRaceColumn(leaderboardName, raceColumnName); Fleet fleet = getFleetByName(raceColumn, fleetName); - Iterable competitorsToRemove = raceColumn.getCompetitorsRegisteredInRacelog(fleet); + + Collection competitorsToRemove = raceColumn.getCompetitorsRegisteredInRacelog(fleet).keySet(); HashSet competitorSetToRemove = new HashSet<>(); Util.addAll(competitorsToRemove, competitorSetToRemove); - filterDuplicates(competitorsToRegister, competitorSetToRemove); + filterCompetitorDuplicates(competitorsToRegister, competitorSetToRemove); raceColumn.deregisterCompetitors(competitorSetToRemove, fleet); - raceColumn.registerCompetitors(competitorsToRegister, fleet); + + // we assume that the competitors id of type Competitor here, so we need to find the corresponding boat + for (Competitor competitorToRegister: competitorsToRegister) { + raceColumn.registerCompetitor(competitorToRegister, competitorToBoatMapping.get(competitorToRegister), fleet); + } } @Override - public void setCompetitorRegistrationsInRegattaLog(String leaderboardName, Set competitorDTOs) + public void setCompetitorRegistrationsInRaceLog(String leaderboardName, String raceColumnName, String fleetName, + Set competitorDTOs) throws CompetitorRegistrationOnRaceLogDisabledException, NotFoundException { + Set competitorsToRegister = new HashSet<>(); + for (CompetitorWithoutBoatDTO dto : competitorDTOs) { + competitorsToRegister.add(getCompetitor(dto)); + } + RaceColumn raceColumn = getRaceColumn(leaderboardName, raceColumnName); + Fleet fleet = getFleetByName(raceColumn, fleetName); + Collection competitorsToRemove = raceColumn.getCompetitorsRegisteredInRacelog(fleet).keySet(); + HashSet competitorSetToRemove = new HashSet<>(); + Util.addAll(competitorsToRemove, competitorSetToRemove); + filterCompetitorDuplicates(competitorsToRegister, competitorSetToRemove); + raceColumn.deregisterCompetitors(competitorSetToRemove, fleet); + // we assume that the competitors id of type Competitor here, so we need to find the corresponding boat + for (Competitor competitorToRegister : competitorsToRegister) { + if (competitorToRegister.hasBoat()) { + raceColumn.registerCompetitor((CompetitorWithBoat) competitorToRegister, fleet); + } else { + logger.warning("The competitor "+competitorToRegister.getName()+" does not have a boat associated but should have; "+ + "competitor is not registered for race log of race "+raceColumnName+" in leaderboard "+leaderboardName+" for fleet "+fleetName); + } + } + } + + @Override + public void setCompetitorRegistrationsInRegattaLog(String leaderboardName, Set competitorDTOs) throws DoesNotHaveRegattaLogException, NotFoundException { Leaderboard leaderboard = getLeaderboardByName(leaderboardName); if (!(leaderboard instanceof HasRegattaLike)){ @@ -5519,7 +5720,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S } Set competitorsToRegister = new HashSet(); - for (CompetitorDTO dto : competitorDTOs) { + for (CompetitorWithoutBoatDTO dto : competitorDTOs) { competitorsToRegister.add(getCompetitor(dto)); } @@ -5527,13 +5728,13 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S Iterable competitorsToRemove = leaderboard.getAllCompetitors(); HashSet competitorSetToRemove = new HashSet<>(); Util.addAll(competitorsToRemove, competitorSetToRemove); - filterDuplicates(competitorsToRegister, competitorSetToRemove); + filterCompetitorDuplicates(competitorsToRegister, competitorSetToRemove); hasRegattaLike.deregisterCompetitors(competitorSetToRemove); hasRegattaLike.registerCompetitors(competitorsToRegister); } - private HashSet filterDuplicates(Set competitorsToRegister, HashSet competitorSetToRemove) { + private HashSet filterCompetitorDuplicates(Set competitorsToRegister, HashSet competitorSetToRemove) { for (Iterator iterator = competitorSetToRemove.iterator(); iterator.hasNext();) { Competitor competitor = iterator.next(); if (competitorsToRegister.remove(competitor)) { @@ -5542,7 +5743,17 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S } return competitorSetToRemove; } - + + private HashSet filterBoatDuplicates(Set boatsToRegister, HashSet boatSetToRemove) { + for (Iterator iterator = boatSetToRemove.iterator(); iterator.hasNext();) { + Boat boat = iterator.next(); + if (boatsToRegister.remove(boat)) { + iterator.remove(); + } + } + return boatSetToRemove; + } + private Mark convertToMark(MarkDTO dto, boolean resolve) { Mark result = null; if (resolve) { @@ -5762,8 +5973,11 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S item = baseDomainFactory.convertToCompetitorDTO((Competitor) mapping.getMappedTo()); } else if (mappedTo instanceof Mark) { item = convertToMarkDTO((Mark) mapping.getMappedTo(), null); + } else if (mappedTo instanceof Boat) { + item = baseDomainFactory.convertToBoatDTO((Boat) mappedTo); } else { - throw new RuntimeException("Can only handle Competitor or Mark as mapped item type, but not "+mappedTo.getClass().getName()); + throw new RuntimeException("Can only handle Competitor, Boat or Mark as mapped item type, but not " + + mappedTo.getClass().getName()); } //Only deal with UUIDs - otherwise we would have to pass Serializable to browser context - which //has a large performance implact for GWT. @@ -5833,8 +6047,13 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S event = new RegattaLogDeviceCompetitorMappingEventImpl(now, now, getService().getServerAuthor(), UUID.randomUUID(), competitor, mapping.getDevice(), from, to); } + } else if (dto.mappedTo instanceof BoatDTO) { + final Boat boat = getService().getCompetitorStore() + .getExistingBoatByIdAsString(((BoatDTO) dto.mappedTo).getIdAsString()); + event = new RegattaLogDeviceBoatMappingEventImpl(now, now, getService().getServerAuthor(), UUID.randomUUID(), + boat, mapping.getDevice(), from, to); } else { - throw new RuntimeException("Can only map devices to competitors or marks"); + throw new RuntimeException("Can only map devices to competitors, boats or marks"); } regattaLog.add(event); } @@ -5852,8 +6071,12 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S DoubleVectorFixImporter importer = getRegisteredImporter(DoubleVectorFixImporter.class, dto.dataType); event = importer.createEvent(now, now, getService().getServerAuthor(), UUID.randomUUID(), getCompetitor((CompetitorDTO) dto.mappedTo), mapping.getDevice(), from, to); + } else if (dto.mappedTo instanceof BoatDTO) { + DoubleVectorFixImporter importer = getRegisteredImporter(DoubleVectorFixImporter.class, dto.dataType); + event = importer.createEvent(now, now, getService().getServerAuthor(), UUID.randomUUID(), + getBoat((BoatDTO) dto.mappedTo), mapping.getDevice(), from, to); } else { - throw new RuntimeException("Can only map devices to competitors"); + throw new RuntimeException("Can only map devices to a competitor or boat"); } regattaLog.add(event); } @@ -5868,7 +6091,8 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S return result; } - DeviceMapping convertToDeviceMapping(DeviceMappingDTO dto) throws NoCorrespondingServiceRegisteredException, TransformationException { + private DeviceMapping convertToDeviceMapping(DeviceMappingDTO dto) + throws NoCorrespondingServiceRegisteredException, TransformationException { DeviceIdentifier device = deserializeDeviceIdentifier(dto.deviceIdentifier.deviceType, dto.deviceIdentifier.deviceId); TimePoint from = dto.from == null ? null : new MillisecondsTimePoint(dto.from); TimePoint to = dto.to == null ? null : new MillisecondsTimePoint(dto.to); @@ -5881,8 +6105,13 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S Competitor competitor = getService().getCompetitorStore().getExistingCompetitorByIdAsString( ((CompetitorDTO) dto.mappedTo).getIdAsString()); return new DeviceMappingImpl(competitor, device, timeRange, dto.originalRaceLogEventIds, RegattaLogDeviceCompetitorMappingEventImpl.class); + } else if (dto.mappedTo instanceof BoatDTO) { + final Boat boat = getService().getCompetitorStore() + .getExistingBoatByIdAsString(dto.mappedTo.getIdAsString()); + return new DeviceMappingImpl(boat, device, timeRange, dto.originalRaceLogEventIds, + RegattaLogDeviceBoatMappingEventImpl.class); } else { - throw new RuntimeException("Can only map devices to competitors or marks"); + throw new RuntimeException("Can only map devices to competitors, boats or marks"); } } @@ -6088,7 +6317,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S Competitor competitor = getCompetitor(competitorDTO); RaceLogFixedMarkPassingEvent oldFixedMarkPassingEvent = null; for (RaceLogEvent event : raceLog.getUnrevokedEvents()) { - if (event instanceof RaceLogFixedMarkPassingEventImpl && event.getInvolvedBoats().contains(competitor)) { + if (event instanceof RaceLogFixedMarkPassingEventImpl && event.getInvolvedCompetitors().contains(competitor)) { RaceLogFixedMarkPassingEvent fixedEvent = (RaceLogFixedMarkPassingEvent) event; if (Util.equalsWithNull(fixedEvent.getZeroBasedIndexOfPassedWaypoint(), indexOfWaypoint)) { oldFixedMarkPassingEvent = fixedEvent; @@ -6117,7 +6346,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S Competitor competitor = getCompetitor(competitorDTO); NavigableSet unrevokedEvents = raceLog.getUnrevokedEvents(); for (RaceLogEvent event : unrevokedEvents) { - if (event instanceof RaceLogSuppressedMarkPassingsEvent && event.getInvolvedBoats().contains(competitor)) { + if (event instanceof RaceLogSuppressedMarkPassingsEvent && event.getInvolvedCompetitors().contains(competitor)) { oldSuppressedMarkPassingEvent = (RaceLogSuppressedMarkPassingsEvent) event; break; } @@ -6416,7 +6645,55 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S String fleetName) throws NotFoundException { RaceColumn raceColumn = getRaceColumn(leaderboardName, raceColumnName); Fleet fleet = getFleetByName(raceColumn, fleetName); - return convertToCompetitorDTOs(raceColumn.getCompetitorsRegisteredInRacelog(fleet)); + return convertToCompetitorDTOs(raceColumn.getCompetitorsRegisteredInRacelog(fleet).keySet()); + } + + @Override + public Map getCompetitorAndBoatRegistrationsInRaceLog(String leaderboardName, String raceColumnName, + String fleetName) throws NotFoundException { + RaceColumn raceColumn = getRaceColumn(leaderboardName, raceColumnName); + Fleet fleet = getFleetByName(raceColumn, fleetName); + Map competitorsAndBoatsRegisteredInRacelog = raceColumn.getCompetitorsRegisteredInRacelog(fleet); + return baseDomainFactory.convertToCompetitorAndBoatDTOs(competitorsAndBoatsRegisteredInRacelog); + } + + @Override + public Collection getBoatRegistrationsInRegattaLog(String leaderboardName) throws DoesNotHaveRegattaLogException, NotFoundException { + Leaderboard leaderboard = getLeaderboardByName(leaderboardName); + if (! (leaderboard instanceof HasRegattaLike)) { + throw new DoesNotHaveRegattaLogException(); + } + HasRegattaLike regattaLikeLeaderboard = ((HasRegattaLike) leaderboard); + return convertToBoatDTOs(regattaLikeLeaderboard.getBoatsRegisteredInRegattaLog()); + } + + @Override + public void setBoatRegistrationsInRegattaLog(String leaderboardName, Set boatDTOs) + throws DoesNotHaveRegattaLogException, NotFoundException { + Leaderboard leaderboard = getLeaderboardByName(leaderboardName); + if (!(leaderboard instanceof HasRegattaLike)){ + throw new DoesNotHaveRegattaLogException(); + } + + Set boatsToRegister = new HashSet(); + for (BoatDTO dto : boatDTOs) { + boatsToRegister.add(getBoat(dto)); + } + + HasRegattaLike hasRegattaLike = (HasRegattaLike) leaderboard; + Iterable boatsToRemove = leaderboard.getAllBoats(); + HashSet boatSetToRemove = new HashSet<>(); + Util.addAll(boatsToRemove, boatSetToRemove); + filterBoatDuplicates(boatsToRegister, boatSetToRemove); + + hasRegattaLike.deregisterBoats(boatSetToRemove); + hasRegattaLike.registerBoats(boatsToRegister); + } + + @Override + public Collection getBoatRegistrationsForLeaderboard(String leaderboardName) throws NotFoundException { + Leaderboard leaderboard = getLeaderboardByName(leaderboardName); + return convertToBoatDTOs(leaderboard.getAllBoats()); } @Override @@ -6855,23 +7132,24 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S templateDTO.getCompetitorCount(), templateDTO.getFlightMultiplier()); PairingList pairingList = getService() .getPairingListFromTemplate(pairingListTemplate, leaderboardName, selectedRaces); - List>>> result = new ArrayList<>(); + List>>> result = new ArrayList<>(); for (RaceColumn raceColumn : selectedRaces) { - List>> raceColumnList = new ArrayList<>(); + List>> raceColumnList = new ArrayList<>(); for (Fleet fleet : raceColumn.getFleets()) { - List> fleetList = new ArrayList<>(); + List> fleetList = new ArrayList<>(); for (Pair competitorAndBoatPair : pairingList.getCompetitors(raceColumn, fleet)) { + final Boat boat = competitorAndBoatPair.getB(); if (competitorAndBoatPair.getA() != null) { - fleetList.add(new Pair( + fleetList.add(new Pair<>( baseDomainFactory.convertToCompetitorDTO(competitorAndBoatPair.getA()), - new BoatDTO(competitorAndBoatPair.getB().getName(), - competitorAndBoatPair.getB().getSailID(), - competitorAndBoatPair.getB().getColor()))); + new BoatDTO(boat.getId().toString(), boat.getName(), convertToBoatClassDTO(boat.getBoatClass()), + boat.getSailID(), + boat.getColor()))); } else { - fleetList.add(new Pair(new CompetitorDTOImpl(), - new BoatDTO(competitorAndBoatPair.getB().getName(), - competitorAndBoatPair.getB().getSailID(), - competitorAndBoatPair.getB().getColor()))); + fleetList.add(new Pair<>(/* no competitor */ null, + new BoatDTO(boat.getId().toString(), boat.getName(), convertToBoatClassDTO(boat.getBoatClass()), + boat.getSailID(), + boat.getColor()))); } } raceColumnList.add(fleetList); @@ -6884,19 +7162,20 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S @Override public PairingListDTO getPairingListFromRaceLogs(final String leaderboardName) throws NotFoundException { Leaderboard leaderboard = getLeaderboardByName(leaderboardName); - - List>>> result = new ArrayList<>(); + List>>> result = new ArrayList<>(); List raceColumnNames = new ArrayList<>(); PairingListLeaderboardAdapter adapter = new PairingListLeaderboardAdapter(); for (RaceColumn raceColumn : leaderboard.getRaceColumns()) { if (!raceColumn.isMedalRace()) { - List>> raceColumnList = new ArrayList<>(); + List>> raceColumnList = new ArrayList<>(); for (Fleet fleet : raceColumn.getFleets()) { - List> fleetList = new ArrayList<>(); + List> fleetList = new ArrayList<>(); for (Pair competitorAndBoatPair : adapter.getCompetitors(raceColumn, fleet)) { - fleetList.add(new Pair(baseDomainFactory.convertToCompetitorDTO(competitorAndBoatPair.getA()), - new BoatDTO(competitorAndBoatPair.getB().getName(), competitorAndBoatPair.getB().getSailID(), - competitorAndBoatPair.getB().getColor()))); + final Boat boat = competitorAndBoatPair.getB(); + fleetList.add(new Pair(baseDomainFactory.convertToCompetitorDTO(competitorAndBoatPair.getA()), + new BoatDTO(boat.getId().toString(), boat.getName(), + convertToBoatClassDTO(boat.getBoatClass()), boat.getSailID(), + boat.getColor()))); } if (fleetList.size() > 0) { raceColumnList.add(fleetList); @@ -6904,14 +7183,12 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S } if (raceColumnList.size() > 0) { result.add(raceColumnList); - if (!raceColumnNames.contains(raceColumn.getName())) { raceColumnNames.add(raceColumn.getName()); } } } } - return new PairingListDTO(result, raceColumnNames); } @@ -6927,16 +7204,14 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S groupCount = 0; for (Fleet fleet : raceColumn.getFleets()) { raceColumn.enableCompetitorRegistrationOnRaceLog(fleet); - Set competitors = new HashSet<>(); - List> competitorsFromPairingList = pairingListDTO.getPairingList() + Map competitors = new HashMap<>(); + List> competitorsFromPairingList = pairingListDTO.getPairingList() .get(flightCount).get(groupCount); - for (Pair competitorAndBoatPair : competitorsFromPairingList) { + for (Pair competitorAndBoatPair : competitorsFromPairingList) { if (competitorAndBoatPair.getA() != null && competitorAndBoatPair.getA().getName() != null) { - competitors.add(competitorAndBoatPair.getA()); + competitors.put(competitorAndBoatPair.getA(), competitorAndBoatPair.getB()); } } - // TODO set boat and competitors in race logs (bug4403) - // TODO add Javadoc to setCompetitorRegistrationsInRacelog this.setCompetitorRegistrationsInRaceLog(leaderboard.getName(), raceColumn.getName(), fleet.getName(), competitors); groupCount++; @@ -6967,6 +7242,8 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S for (Fleet fleet : raceColumn.getFleets()) { if(raceColumn.getTrackedRace(fleet) != null && raceColumn.getTrackedRace(fleet).getRaceIdentifier()!=null) { result.add(raceColumn.getTrackedRace(fleet).getRaceIdentifier().getRaceName()); + } else { + break; } } } @@ -7153,7 +7430,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S public void visit(RaceLogRegisterCompetitorEvent event) { raceLog.add(new RaceLogRegisterCompetitorEventImpl(event.getCreatedAt(), event.getLogicalTimePoint(), event.getAuthor(), UUID.randomUUID(), - raceLog.getCurrentPassId(), event.getCompetitor())); + raceLog.getCurrentPassId(), event.getCompetitor(), event.getBoat())); } @Override @@ -7221,7 +7498,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S if (hasStartTime && isLatestPass(event) && timeRange.includes(event.getTimePointOfFixedPassing())) { raceLog.add(new RaceLogFixedMarkPassingEventImpl(event.getCreatedAt(), event.getLogicalTimePoint(), event.getAuthor(), UUID.randomUUID(), - event.getInvolvedBoats(), raceLog.getCurrentPassId(), + event.getInvolvedCompetitors(), raceLog.getCurrentPassId(), event.getTimePointOfFixedPassing(), event.getZeroBasedIndexOfPassedWaypoint())); } } @@ -7231,7 +7508,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S if (hasStartTime && isLatestPass(event) && timeRange.includes(event.getLogicalTimePoint())) { raceLog.add(new RaceLogSuppressedMarkPassingsEventImpl(event.getCreatedAt(), event.getLogicalTimePoint(), event.getAuthor(), UUID.randomUUID(), - event.getInvolvedBoats(), raceLog.getCurrentPassId(), + event.getInvolvedCompetitors(), raceLog.getCurrentPassId(), event.getZeroBasedIndexOfFirstSuppressedWaypoint())); } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/RaceWithCompetitorsAndBoatsDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/RaceWithCompetitorsAndBoatsDTO.java new file mode 100644 index 00000000000..fc01a9db971 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/RaceWithCompetitorsAndBoatsDTO.java @@ -0,0 +1,44 @@ +package com.sap.sailing.gwt.ui.shared; + +import java.util.Collection; +import java.util.Map; + +import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; +import com.sap.sailing.domain.common.dto.BoatDTO; +import com.sap.sailing.domain.common.dto.CompetitorDTO; +import com.sap.sailing.domain.common.dto.RaceDTO; +import com.sap.sailing.domain.common.dto.TrackedRaceDTO; + +public class RaceWithCompetitorsAndBoatsDTO extends RaceDTO { + private static final long serialVersionUID = 5389509649915599951L; + private Map competitorsAndBoats; + + /** + * Constructor for GWT serialization. + */ + RaceWithCompetitorsAndBoatsDTO() {} + + public RaceWithCompetitorsAndBoatsDTO(RegattaAndRaceIdentifier raceIdentifier, Map competitorsAndBoats, + TrackedRaceDTO trackedRace, boolean isCurrentlyTracked) { + super(raceIdentifier, trackedRace, isCurrentlyTracked); + this.competitorsAndBoats = competitorsAndBoats; + } + + public RaceWithCompetitorsAndBoatsDTO(RegattaAndRaceIdentifier raceIdentifier, Map competitorsAndBoats) { + super(raceIdentifier); + this.competitorsAndBoats = competitorsAndBoats; + } + + public Map getCompetitorsAndBoats() { + return competitorsAndBoats; + } + + public Iterable getCompetitors() { + return competitorsAndBoats.keySet(); + } + + public Collection getBoats() { + return competitorsAndBoats.values(); + } + +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/RaceWithCompetitorsDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/RaceWithCompetitorsDTO.java deleted file mode 100644 index 5430bce8913..00000000000 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/RaceWithCompetitorsDTO.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.sap.sailing.gwt.ui.shared; - -import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; -import com.sap.sailing.domain.common.dto.CompetitorDTO; -import com.sap.sailing.domain.common.dto.RaceDTO; -import com.sap.sailing.domain.common.dto.TrackedRaceDTO; - -public class RaceWithCompetitorsDTO extends RaceDTO { - private static final long serialVersionUID = 5389509649915599951L; - private Iterable competitors; - - /** - * Constructor for GWT serialization. - */ - RaceWithCompetitorsDTO() {} - - public RaceWithCompetitorsDTO(RegattaAndRaceIdentifier raceIdentifier, Iterable competitors, - TrackedRaceDTO trackedRace, boolean isCurrentlyTracked) { - super(raceIdentifier, trackedRace, isCurrentlyTracked); - this.competitors = competitors; - } - - public RaceWithCompetitorsDTO(RegattaAndRaceIdentifier raceIdentifier, Iterable competitors) { - super(raceIdentifier); - this.competitors = competitors; - } - - public Iterable getCompetitors() { - return competitors; - } -} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/RaceboardDataDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/RaceboardDataDTO.java index 5d49568ef14..bc153f1ae77 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/RaceboardDataDTO.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/RaceboardDataDTO.java @@ -7,8 +7,7 @@ import com.sap.sailing.domain.common.dto.BoatDTO; import com.sap.sailing.domain.common.dto.CompetitorDTO; public class RaceboardDataDTO implements IsSerializable { - private Map competitorAndTheirBoats; - private RaceWithCompetitorsDTO race; + private RaceWithCompetitorsAndBoatsDTO race; private boolean isValidLeaderboardGroup; private boolean isValidLeaderboard; private boolean isValidEvent; @@ -16,24 +15,22 @@ public class RaceboardDataDTO implements IsSerializable { // for GWT RaceboardDataDTO() {} - public RaceboardDataDTO(RaceWithCompetitorsDTO race, Map competitorAndTheirBoats, - boolean isValidLeaderboard, boolean isValidLeaderboardGroup, boolean isValidEvent) { + public RaceboardDataDTO(RaceWithCompetitorsAndBoatsDTO race, boolean isValidLeaderboard, boolean isValidLeaderboardGroup, boolean isValidEvent) { this.race = race; - this.competitorAndTheirBoats = competitorAndTheirBoats; this.isValidLeaderboard = isValidLeaderboard; this.isValidLeaderboardGroup = isValidLeaderboardGroup; this.isValidEvent = isValidEvent; } public Map getCompetitorAndTheirBoats() { - return competitorAndTheirBoats; + return race.getCompetitorsAndBoats(); } public Iterable getCompetitors() { return race.getCompetitors(); } - public RaceWithCompetitorsDTO getRace() { + public RaceWithCompetitorsAndBoatsDTO getRace() { return race; } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/RegattaDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/RegattaDTO.java index 1b279548778..60dbcedc501 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/RegattaDTO.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/RegattaDTO.java @@ -28,7 +28,7 @@ public class RegattaDTO extends NamedDTO { public BoatClassDTO boatClass; public Date startDate; public Date endDate; - public List races; + public List races; public List series; public ScoringSchemeType scoringScheme; public UUID defaultCourseAreaUuid; @@ -36,6 +36,7 @@ public class RegattaDTO extends NamedDTO { public DeviceConfigurationDTO.RegattaConfigurationDTO configuration; public boolean useStartTimeInference = true; public boolean controlTrackingFromStartAndFinishTimes = false; + public boolean canBoatsOfCompetitorsChangePerRace = false; public RankingMetrics rankingMetricType; public Double buoyZoneRadiusInHullLengths; @@ -68,6 +69,7 @@ public class RegattaDTO extends NamedDTO { this.configuration = other.configuration; this.useStartTimeInference = other.useStartTimeInference; this.controlTrackingFromStartAndFinishTimes = other.controlTrackingFromStartAndFinishTimes; + this.canBoatsOfCompetitorsChangePerRace = other.canBoatsOfCompetitorsChangePerRace; this.buoyZoneRadiusInHullLengths = other.buoyZoneRadiusInHullLengths; } diff --git a/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/client/images/boat_registrations.png b/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/client/images/boat_registrations.png new file mode 100644 index 00000000000..6f90a4bd9bc Binary files /dev/null and b/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/client/images/boat_registrations.png differ diff --git a/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/client/images/sailboat.png b/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/client/images/sailboat.png new file mode 100644 index 00000000000..6f90a4bd9bc Binary files /dev/null and b/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/client/images/sailboat.png differ diff --git a/java/com.sap.sailing.manage2sail.resultimport/src/com/sap/sailing/manage2sail/resultimport/CompetitorImporter.java b/java/com.sap.sailing.manage2sail.resultimport/src/com/sap/sailing/manage2sail/resultimport/CompetitorImporter.java index 38402b3a55e..c1aefb89e9d 100644 --- a/java/com.sap.sailing.manage2sail.resultimport/src/com/sap/sailing/manage2sail/resultimport/CompetitorImporter.java +++ b/java/com.sap.sailing.manage2sail.resultimport/src/com/sap/sailing/manage2sail/resultimport/CompetitorImporter.java @@ -7,6 +7,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.UUID; import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; @@ -170,13 +171,25 @@ public class CompetitorImporter extends AbstractManage2SailProvider implements C nationality==null?null:nationality.getCountryCode().getThreeLetterIOCCode()); return person; }).collect(Collectors.toList()); + UUID competitorUUID; + try { + competitorUUID = UUID.fromString(team.getTeamID()); + } catch (IllegalArgumentException e) { + competitorUUID = null; + } + UUID boatUUID; + try { + boatUUID = UUID.fromString(boat.getBoatID()); + } catch (IllegalArgumentException e) { + boatUUID = null; + } final CompetitorDescriptor competitorDescriptor = new CompetitorDescriptor( - event==null?null:event.getTitle(), - division==null?null:(division.getTitle() + (division.getGender() == null ? "" : division.getGender().name())), - boatClassName, race != null ? race.getRaceName() : null, /* fleetName */ null, sailNumber, - team.getTeamName(), team.getTeamName(), boat.getBoatName(), - teamNationality[0] == null ? null : teamNationality[0].getCountryCode(), - persons, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null); + event == null ? null : event.getTitle(), + division == null ? null : (division.getTitle() + (division.getGender() == null ? "" : division.getGender().name())), + race != null ? race.getRaceName() : null, /* fleetName */ null, competitorUUID, /* name */ team.getTeamName(), + /* short name */ null, /* team name */ team.getTeamName(), persons, + teamNationality[0] == null ? null : teamNationality[0].getCountryCode(), /* timeOnTimeFactor */ null, + /* timeOnDistanceAllowancePerNauticalMile */ null, boatUUID, boat.getBoatName(), boatClassName, sailNumber); return competitorDescriptor; } diff --git a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/AbstractTestStoringAndRetrievingRaceLogInRegatta.java b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/AbstractTestStoringAndRetrievingRaceLogInRegatta.java index bbb5f05021d..6f50e726394 100755 --- a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/AbstractTestStoringAndRetrievingRaceLogInRegatta.java +++ b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/AbstractTestStoringAndRetrievingRaceLogInRegatta.java @@ -61,7 +61,8 @@ public abstract class AbstractTestStoringAndRetrievingRaceLogInRegatta extends R boolean persistent, ScoringScheme scoringScheme, CourseArea courseArea) { List series = createSeriesForTestRegatta(); Regatta regatta = new RegattaImpl(getRaceLogStore(), getRegattaLogStore(), RegattaImpl.getDefaultName(regattaBaseName, - boatClass == null ? null : boatClass.getName()), boatClass, /*startDate*/ null, /*endDate*/ null, series, persistent, scoringScheme, "123", + boatClass == null ? null : boatClass.getName()), boatClass, + /* canBoatsOfCompetitorsChangePerRace */ true, /*startDate*/ null, /*endDate*/ null, series, persistent, scoringScheme, "123", courseArea, /*buoyZoneRadiusInHullLengths*/2.0, /* useStartTimeInference */ true, /* controlTrackingFromStartAndFinishTimes */ false, OneDesignRankingMetric::new); return regatta; } diff --git a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/RaceLogConnectivityParamsLoadAndStoreTest.java b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/RaceLogConnectivityParamsLoadAndStoreTest.java index b94514684c6..5b844e71982 100755 --- a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/RaceLogConnectivityParamsLoadAndStoreTest.java +++ b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/RaceLogConnectivityParamsLoadAndStoreTest.java @@ -49,7 +49,7 @@ public class RaceLogConnectivityParamsLoadAndStoreTest extends AbstractConnectiv final SeriesImpl theSeries = new SeriesImpl("Default", /* isMedal */ false, /* isFleetsCanRunInParallel */ true, Arrays.asList(fleet), Collections.emptyList(), racingEventService); final Iterable series = Arrays.asList(theSeries); - final Regatta regatta = racingEventService.createRegatta("My Regatta", "12mR", MillisecondsTimePoint.now(), MillisecondsTimePoint.now().plus(Duration.ONE_DAY), + final Regatta regatta = racingEventService.createRegatta("My Regatta", "12mR", true, MillisecondsTimePoint.now(), MillisecondsTimePoint.now().plus(Duration.ONE_DAY), UUID.randomUUID(), series, /* persistent */ true, new LowPoint(), new CourseAreaImpl("Default", UUID.randomUUID()), /* buoyZoneRadiusInHullLengths */ 2.0, /* useStartTimeInference */ true, /* controlTrackingFromStartAndFinishTimes */ false, OneDesignRankingMetric::new); diff --git a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/StoreAndLoadBoatsTest.java b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/StoreAndLoadBoatsTest.java new file mode 100644 index 00000000000..c992b6a0f36 --- /dev/null +++ b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/StoreAndLoadBoatsTest.java @@ -0,0 +1,123 @@ +package com.sap.sailing.mongodb.test; + +import static org.junit.Assert.assertEquals; + +import java.net.URISyntaxException; +import java.net.UnknownHostException; +import java.util.Collection; +import java.util.UUID; + +import org.junit.Before; +import org.junit.Test; + +import com.mongodb.DB; +import com.mongodb.DBCollection; +import com.mongodb.MongoException; +import com.mongodb.WriteConcern; +import com.sap.sailing.domain.base.Boat; +import com.sap.sailing.domain.base.BoatClass; +import com.sap.sailing.domain.base.DomainFactory; +import com.sap.sailing.domain.base.impl.BoatClassImpl; +import com.sap.sailing.domain.base.impl.BoatImpl; +import com.sap.sailing.domain.base.impl.DomainFactoryImpl; +import com.sap.sailing.domain.base.impl.DynamicBoat; +import com.sap.sailing.domain.persistence.DomainObjectFactory; +import com.sap.sailing.domain.persistence.MongoObjectFactory; +import com.sap.sailing.domain.persistence.PersistenceFactory; +import com.sap.sailing.domain.persistence.impl.CollectionNames; +import com.sap.sse.common.Util; + +public class StoreAndLoadBoatsTest extends AbstractMongoDBTest { + private DomainFactory domainFactory; + private final static BoatClass boatClass505 = new BoatClassImpl("505", /* typicallyStartsUpwind */ true); + + public StoreAndLoadBoatsTest() throws UnknownHostException, MongoException { + super(); + } + + @Before + public void setUp() { + // clear the domainFactory competitor store for a clean start: + domainFactory = new DomainFactoryImpl((srlid)->null); + } + + private void dropBoatCollection() { + DB db = getMongoService().getDB(); + DBCollection boatCollection = db.getCollection(CollectionNames.BOATS.name()); + boatCollection.setWriteConcern(WriteConcern.SAFE); // ensure that the drop() has happened + boatCollection.drop(); + } + + @Test + public void testStoreAndUpdateBoat() throws URISyntaxException { + String sailID1 = "GER 1234"; + String sailID2 = "GER 12345"; + String boatName1 = "Hassos Boot"; + String boatName2 = "Hasso Plattners Boot"; + + MongoObjectFactory mongoObjectFactory = PersistenceFactory.INSTANCE.getMongoObjectFactory(getMongoService()); + DomainObjectFactory domainObjectFactory = PersistenceFactory.INSTANCE.getDomainObjectFactory(getMongoService(), domainFactory); + dropBoatCollection(); + + DynamicBoat boat = (DynamicBoat) new BoatImpl("id12345", boatName1, boatClass505, sailID1); + mongoObjectFactory.storeBoat(boat); + + Collection allBoats= domainObjectFactory.loadAllBoats(); + assertEquals(1, Util.size(allBoats)); + DynamicBoat loadedBoat = (DynamicBoat) allBoats.iterator().next(); + assertEquals(sailID1, loadedBoat.getSailID()); + assertEquals(boatName1, loadedBoat.getName()); + + loadedBoat.setName(boatName2); + loadedBoat.setSailId(sailID2); + mongoObjectFactory.storeBoat(loadedBoat); + + allBoats = domainObjectFactory.loadAllBoats(); + assertEquals(1, Util.size(allBoats)); + loadedBoat = (DynamicBoat) allBoats.iterator().next(); + assertEquals(sailID2, loadedBoat.getSailID()); + assertEquals(boatName2, loadedBoat.getName()); + } + + @Test + public void testStoreAndUpdateBoatWithUUIDAsId() { + MongoObjectFactory mongoObjectFactory = PersistenceFactory.INSTANCE.getMongoObjectFactory(getMongoService()); + DomainObjectFactory domainObjectFactory = PersistenceFactory.INSTANCE.getDomainObjectFactory(getMongoService(), domainFactory); + dropBoatCollection(); + + DynamicBoat boat = (DynamicBoat) new BoatImpl(UUID.randomUUID(), "Hasso", boatClass505, "GER 1234"); + mongoObjectFactory.storeBoat(boat); + assertEquals(1, Util.size(domainObjectFactory.loadAllBoats())); + boat.setName("Hasso Plattner"); + mongoObjectFactory.storeBoat(boat); + assertEquals(1, Util.size(domainObjectFactory.loadAllBoats())); + } + + @Test + public void testStoreAndRemoveBoat() { + MongoObjectFactory mongoObjectFactory = PersistenceFactory.INSTANCE.getMongoObjectFactory(getMongoService()); + DomainObjectFactory domainObjectFactory = PersistenceFactory.INSTANCE.getDomainObjectFactory(getMongoService(), domainFactory); + dropBoatCollection(); + + DynamicBoat boat = (DynamicBoat) new BoatImpl("id12345", "Hassos boot", boatClass505, "GER 1234"); + mongoObjectFactory.storeBoat(boat); + assertEquals(1, Util.size(domainObjectFactory.loadAllBoats())); + + mongoObjectFactory.removeBoat(boat); + assertEquals(0, Util.size(domainObjectFactory.loadAllBoats())); + } + + @Test + public void testStoreAndRemoveCompetitorWithUUIDAsId() { + MongoObjectFactory mongoObjectFactory = PersistenceFactory.INSTANCE.getMongoObjectFactory(getMongoService()); + DomainObjectFactory domainObjectFactory = PersistenceFactory.INSTANCE.getDomainObjectFactory(getMongoService(), domainFactory); + dropBoatCollection(); + + DynamicBoat boat = (DynamicBoat) new BoatImpl(UUID.randomUUID(), "Hassos boot", boatClass505, "GER 1234"); + mongoObjectFactory.storeBoat(boat); + assertEquals(1, Util.size(domainObjectFactory.loadAllBoats())); + + mongoObjectFactory.removeBoat(boat); + assertEquals(0, Util.size(domainObjectFactory.loadAllBoats())); + } +} diff --git a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/StoreCompetitorTest.java b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/StoreAndLoadCompetitorsTest.java similarity index 60% rename from java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/StoreCompetitorTest.java rename to java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/StoreAndLoadCompetitorsTest.java index 2f140adbb70..ff3156e43f6 100755 --- a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/StoreCompetitorTest.java +++ b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/StoreAndLoadCompetitorsTest.java @@ -2,10 +2,12 @@ package com.sap.sailing.mongodb.test; import static org.junit.Assert.assertEquals; +import java.io.Serializable; import java.net.URI; import java.net.URISyntaxException; import java.net.UnknownHostException; import java.util.Collection; +import java.util.Collections; import java.util.UUID; import org.junit.Before; @@ -15,21 +17,52 @@ import com.mongodb.DB; import com.mongodb.DBCollection; import com.mongodb.MongoException; import com.mongodb.WriteConcern; +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.impl.BoatClassImpl; +import com.sap.sailing.domain.base.impl.BoatImpl; +import com.sap.sailing.domain.base.impl.CompetitorImpl; +import com.sap.sailing.domain.base.impl.CompetitorWithBoatImpl; import com.sap.sailing.domain.base.impl.DomainFactoryImpl; +import com.sap.sailing.domain.base.impl.DynamicBoat; import com.sap.sailing.domain.base.impl.DynamicCompetitor; +import com.sap.sailing.domain.base.impl.NationalityImpl; +import com.sap.sailing.domain.base.impl.PersonImpl; +import com.sap.sailing.domain.base.impl.TeamImpl; import com.sap.sailing.domain.persistence.DomainObjectFactory; import com.sap.sailing.domain.persistence.MongoObjectFactory; import com.sap.sailing.domain.persistence.PersistenceFactory; import com.sap.sailing.domain.persistence.impl.CollectionNames; -import com.sap.sailing.domain.test.AbstractLeaderboardTest; +import com.sap.sse.common.Color; import com.sap.sse.common.Util; -public class StoreCompetitorTest extends AbstractMongoDBTest { +public class StoreAndLoadCompetitorsTest extends AbstractMongoDBTest { private DomainFactory domainFactory; + + private final static BoatClass boatClass = new BoatClassImpl("505", /* typicallyStartsUpwind */ true); - public StoreCompetitorTest() throws UnknownHostException, MongoException { + public static Competitor createCompetitor(String competitorName) { + return createCompetitor(competitorName, competitorName); + } + + public static Competitor createCompetitor(String competitorName, Serializable id) { + return new CompetitorImpl(id, competitorName, "KYC", Color.RED, null, null, new TeamImpl("STG", Collections.singleton( + new PersonImpl(competitorName, new NationalityImpl("GER"), + /* dateOfBirth */ null, "This is famous "+competitorName)), + new PersonImpl("Rigo van Maas", new NationalityImpl("NED"), + /* dateOfBirth */null, "This is Rigo, the coach")), + /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null); + } + + public static CompetitorWithBoat createCompetitorWithBoat(String competitorName) { + Competitor c = createCompetitor(competitorName); + DynamicBoat b = new BoatImpl("id12345", competitorName + "'s boat", boatClass, /* sailID */ null); + return new CompetitorWithBoatImpl(c, b); + } + + public StoreAndLoadCompetitorsTest() throws UnknownHostException, MongoException { super(); } @@ -39,11 +72,14 @@ public class StoreCompetitorTest extends AbstractMongoDBTest { domainFactory = new DomainFactoryImpl((srlid)->null); } - private void dropCompetitorCollection() { + private void dropCompetitorAndBoatsCollection() { DB db = getMongoService().getDB(); DBCollection competitorCollection = db.getCollection(CollectionNames.COMPETITORS.name()); competitorCollection.setWriteConcern(WriteConcern.SAFE); // ensure that the drop() has happened competitorCollection.drop(); + DBCollection boatsCollection = db.getCollection(CollectionNames.BOATS.name()); + boatsCollection.setWriteConcern(WriteConcern.SAFE); // ensure that the drop() has happened + boatsCollection.drop(); } @Test @@ -52,12 +88,15 @@ public class StoreCompetitorTest extends AbstractMongoDBTest { URI flagImageURI2 = new URI("http://www.sapsailing/flagimage2.jpg"); String competitorName1 = "Hasso"; String competitorName2 = "Hasso Plattner"; + String competitorShortName1 = "H."; + String competitorShortName2 = "H.P."; MongoObjectFactory mongoObjectFactory = PersistenceFactory.INSTANCE.getMongoObjectFactory(getMongoService()); DomainObjectFactory domainObjectFactory = PersistenceFactory.INSTANCE.getDomainObjectFactory(getMongoService(), domainFactory); - dropCompetitorCollection(); + dropCompetitorAndBoatsCollection(); - DynamicCompetitor c = AbstractLeaderboardTest.createCompetitor(competitorName1); + DynamicCompetitor c = (DynamicCompetitor) createCompetitor(competitorName1); + c.setShortName(competitorShortName1); c.setFlagImage(flagImageURI1); mongoObjectFactory.storeCompetitor(c); @@ -66,8 +105,10 @@ public class StoreCompetitorTest extends AbstractMongoDBTest { DynamicCompetitor loadedCompetitor = (DynamicCompetitor) allCompetitors.iterator().next(); assertEquals(flagImageURI1, loadedCompetitor.getFlagImage()); assertEquals(competitorName1, loadedCompetitor.getName()); + assertEquals(competitorShortName1, loadedCompetitor.getShortName()); loadedCompetitor.setName(competitorName2); + loadedCompetitor.setShortName(competitorShortName2); loadedCompetitor.setFlagImage(flagImageURI2); mongoObjectFactory.storeCompetitor(loadedCompetitor); @@ -76,15 +117,16 @@ public class StoreCompetitorTest extends AbstractMongoDBTest { loadedCompetitor = (DynamicCompetitor) allCompetitors.iterator().next(); assertEquals(flagImageURI2, loadedCompetitor.getFlagImage()); assertEquals(competitorName2, loadedCompetitor.getName()); + assertEquals(competitorShortName2, loadedCompetitor.getShortName()); } @Test public void testStoreAndUpdateCompetitorWithUUIDAsId() { MongoObjectFactory mongoObjectFactory = PersistenceFactory.INSTANCE.getMongoObjectFactory(getMongoService()); DomainObjectFactory domainObjectFactory = PersistenceFactory.INSTANCE.getDomainObjectFactory(getMongoService(), domainFactory); - dropCompetitorCollection(); + dropCompetitorAndBoatsCollection(); - DynamicCompetitor c = AbstractLeaderboardTest.createCompetitor("Hasso", UUID.randomUUID()); + DynamicCompetitor c = (DynamicCompetitor) createCompetitor("Hasso", UUID.randomUUID()); mongoObjectFactory.storeCompetitor(c); assertEquals(1, Util.size(domainObjectFactory.loadAllCompetitors())); c.setName("Hasso Plattner"); @@ -96,9 +138,9 @@ public class StoreCompetitorTest extends AbstractMongoDBTest { public void testStoreAndRemoveCompetitor() { MongoObjectFactory mongoObjectFactory = PersistenceFactory.INSTANCE.getMongoObjectFactory(getMongoService()); DomainObjectFactory domainObjectFactory = PersistenceFactory.INSTANCE.getDomainObjectFactory(getMongoService(), domainFactory); - dropCompetitorCollection(); + dropCompetitorAndBoatsCollection(); - DynamicCompetitor c = AbstractLeaderboardTest.createCompetitor("Hasso"); + DynamicCompetitor c = (DynamicCompetitor) createCompetitor("Hasso"); mongoObjectFactory.storeCompetitor(c); assertEquals(1, Util.size(domainObjectFactory.loadAllCompetitors())); @@ -110,9 +152,9 @@ public class StoreCompetitorTest extends AbstractMongoDBTest { public void testStoreAndRemoveCompetitorWithUUIDAsId() { MongoObjectFactory mongoObjectFactory = PersistenceFactory.INSTANCE.getMongoObjectFactory(getMongoService()); DomainObjectFactory domainObjectFactory = PersistenceFactory.INSTANCE.getDomainObjectFactory(getMongoService(), domainFactory); - dropCompetitorCollection(); + dropCompetitorAndBoatsCollection(); - DynamicCompetitor c = AbstractLeaderboardTest.createCompetitor("Hasso", UUID.randomUUID()); + DynamicCompetitor c = (DynamicCompetitor) createCompetitor("Hasso", UUID.randomUUID()); mongoObjectFactory.storeCompetitor(c); assertEquals(1, Util.size(domainObjectFactory.loadAllCompetitors())); diff --git a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/StoreAndLoadDeviceIdentifierTest.java b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/StoreAndLoadDeviceIdentifierTest.java index f7b711cd36d..8d854fc6dce 100644 --- a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/StoreAndLoadDeviceIdentifierTest.java +++ b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/StoreAndLoadDeviceIdentifierTest.java @@ -75,7 +75,7 @@ public class StoreAndLoadDeviceIdentifierTest extends AbstractMongoDBTest { private DeviceIdentifier storeAndLoad(DeviceIdentifier device, TypeBasedServiceFinderFactory forStoring, TypeBasedServiceFinderFactory forLoading) { createFactories(forStoring); - Competitor c = DomainFactory.INSTANCE.getOrCreateCompetitor("a", "a", null, null, null, null, null, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null); + Competitor c = DomainFactory.INSTANCE.getOrCreateCompetitor("a", "a", "a", null, null, null, null, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null); regattaLog.add(new RegattaLogDeviceCompetitorMappingEventImpl(now(), now(), author, 0, c, device, now(), now())); createFactories(forLoading); diff --git a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/StoreAndLoadRaceLogEventsTest.java b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/StoreAndLoadRaceLogEventsTest.java index 4eb5c543b50..0698183a78b 100644 --- a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/StoreAndLoadRaceLogEventsTest.java +++ b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/StoreAndLoadRaceLogEventsTest.java @@ -38,6 +38,7 @@ import com.sap.sailing.domain.abstractlog.race.tracking.impl.RaceLogRegisterComp import com.sap.sailing.domain.base.BoatClass; import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.DomainFactory; +import com.sap.sailing.domain.base.impl.BoatClassImpl; import com.sap.sailing.domain.common.racelog.Flags; import com.sap.sailing.domain.common.racelog.RaceLogRaceStatus; import com.sap.sailing.domain.persistence.PersistenceFactory; @@ -51,6 +52,7 @@ import com.sap.sse.common.Util; import com.sap.sse.common.impl.MillisecondsTimePoint; public class StoreAndLoadRaceLogEventsTest extends AbstractMongoDBTest { + private final static BoatClass boatClass = new BoatClassImpl("505", /* typicallyStartsUpwind */ true); protected MongoObjectFactoryImpl mongoFactory = (MongoObjectFactoryImpl) PersistenceFactory.INSTANCE .getMongoObjectFactory(getMongoService(), new MockSmartphoneImeiServiceFinderFactory()); @@ -82,7 +84,7 @@ public class StoreAndLoadRaceLogEventsTest extends AbstractMongoDBTest { assertEquals(expectedEvent.getCreatedAt(), actualEvent.getCreatedAt()); assertEquals(expectedEvent.getLogicalTimePoint(), actualEvent.getLogicalTimePoint()); assertEquals(expectedEvent.getId(), actualEvent.getId()); - assertEquals(expectedEvent.getInvolvedBoats().size(), Util.size(actualEvent.getInvolvedBoats())); + assertEquals(expectedEvent.getInvolvedCompetitors().size(), Util.size(actualEvent.getInvolvedCompetitors())); assertEquals(expectedEvent.getPassId(), actualEvent.getPassId()); } @@ -193,11 +195,12 @@ public class StoreAndLoadRaceLogEventsTest extends AbstractMongoDBTest { } @Test - public void testStoreAndLoadRegisterCompetitorEvent() { + public void testStoreAndLoadRegisterCompetitorAndBoatEvent() { RaceLogRegisterCompetitorEvent expectedEvent = new RaceLogRegisterCompetitorEventImpl(expectedEventTime, expectedEventTime, author, expectedId, expectedPassId, DomainFactory.INSTANCE.getOrCreateCompetitor( - "comp", "comp", null, null, null, null, null, - /* timeOnTimeFactor */null, /* timeOnDistanceAllowancePerNauticalMile */null, null)); + "comp", "comp", "c", null, null, null, null, + /* timeOnTimeFactor */null, /* timeOnDistanceAllowancePerNauticalMile */null, null), + DomainFactory.INSTANCE.getOrCreateBoat("boat", "b", boatClass, null, null)); DBObject dbObject = mongoFactory.storeRaceLogEntry(logIdentifier, expectedEvent); RaceLogRegisterCompetitorEvent actualEvent = loadEvent(dbObject); @@ -216,8 +219,7 @@ public class StoreAndLoadRaceLogEventsTest extends AbstractMongoDBTest { } catch (InterruptedException ie) { fail(ie.toString()); } - RaceLogEvent dbEvent = domainFactory - .loadRaceLogEvent((DBObject) dbObject.get(FieldNames.RACE_LOG_EVENT.name())); + RaceLogEvent dbEvent = domainFactory.loadRaceLogEvent((DBObject) dbObject.get(FieldNames.RACE_LOG_EVENT.name())).getA(); T actualEvent = (T) dbEvent; return actualEvent; } diff --git a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/StoreGPSFixTrackTest.java b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/StoreGPSFixTrackTest.java index d6f950fdf65..380b718f8be 100644 --- a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/StoreGPSFixTrackTest.java +++ b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/StoreGPSFixTrackTest.java @@ -60,7 +60,7 @@ public class StoreGPSFixTrackTest extends AbstractMongoDBTest { public void testStoreAndLoadFixes() throws TransformationException, NoCorrespondingServiceRegisteredException { TypeBasedServiceFinderFactory factory = new MockSmartphoneImeiServiceFinderFactory(); AbstractLogEventAuthor author = new LogEventAuthorImpl("author", 0); - Competitor comp = DomainFactory.INSTANCE.getOrCreateCompetitor("comp", "comp", null, null, null, null, null, + Competitor comp = DomainFactory.INSTANCE.getOrCreateCompetitor("comp", "comp", "c", null, null, null, null, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null); Mark mark = DomainFactory.INSTANCE.getOrCreateMark("mark"); MongoObjectFactory mongoObjectFactory = PersistenceFactory.INSTANCE.getMongoObjectFactory(getMongoService(), factory); diff --git a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestParseAndMigratePassingSideAndControlPointTwoMarks.java b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestParseAndMigratePassingSideAndControlPointTwoMarks.java index 0625a6c83f1..0513d18f935 100755 --- a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestParseAndMigratePassingSideAndControlPointTwoMarks.java +++ b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestParseAndMigratePassingSideAndControlPointTwoMarks.java @@ -77,7 +77,7 @@ public class TestParseAndMigratePassingSideAndControlPointTwoMarks { raceLogEvent.put(FieldNames.RACE_LOG_EVENT_PASS_ID.name(), 1); raceLogEvent.put(FieldNames.RACE_LOG_EVENT_AUTHOR_NAME.name(), "Test Author"); raceLogEvent.put(FieldNames.RACE_LOG_EVENT_AUTHOR_PRIORITY.name(), 1); - RaceLogCourseDesignChangedEvent event = (RaceLogCourseDesignChangedEvent) dof.loadRaceLogEvent(raceLogEvent); + RaceLogCourseDesignChangedEvent event = (RaceLogCourseDesignChangedEvent) dof.loadRaceLogEvent(raceLogEvent).getA(); assertEquals(event.getCourseDesign().getFirstWaypoint().getPassingInstructions(), PassingInstruction.Line); assertTrue(waypoint1.containsField(FieldNames.WAYPOINT_PASSINGINSTRUCTIONS.name()) && !waypoint1.containsField(FieldNames.WAYPOINT_PASSINGSIDE.name())); diff --git a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndLoadingEventsAndRegattas.java b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndLoadingEventsAndRegattas.java index 2361e975ffe..1263510880e 100755 --- a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndLoadingEventsAndRegattas.java +++ b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndLoadingEventsAndRegattas.java @@ -17,8 +17,10 @@ import java.util.Arrays; import java.util.Calendar; import java.util.Collections; import java.util.Iterator; +import java.util.LinkedHashMap; import java.util.List; import java.util.Locale; +import java.util.Map; import java.util.UUID; import java.util.logging.Logger; @@ -27,8 +29,10 @@ import org.junit.Test; import com.mongodb.MongoException; import com.sap.sailing.domain.abstractlog.impl.LogEventAuthorImpl; 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.Course; import com.sap.sailing.domain.base.CourseArea; import com.sap.sailing.domain.base.DomainFactory; @@ -42,6 +46,7 @@ import com.sap.sailing.domain.base.Series; import com.sap.sailing.domain.base.Venue; import com.sap.sailing.domain.base.Waypoint; import com.sap.sailing.domain.base.configuration.impl.RegattaConfigurationImpl; +import com.sap.sailing.domain.base.impl.BoatImpl; import com.sap.sailing.domain.base.impl.CompetitorImpl; import com.sap.sailing.domain.base.impl.CourseImpl; import com.sap.sailing.domain.base.impl.EventImpl; @@ -175,6 +180,11 @@ public class TestStoringAndLoadingEventsAndRegattas extends AbstractMongoDBTest public LeaderboardGroup getLeaderboardGroupByID(UUID leaderboardGroupID) { return leaderboardGroupID.equals(lg1.getId()) ? lg1 : leaderboardGroupID.equals(lg2.getId()) ? lg2 : null; } + + @Override + public LeaderboardGroup resolveLeaderboardGroupByRegattaName(String regattaName) { + return null; + } }); assertNotNull(loadedEvent); assertEquals(eventName, loadedEvent.getName()); @@ -214,7 +224,7 @@ public class TestStoringAndLoadingEventsAndRegattas extends AbstractMongoDBTest final String regattaBaseName = "Kieler Woche"; BoatClass boatClass = DomainFactory.INSTANCE.getOrCreateBoatClass("29erXX", /* typicallyStartsUpwind */ true); Regatta regatta = createRegatta(RegattaImpl.getDefaultName(regattaBaseName, boatClass.getName()), boatClass, - regattaStartDate, regattaEndDate, /* persistent */ true, DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), courseArea, OneDesignRankingMetric::new); + /* canBoatsOfCompetitorsChangePerRace */ true, regattaStartDate, regattaEndDate, /* persistent */ true, DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), courseArea, OneDesignRankingMetric::new); mof.storeRegatta(regatta); DomainObjectFactory dof = PersistenceFactory.INSTANCE.getDomainObjectFactory(getMongoService(), DomainFactory.INSTANCE); @@ -406,24 +416,32 @@ public class TestStoringAndLoadingEventsAndRegattas extends AbstractMongoDBTest final String regattaBaseName = "Kieler Woche"; BoatClass boatClass = DomainFactory.INSTANCE.getOrCreateBoatClass("29erXX", /* typicallyStartsUpwind */ true); Regatta regattaProxy = createRegatta(RegattaImpl.getDefaultName(regattaBaseName, boatClass.getName()), boatClass, - regattaStartDate, regattaEndDate, /* persistent */ true, DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), null, OneDesignRankingMetric::new); + /* canBoatsOfCompetitorsChangePerRace */ false, regattaStartDate, regattaEndDate, /* persistent */ true, DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), null, OneDesignRankingMetric::new); final String regattaName = regattaProxy.getName(); - Regatta regatta = res.createRegatta(regattaName, regattaProxy.getBoatClass().getName(), regattaStartDate, regattaEndDate, + Regatta regatta = res.createRegatta(regattaName, regattaProxy.getBoatClass().getName(), + /* canBoatsOfCompetitorsChangePerRace */ false, regattaStartDate, regattaEndDate, "123", regattaProxy.getSeries(), regattaProxy.isPersistent(), DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), /* defaultCourseAreaId */ null, /*buoyZoneRadiusInHullLengths*/ 2.0, /* useStartTimeInference */ true, /* controlTrackingFromStartAndFinishTimes */ false, OneDesignRankingMetric::new); - Competitor competitor1 = AbstractLeaderboardTest.createCompetitor("Humba1"); - Competitor competitor2 = AbstractLeaderboardTest.createCompetitor("Humba2"); - res.getCompetitorStore().addNewCompetitors(Arrays.asList(competitor1, competitor2)); - regatta.getRegattaLog().add(new RegattaLogRegisterCompetitorEventImpl(MillisecondsTimePoint.now(), new LogEventAuthorImpl("Axel", 0), competitor1)); - regatta.getRegattaLog().add(new RegattaLogRegisterCompetitorEventImpl(MillisecondsTimePoint.now(), new LogEventAuthorImpl("Axel", 0), competitor2)); - assertTrue(Util.contains(regatta.getAllCompetitors(), competitor1)); - assertTrue(Util.contains(regatta.getAllCompetitors(), competitor2)); + CompetitorWithBoat competitorWithBoat1 = AbstractLeaderboardTest.createCompetitorWithBoat("Humba1"); + CompetitorWithBoat competitorWithBoat2 = AbstractLeaderboardTest.createCompetitorWithBoat("Humba2"); + Boat boat1= AbstractLeaderboardTest.createBoat("Humba1 Boot"); + Boat boat2 = AbstractLeaderboardTest.createBoat("Humba2 Boot"); + res.getCompetitorStore().addNewCompetitors(Arrays.asList(competitorWithBoat1, competitorWithBoat2)); + res.getCompetitorStore().addNewBoats(Arrays.asList(boat1, boat2)); + regatta.getRegattaLog().add(new RegattaLogRegisterCompetitorEventImpl(MillisecondsTimePoint.now(), new LogEventAuthorImpl("Axel", 0), competitorWithBoat1)); + regatta.getRegattaLog().add(new RegattaLogRegisterCompetitorEventImpl(MillisecondsTimePoint.now(), new LogEventAuthorImpl("Axel", 0), competitorWithBoat2)); + assertTrue(Util.contains(regatta.getAllCompetitors(), competitorWithBoat1)); + assertTrue(Util.contains(regatta.getAllCompetitors(), competitorWithBoat2)); + Iterable allBoats = regatta.getAllBoats(); + assertTrue(Util.size(allBoats) == 2); + assertTrue(Util.contains(regatta.getAllBoats(), competitorWithBoat1.getBoat())); + assertTrue(Util.contains(regatta.getAllBoats(), competitorWithBoat2.getBoat())); addRaceColumns(numberOfQualifyingRaces, numberOfFinalRaces, regatta); RegattaLeaderboard fullLeaderboard = res.addRegattaLeaderboard(regatta.getRegattaIdentifier(), null, new int[] { 3, 5 }); // use the set-up and add a regatta leaderboard with eliminations that wraps the regatta leaderboard RegattaLeaderboardWithEliminations withEliminations = res.addRegattaLeaderboardWithEliminations("U16", /* leaderboardDisplayName */ "Display Name", fullLeaderboard); - res.apply(new UpdateEliminatedCompetitorsInLeaderboard(withEliminations.getName(), Collections.singleton(competitor1))); + res.apply(new UpdateEliminatedCompetitorsInLeaderboard(withEliminations.getName(), Collections.singleton(competitorWithBoat1))); DomainObjectFactory dof = PersistenceFactory.INSTANCE.getDomainObjectFactory(getMongoService(), DomainFactory.INSTANCE); Regatta loadedRegatta = dof.loadRegatta(regatta.getName(), /* trackedRegattaRegistry */ null); @@ -445,8 +463,8 @@ public class TestStoringAndLoadingEventsAndRegattas extends AbstractMongoDBTest assertTrue(loadedLeaderboardWithEliminations instanceof RegattaLeaderboardWithEliminations); assertEquals("Display Name", loadedLeaderboardWithEliminations.getDisplayName()); assertEquals(withEliminations.getAllCompetitors(), loadedLeaderboardWithEliminations.getAllCompetitors()); - assertTrue(((RegattaLeaderboardWithEliminations) loadedLeaderboardWithEliminations).isEliminated(competitor1)); - assertFalse(((RegattaLeaderboardWithEliminations) loadedLeaderboardWithEliminations).isEliminated(competitor2)); + assertTrue(((RegattaLeaderboardWithEliminations) loadedLeaderboardWithEliminations).isEliminated(competitorWithBoat1)); + assertFalse(((RegattaLeaderboardWithEliminations) loadedLeaderboardWithEliminations).isEliminated(competitorWithBoat2)); } @Test @@ -454,7 +472,7 @@ public class TestStoringAndLoadingEventsAndRegattas extends AbstractMongoDBTest // for some reason the dropping of collections doesn't work reliably on Linux... explicitly drop those collections that we depend on getMongoService().getDB().getCollection(CollectionNames.LEADERBOARDS.name()).drop(); getMongoService().getDB().getCollection(CollectionNames.REGATTAS.name()).drop(); - Competitor hasso = AbstractLeaderboardTest.createCompetitor("Dr. Hasso Plattner"); + CompetitorWithBoat hasso = AbstractLeaderboardTest.createCompetitorWithBoat("Dr. Hasso Plattner"); BoatClass boatClass = DomainFactory.INSTANCE.getOrCreateBoatClass("29erXX", /* typicallyStartsUpwind */ true); final DynamicTrackedRegatta[] trackedRegatta = new DynamicTrackedRegatta[1]; final DynamicTrackedRace q2YellowTrackedRace = new MockedTrackedRaceWithFixedRank(hasso, /* rank */ 1, /* started */ false, boatClass) { @@ -473,8 +491,10 @@ public class TestStoringAndLoadingEventsAndRegattas extends AbstractMongoDBTest final int numberOfFinalRaces = 7; final String regattaBaseName = "Kieler Woche"; - Regatta regattaProxy = createRegatta(RegattaImpl.getDefaultName(regattaBaseName, boatClass.getName()), boatClass, regattaStartDate, regattaEndDate, /* persistent */ true, DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), null, OneDesignRankingMetric::new); - Regatta regatta = res.createRegatta(regattaProxy.getName(), regattaProxy.getBoatClass().getName(), regattaProxy.getStartDate(), regattaProxy.getEndDate(), + Regatta regattaProxy = createRegatta(RegattaImpl.getDefaultName(regattaBaseName, boatClass.getName()), boatClass, + /* canBoatsOfCompetitorsChangePerRace */ true, regattaStartDate, regattaEndDate, /* persistent */ true, DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), null, OneDesignRankingMetric::new); + Regatta regatta = res.createRegatta(regattaProxy.getName(), regattaProxy.getBoatClass().getName(), + regattaProxy.canBoatsOfCompetitorsChangePerRace(), regattaProxy.getStartDate(), regattaProxy.getEndDate(), "123", regattaProxy.getSeries(), regattaProxy.isPersistent(), DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), null, /*buoyZoneRadiusInHullLengths*/2.0, /* useStartTimeInference */ true, /* controlTrackingFromStartAndFinishTimes */ false, OneDesignRankingMetric::new); trackedRegatta[0] = new DynamicTrackedRegattaImpl(regatta); addRaceColumns(numberOfQualifyingRaces, numberOfFinalRaces, regatta); @@ -690,6 +710,42 @@ public class TestStoringAndLoadingEventsAndRegattas extends AbstractMongoDBTest assertFalse(loadedRegatta.getSeriesByName("Medal").isFleetsCanRunInParallel()); } + @Test + public void testLoadStoreRegattaWithCompetitorsWhichCanChangeBoats() { + final boolean canBoatsOfCompetitorsChangePerRace = true; + final String regattaBaseName = "Kieler Woche"; + BoatClass boatClass = DomainFactory.INSTANCE.getOrCreateBoatClass("29erXX", /* typicallyStartsUpwind */true); + final String regattaName = RegattaImpl.getDefaultName(regattaBaseName, boatClass.getName()); + Regatta regatta = createRegatta(regattaName, boatClass, canBoatsOfCompetitorsChangePerRace, + null, null, /* persistent */false, + DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), null, OneDesignRankingMetric::new); + MongoObjectFactory mof = PersistenceFactory.INSTANCE.getMongoObjectFactory(getMongoService()); + mof.storeRegatta(regatta); + + DomainObjectFactory dof = PersistenceFactory.INSTANCE.getDomainObjectFactory(getMongoService(), DomainFactory.INSTANCE); + Regatta loadedRegatta = dof.loadRegatta(regatta.getName(), /* trackedRegattaRegistry */ null); + assertEquals(regattaName, loadedRegatta.getName()); + assertEquals(loadedRegatta.canBoatsOfCompetitorsChangePerRace(), canBoatsOfCompetitorsChangePerRace); + } + + @Test + public void testLoadStoreRegattaWithCompetitorsWhichCanNotChangeBoats() { + final boolean canBoatsOfCompetitorsChangePerRace = false; + final String regattaBaseName = "Kieler Woche"; + BoatClass boatClass = DomainFactory.INSTANCE.getOrCreateBoatClass("29erXX", /* typicallyStartsUpwind */true); + final String regattaName = RegattaImpl.getDefaultName(regattaBaseName, boatClass.getName()); + Regatta regatta = createRegatta(regattaName, boatClass, canBoatsOfCompetitorsChangePerRace, + null, null, /* persistent */false, + DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), null, OneDesignRankingMetric::new); + MongoObjectFactory mof = PersistenceFactory.INSTANCE.getMongoObjectFactory(getMongoService()); + mof.storeRegatta(regatta); + + DomainObjectFactory dof = PersistenceFactory.INSTANCE.getDomainObjectFactory(getMongoService(), DomainFactory.INSTANCE); + Regatta loadedRegatta = dof.loadRegatta(regatta.getName(), /* trackedRegattaRegistry */ null); + assertEquals(regattaName, loadedRegatta.getName()); + assertEquals(loadedRegatta.canBoatsOfCompetitorsChangePerRace(), canBoatsOfCompetitorsChangePerRace); + } + @Test public void testLoadStoreSimpleRegattaWithHighPointScoringScheme() { final int numberOfQualifyingRaces = 5; @@ -752,7 +808,8 @@ public class TestStoringAndLoadingEventsAndRegattas extends AbstractMongoDBTest final String regattaBaseName = "Kieler Woche"; BoatClass boatClass = DomainFactory.INSTANCE.getOrCreateBoatClass("29erXX", /* typicallyStartsUpwind */ true); final String regattaName = RegattaImpl.getDefaultName(regattaBaseName, boatClass.getName()); - Regatta regatta = createRegatta(regattaName, boatClass, regattaStartDate, regattaEndDate, + Regatta regatta = createRegatta(regattaName, boatClass, + /* canBoatsOfCompetitorsChangePerRace */ true, regattaStartDate, regattaEndDate, /* persistent */ false, DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), null, OneDesignRankingMetric::new); MongoObjectFactory mof = PersistenceFactory.INSTANCE.getMongoObjectFactory(getMongoService()); mof.storeRegatta(regatta); @@ -808,8 +865,8 @@ public class TestStoringAndLoadingEventsAndRegattas extends AbstractMongoDBTest private Regatta createRegattaAndAddRaceColumns(final int numberOfQualifyingRaces, final int numberOfFinalRaces, final String regattaName, BoatClass boatClass, TimePoint startDate, TimePoint endDate, boolean persistent, ScoringScheme scoringScheme, RankingMetricConstructor rankingMetricConstructor) { - Regatta regatta = createRegatta(regattaName, boatClass, startDate, endDate, persistent, scoringScheme, null, - rankingMetricConstructor); + Regatta regatta = createRegatta(regattaName, boatClass, /* canBoatsOfCompetitorsChangePerRace */ true, + startDate, endDate, persistent, scoringScheme, null, rankingMetricConstructor); addRaceColumns(numberOfQualifyingRaces, numberOfFinalRaces, regatta); return regatta; } @@ -830,7 +887,7 @@ public class TestStoringAndLoadingEventsAndRegattas extends AbstractMongoDBTest addRaceColumnsToSeries(medalRaceColumnNames, regatta.getSeriesByName("Medal")); } - private Regatta createRegatta(final String regattaName, BoatClass boatClass, TimePoint startDate, + private Regatta createRegatta(final String regattaName, BoatClass boatClass, boolean canBoatsOfCompetitorsChangePerRace, TimePoint startDate, TimePoint endDate, boolean persistent, ScoringScheme scoringScheme, CourseArea courseArea, RankingMetricConstructor rankingMetricConstructor) { List emptyRaceColumnNames = Collections.emptyList(); @@ -856,7 +913,8 @@ public class TestStoringAndLoadingEventsAndRegattas extends AbstractMongoDBTest medalFleets.add(new FleetImpl("Medal")); Series medalSeries = new SeriesImpl("Medal", /* isMedal */ true, /* isFleetsCanRunInParallel */ true, medalFleets, emptyRaceColumnNames, /* trackedRegattaRegistry */ null); series.add(medalSeries); - Regatta regatta = new RegattaImpl(regattaName, boatClass, startDate, endDate, series, persistent, scoringScheme, "123", courseArea, rankingMetricConstructor); + Regatta regatta = new RegattaImpl(regattaName, boatClass, canBoatsOfCompetitorsChangePerRace, startDate, endDate, + series, persistent, scoringScheme, "123", courseArea, rankingMetricConstructor); return regatta; } @@ -870,14 +928,17 @@ public class TestStoringAndLoadingEventsAndRegattas extends AbstractMongoDBTest public void testRegattaRaceAssociationStore() throws Exception { BoatClass boatClass = DomainFactory.INSTANCE.getOrCreateBoatClass("112er", /* typicallyStartsUpwind */ true); Regatta regatta = createRegatta(RegattaImpl.getDefaultName("Cologne Masters", boatClass.getName()), boatClass, - regattaStartDate, regattaEndDate, /* persistent */ true, DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), null, OneDesignRankingMetric::new); + /* canBoatsOfCompetitorsChangePerRace */ true, regattaStartDate, regattaEndDate, /* persistent */ true, + DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), null, OneDesignRankingMetric::new); - List competitors = new ArrayList(); - competitors.add(new CompetitorImpl("Axel", "Axel Uhl", Color.RED, null, null, null, null, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null)); + Map competitorsAndBoats = new LinkedHashMap<>(); + Competitor c = new CompetitorImpl("Axel", "Axel Uhl", "KYC", Color.RED, null, null, null, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null); + Boat b = new BoatImpl("123", "boat", boatClass, "GER 124"); + competitorsAndBoats.put(c, b); Iterable waypoints = Collections.emptyList(); Course course = new CourseImpl("Course", waypoints); - RaceDefinition racedef = new RaceDefinitionImpl("M1", course, boatClass, competitors); + RaceDefinition racedef = new RaceDefinitionImpl("M1", course, boatClass, competitorsAndBoats); regatta.addRace(racedef); RacingEventServiceImpl evs = new RacingEventServiceImpl(PersistenceFactory.INSTANCE.getDomainObjectFactory(getMongoService(), DomainFactory.INSTANCE), PersistenceFactory.INSTANCE diff --git a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndRetrievingLeaderboardGroups.java b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndRetrievingLeaderboardGroups.java index 312fc38dfb4..b193fc613c8 100755 --- a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndRetrievingLeaderboardGroups.java +++ b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndRetrievingLeaderboardGroups.java @@ -19,12 +19,15 @@ import org.junit.Test; import com.mongodb.MongoException; 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.impl.BoatClassImpl; import com.sap.sailing.domain.base.impl.BoatImpl; import com.sap.sailing.domain.base.impl.CompetitorImpl; +import com.sap.sailing.domain.base.impl.CompetitorWithBoatImpl; +import com.sap.sailing.domain.base.impl.DynamicBoat; import com.sap.sailing.domain.base.impl.NationalityImpl; import com.sap.sailing.domain.base.impl.PersonImpl; import com.sap.sailing.domain.base.impl.TeamImpl; @@ -69,7 +72,7 @@ public class TestStoringAndRetrievingLeaderboardGroups extends AbstractMongoDBTe @Before public void setUp() { - DomainFactory.INSTANCE.getCompetitorStore().clear(); + DomainFactory.INSTANCE.getCompetitorStore().clearCompetitors(); mongoObjectFactory = new MongoObjectFactoryImpl(db); domainObjectFactory = new DomainObjectFactoryImpl(db, DomainFactory.INSTANCE); } @@ -113,18 +116,23 @@ public class TestStoringAndRetrievingLeaderboardGroups extends AbstractMongoDBTe @Test public void testStoringAndRetrievingLeaderboardGroupWithOverallLeaderboardWithScoreCorrection() throws NoWindException { RacingEventService racingEventService = new RacingEventServiceImpl(); - Competitor wolfgang = new CompetitorImpl(123, "$$$Dr. Wolfgang+Hunger$$$", Color.RED, null, null, new TeamImpl("STG", Collections.singleton( + Competitor wolfgangWithoutBoat = new CompetitorImpl(123, "$$$Dr. Wolfgang+Hunger$$$", "KYC", Color.RED, null, null, new TeamImpl("STG", Collections.singleton( new PersonImpl("$$$Dr. Wolfgang+Hunger$$$", new NationalityImpl("GER"), /* dateOfBirth */ null, "This is famous Dr. Wolfgang Hunger")), new PersonImpl("Rigo van Maas", new NationalityImpl("NED"), - /* dateOfBirth */ null, "This is Rigo, the coach")), new BoatImpl("Dr. Wolfgang Hunger's boat", new BoatClassImpl("505", /* typicallyStartsUpwind */ true), null), /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null); - Competitor hasso = new CompetitorImpl(234, "Hasso Plattner", Color.RED, null, null, + /* dateOfBirth */ null, "This is Rigo, the coach")), /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null); + DynamicBoat wolfgangsBoat = new BoatImpl("123", "Dr. Wolfgang Hunger's boat", new BoatClassImpl("505", /* typicallyStartsUpwind */ true), null); + CompetitorWithBoat wolfgang = new CompetitorWithBoatImpl(wolfgangWithoutBoat,wolfgangsBoat); + Competitor hassoWithoutBoat = new CompetitorImpl(234, "Hasso Plattner", "KYC", Color.RED, null, null, new TeamImpl("STG", Collections.singleton( new PersonImpl("Hasso Plattner", new NationalityImpl("GER"), /* dateOfBirth */ null, "This is famous Dr. Hasso Plattner")), new PersonImpl("Lutz Patrunky", new NationalityImpl("GER"), - /* dateOfBirth */ null, "This is Patty, the coach")), new BoatImpl("Dr. Hasso Plattner's boat", new BoatClassImpl("505", /* typicallyStartsUpwind */ true), null), /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null); + /* dateOfBirth */ null, "This is Patty, the coach")), /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null); + DynamicBoat hassosBoat = new BoatImpl("456", "Dr. Hasso Plattner's boat", new BoatClassImpl("505", /* typicallyStartsUpwind */ true), null); + CompetitorWithBoat hasso = new CompetitorWithBoatImpl(hassoWithoutBoat,hassosBoat); final String raceColumnName1 = "My First Race 1"; - MockedTrackedRaceWithFixedRankAndManyCompetitors raceWithTwoCompetitors = new MockedTrackedRaceWithFixedRankAndManyCompetitors(wolfgang, /* rank */ 1, /* started */ true); - raceWithTwoCompetitors.addCompetitor(hasso); + MockedTrackedRaceWithFixedRankAndManyCompetitors raceWithTwoCompetitors = new MockedTrackedRaceWithFixedRankAndManyCompetitors( + wolfgang, /* rank */ 1, /* started */ true); + raceWithTwoCompetitors.addCompetitorWithBoat(hasso); final String[] leaderboardNames = {"Leaderboard 0", "Leaderboard 1", "Leaderboard 2", "Leaderboard 3"}; final int[] discardIndexResultsStartingWithHowManyRaces = new int[] { 5, 8 }; @@ -169,18 +177,23 @@ public class TestStoringAndRetrievingLeaderboardGroups extends AbstractMongoDBTe @Test public void testStoringAndRetrievingLeaderboardGroupWithSuppressedCompetitorsInOverallLeaderboard() { - Competitor wolfgang = new CompetitorImpl(123, "$$$Dr. Wolfgang+Hunger$$$", Color.RED, null, null, new TeamImpl("STG", Collections.singleton( + Competitor wolfgangWithoutBoat = new CompetitorImpl(123, "$$$Dr. Wolfgang+Hunger$$$", "KYC", Color.RED, null, null, new TeamImpl("STG", Collections.singleton( new PersonImpl("$$$Dr. Wolfgang+Hunger$$$", new NationalityImpl("GER"), /* dateOfBirth */ null, "This is famous Dr. Wolfgang Hunger")), new PersonImpl("Rigo van Maas", new NationalityImpl("NED"), - /* dateOfBirth */ null, "This is Rigo, the coach")), new BoatImpl("Dr. Wolfgang Hunger's boat", new BoatClassImpl("505", /* typicallyStartsUpwind */ true), null), /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null); - Competitor hasso = new CompetitorImpl(234, "Hasso Plattner", Color.RED, null, null, + /* dateOfBirth */ null, "This is Rigo, the coach")), /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null); + DynamicBoat wolfgangsBoat = new BoatImpl("123", "Dr. Wolfgang Hunger's boat", new BoatClassImpl("505", /* typicallyStartsUpwind */ true), null); + CompetitorWithBoat wolfgang = new CompetitorWithBoatImpl(wolfgangWithoutBoat,wolfgangsBoat); + Competitor hassoWithoutBaot = new CompetitorImpl(234, "Hasso Plattner", "KYC", Color.RED, null, null, new TeamImpl("STG", Collections.singleton( new PersonImpl("Hasso Plattner", new NationalityImpl("GER"), /* dateOfBirth */ null, "This is famous Dr. Hasso Plattner")), new PersonImpl("Lutz Patrunky", new NationalityImpl("GER"), - /* dateOfBirth */ null, "This is Patty, the coach")), new BoatImpl("Dr. Hasso Plattner's boat", new BoatClassImpl("505", /* typicallyStartsUpwind */ true), null), /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null); + /* dateOfBirth */ null, "This is Patty, the coach")), /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null); + DynamicBoat hassosBoat = new BoatImpl("456", "Dr. Hasso Plattner's boat", new BoatClassImpl("505", /* typicallyStartsUpwind */ true), null); + CompetitorWithBoat hasso = new CompetitorWithBoatImpl(hassoWithoutBaot,hassosBoat); final String raceColumnName1 = "My First Race 1"; - MockedTrackedRaceWithFixedRankAndManyCompetitors raceWithTwoCompetitors = new MockedTrackedRaceWithFixedRankAndManyCompetitors(wolfgang, /* rank */ 1, /* started */ true); - raceWithTwoCompetitors.addCompetitor(hasso); + MockedTrackedRaceWithFixedRankAndManyCompetitors raceWithTwoCompetitors = new MockedTrackedRaceWithFixedRankAndManyCompetitors( + wolfgang, /* rank */ 1, /* started */ true); + raceWithTwoCompetitors.addCompetitorWithBoat(hasso); final String[] leaderboardNames = {"Leaderboard 0", "Leaderboard 1", "Leaderboard 2", "Leaderboard 3"}; final int[] discardIndexResultsStartingWithHowManyRaces = new int[] { 5, 8 }; @@ -225,18 +238,23 @@ public class TestStoringAndRetrievingLeaderboardGroups extends AbstractMongoDBTe @Test public void testStoringAndRetrievingLeaderboardGroupWithTwoSuppressedCompetitorsInOverallLeaderboard() { - Competitor wolfgang = new CompetitorImpl(123, "$$$Dr. Wolfgang+Hunger$$$", Color.RED, null, null, new TeamImpl("STG", Collections.singleton( + Competitor wolfgangWithoutBoat = new CompetitorImpl(123, "$$$Dr. Wolfgang+Hunger$$$", "KYC", Color.RED, null, null, new TeamImpl("STG", Collections.singleton( new PersonImpl("$$$Dr. Wolfgang+Hunger$$$", new NationalityImpl("GER"), /* dateOfBirth */ null, "This is famous Dr. Wolfgang Hunger")), new PersonImpl("Rigo van Maas", new NationalityImpl("NED"), - /* dateOfBirth */ null, "This is Rigo, the coach")), new BoatImpl("Dr. Wolfgang Hunger's boat", new BoatClassImpl("505", /* typicallyStartsUpwind */ true), null), /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null); - Competitor hasso = new CompetitorImpl(234, "Hasso Plattner", Color.RED, null,null, + /* dateOfBirth */ null, "This is Rigo, the coach")), /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null); + DynamicBoat wolfgangsBoat = new BoatImpl("123", "Dr. Wolfgang Hunger's boat", new BoatClassImpl("505", /* typicallyStartsUpwind */ true), null); + CompetitorWithBoat wolfgang = new CompetitorWithBoatImpl(wolfgangWithoutBoat,wolfgangsBoat); + Competitor hassoWithoutBaot = new CompetitorImpl(234, "Hasso Plattner", "KYC", Color.RED, null,null, new TeamImpl("STG", Collections.singleton( new PersonImpl("Hasso Plattner", new NationalityImpl("GER"), /* dateOfBirth */ null, "This is famous Dr. Hasso Plattner")), new PersonImpl("Lutz Patrunky", new NationalityImpl("GER"), - /* dateOfBirth */ null, "This is Patty, the coach")), new BoatImpl("Dr. Hasso Plattner's boat", new BoatClassImpl("505", /* typicallyStartsUpwind */ true), null), /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null); + /* dateOfBirth */ null, "This is Patty, the coach")), /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null); + DynamicBoat hassosBoat = new BoatImpl("456", "Dr. Hasso Plattner's boat", new BoatClassImpl("505", /* typicallyStartsUpwind */ true), null); + CompetitorWithBoat hasso = new CompetitorWithBoatImpl(hassoWithoutBaot,hassosBoat); final String raceColumnName1 = "My First Race 1"; - MockedTrackedRaceWithFixedRankAndManyCompetitors raceWithTwoCompetitors = new MockedTrackedRaceWithFixedRankAndManyCompetitors(wolfgang, /* rank */ 1, /* started */ true); - raceWithTwoCompetitors.addCompetitor(hasso); + MockedTrackedRaceWithFixedRankAndManyCompetitors raceWithTwoCompetitors = new MockedTrackedRaceWithFixedRankAndManyCompetitors( + wolfgang, /* rank */ 1, /* started */ true); + raceWithTwoCompetitors.addCompetitorWithBoat(hasso); final String[] leaderboardNames = {"Leaderboard 0", "Leaderboard 1", "Leaderboard 2", "Leaderboard 3"}; final int[] discardIndexResultsStartingWithHowManyRaces = new int[] { 5, 8 }; diff --git a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndRetrievingLeaderboards.java b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndRetrievingLeaderboards.java index 4c5cdc38c87..b57782429e1 100755 --- a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndRetrievingLeaderboards.java +++ b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndRetrievingLeaderboards.java @@ -14,12 +14,15 @@ import org.junit.Test; import com.mongodb.MongoException; 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.RaceColumn; import com.sap.sailing.domain.base.impl.BoatClassImpl; import com.sap.sailing.domain.base.impl.BoatImpl; import com.sap.sailing.domain.base.impl.CompetitorImpl; +import com.sap.sailing.domain.base.impl.CompetitorWithBoatImpl; import com.sap.sailing.domain.base.impl.DomainFactoryImpl; +import com.sap.sailing.domain.base.impl.DynamicBoat; import com.sap.sailing.domain.base.impl.NationalityImpl; import com.sap.sailing.domain.base.impl.PersonImpl; import com.sap.sailing.domain.base.impl.TeamImpl; @@ -49,7 +52,7 @@ public class TestStoringAndRetrievingLeaderboards extends AbstractMongoDBTest { @Before public void clearCompetitorStore() { - DomainFactory.INSTANCE.getCompetitorStore().clear(); + DomainFactory.INSTANCE.getCompetitorStore().clearCompetitors(); } @Test @@ -76,25 +79,27 @@ public class TestStoringAndRetrievingLeaderboards extends AbstractMongoDBTest { final int[] discardIndexResultsStartingWithHowManyRaces = new int[] { 5, 8 }; FlexibleLeaderboardImpl leaderboard = new FlexibleLeaderboardImpl(leaderboardName, new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null); - Competitor wolfgang = createCompetitor(); - Competitor hasso = new CompetitorImpl(234, "Hasso Plattner", Color.RED, null, null, + CompetitorWithBoat wolfgangWithBoat = createCompetitorWithBoat(); + Competitor hasso = new CompetitorImpl(234, "Hasso Plattner", "KYC", Color.RED, null, null, new TeamImpl("STG", Collections.singleton( new PersonImpl("Hasso Plattner", new NationalityImpl("GER"), /* dateOfBirth */ null, "This is famous Dr. Hasso Plattner")), new PersonImpl("Lutz Patrunky", new NationalityImpl("GER"), - /* dateOfBirth */ null, "This is Patty, the coach")), new BoatImpl("Dr. Hasso Plattner's boat", new BoatClassImpl("505", /* typicallyStartsUpwind */ true), null), /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null); + /* dateOfBirth */ null, "This is Patty, the coach")), /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null); + DynamicBoat hassosBoat = new BoatImpl("123", "Dr. Hasso Plattner's boat", new BoatClassImpl("505", /* typicallyStartsUpwind */ true), null); + CompetitorWithBoat hassoWithBoat = new CompetitorWithBoatImpl(hasso, hassosBoat); final String raceColumnName1 = "My First Race 1"; - MockedTrackedRaceWithFixedRankAndManyCompetitors raceWithTwoCompetitors = new MockedTrackedRaceWithFixedRankAndManyCompetitors(wolfgang, /* rank */ 1, /* started */ true); - raceWithTwoCompetitors.addCompetitor(hasso); + MockedTrackedRaceWithFixedRankAndManyCompetitors raceWithTwoCompetitors = new MockedTrackedRaceWithFixedRankAndManyCompetitors(wolfgangWithBoat, /* rank */ 1, /* started */ true); + raceWithTwoCompetitors.addCompetitorWithBoat(hassoWithBoat); leaderboard.addRace(raceWithTwoCompetitors, raceColumnName1, /* medalRace */ false); - leaderboard.setSuppressed(wolfgang, true); - assertTrue(Util.contains(leaderboard.getSuppressedCompetitors(), wolfgang)); + leaderboard.setSuppressed(wolfgangWithBoat, true); + assertTrue(Util.contains(leaderboard.getSuppressedCompetitors(), wolfgangWithBoat)); assertFalse(Util.contains(leaderboard.getSuppressedCompetitors(), hasso)); new MongoObjectFactoryImpl(db).storeLeaderboard(leaderboard); Leaderboard loadedLeaderboard = new DomainObjectFactoryImpl(db, DomainFactory.INSTANCE).loadLeaderboard(leaderboardName, /* regattaRegistry */ null, /* leaderboardRegistry */ null); - MockedTrackedRaceWithFixedRankAndManyCompetitors raceWithTwoCompetitors2 = new MockedTrackedRaceWithFixedRankAndManyCompetitors(wolfgang, /* rank */ 1, /* started */ true); - raceWithTwoCompetitors2.addCompetitor(hasso); + MockedTrackedRaceWithFixedRankAndManyCompetitors raceWithTwoCompetitors2 = new MockedTrackedRaceWithFixedRankAndManyCompetitors(wolfgangWithBoat, /* rank */ 1, /* started */ true); + raceWithTwoCompetitors2.addCompetitorWithBoat(hassoWithBoat); loadedLeaderboard.getRaceColumnByName(raceColumnName1).setTrackedRace(loadedLeaderboard.getFleet(null), raceWithTwoCompetitors2); - assertTrue(Util.contains(loadedLeaderboard.getSuppressedCompetitors(), wolfgang)); + assertTrue(Util.contains(loadedLeaderboard.getSuppressedCompetitors(), wolfgangWithBoat)); assertFalse(Util.contains(loadedLeaderboard.getSuppressedCompetitors(), hasso)); } @@ -119,18 +124,18 @@ public class TestStoringAndRetrievingLeaderboards extends AbstractMongoDBTest { final int[] discardIndexResultsStartingWithHowManyRaces = new int[] { 5, 8 }; FlexibleLeaderboardImpl leaderboard = new FlexibleLeaderboardImpl(leaderboardName, new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null); - Competitor competitor = createCompetitor(); - TrackedRace raceWithOneCompetitor1 = new MockedTrackedRaceWithFixedRank(competitor, /* rank */ 1, /* started */ true); + CompetitorWithBoat competitorWithBoat = createCompetitorWithBoat(); + TrackedRace raceWithOneCompetitor1 = new MockedTrackedRaceWithFixedRank(competitorWithBoat, /* rank */ 1, /* started */ true); final String raceColumnName1 = "My First Race 1"; leaderboard.addRace(raceWithOneCompetitor1, raceColumnName1, /* medalRace */ false); final String displayName = "$$$ ... The Renamed Competitor ... $$$"; - leaderboard.setDisplayName(competitor, displayName); + leaderboard.setDisplayName(competitorWithBoat, displayName); new MongoObjectFactoryImpl(db).storeLeaderboard(leaderboard); FlexibleLeaderboard loadedLeaderboard = (FlexibleLeaderboard) new DomainObjectFactoryImpl(db, DomainFactory.INSTANCE).loadLeaderboard( leaderboardName, /* regattaRegistry */null, /* leaderboardRegistry */ null); // attach tracked race to leaderboard to ensure that competitor object is assigned properly loadedLeaderboard.addRace(raceWithOneCompetitor1, raceColumnName1, /* medalRace, ignored */ false); - Competitor loadedCompetitor = loadedLeaderboard.getCompetitorByName(competitor.getName()); + Competitor loadedCompetitor = loadedLeaderboard.getCompetitorByName(competitorWithBoat.getName()); assertEquals(displayName, loadedLeaderboard.getDisplayName(loadedCompetitor)); } @@ -167,10 +172,10 @@ public class TestStoringAndRetrievingLeaderboards extends AbstractMongoDBTest { final int[] discardIndexResultsStartingWithHowManyRaces = new int[] { 5, 8 }; FlexibleLeaderboardImpl leaderboard = new FlexibleLeaderboardImpl(leaderboardName, new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null); - Competitor competitor = createCompetitor(); - TrackedRace raceWithOneCompetitor = new MockedTrackedRaceWithFixedRank(competitor, /* rank */ 1, /* started */ true); + CompetitorWithBoat competitorWithBoat = createCompetitorWithBoat(); + TrackedRace raceWithOneCompetitor = new MockedTrackedRaceWithFixedRank(competitorWithBoat, /* rank */ 1, /* started */ true); leaderboard.addRace(raceWithOneCompetitor, raceColumnName, /* medalRace */ false); - leaderboard.setCarriedPoints(competitor, carriedPointsForWolfgangHunger); + leaderboard.setCarriedPoints(competitorWithBoat, carriedPointsForWolfgangHunger); new MongoObjectFactoryImpl(db).storeLeaderboard(leaderboard); FlexibleLeaderboard loadedLeaderboard = (FlexibleLeaderboard) new DomainObjectFactoryImpl(db, DomainFactory.INSTANCE).loadLeaderboard(leaderboardName, /* regattaRegistry */ null, /* leaderboardRegistry */ null); // attach tracked race to leaderboard to ensure that competitor object is assigned properly @@ -179,8 +184,8 @@ public class TestStoringAndRetrievingLeaderboards extends AbstractMongoDBTest { assertTrue(Arrays.equals(discardIndexResultsStartingWithHowManyRaces, ((ThresholdBasedResultDiscardingRule) loadedLeaderboard.getResultDiscardingRule()).getDiscardIndexResultsStartingWithHowManyRaces())); assertEquals(1, Util.size(loadedLeaderboard.getCompetitors())); - assertEquals(competitor, loadedLeaderboard.getCompetitors().iterator().next()); - assertEquals(carriedPointsForWolfgangHunger, loadedLeaderboard.getCarriedPoints(competitor), 0.000000001); + assertEquals(competitorWithBoat, loadedLeaderboard.getCompetitors().iterator().next()); + assertEquals(carriedPointsForWolfgangHunger, loadedLeaderboard.getCarriedPoints(competitorWithBoat), 0.000000001); } @Test @@ -192,21 +197,21 @@ public class TestStoringAndRetrievingLeaderboards extends AbstractMongoDBTest { final int[] discardIndexResultsStartingWithHowManyRaces = new int[] { 5, 8 }; FlexibleLeaderboardImpl leaderboard = new FlexibleLeaderboardImpl(leaderboardName, new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null); - Competitor competitor = createCompetitor(); - TrackedRace raceWithOneCompetitor1 = new MockedTrackedRaceWithFixedRank(competitor, /* rank */ 1, /* started */ true); - TrackedRace raceWithOneCompetitor2 = new MockedTrackedRaceWithFixedRank(competitor, /* rank */ 2, /* started */ true); + CompetitorWithBoat competitorWithBoat = createCompetitorWithBoat(); + TrackedRace raceWithOneCompetitor1 = new MockedTrackedRaceWithFixedRank(competitorWithBoat, /* rank */ 1, /* started */ true); + TrackedRace raceWithOneCompetitor2 = new MockedTrackedRaceWithFixedRank(competitorWithBoat, /* rank */ 2, /* started */ true); leaderboard.addRace(raceWithOneCompetitor1, raceColumnName1, /* medalRace */ false); leaderboard.addRace(raceWithOneCompetitor2, raceColumnName2, /* medalRace */ true); - leaderboard.getScoreCorrection().correctScore(competitor, leaderboard.getRaceColumnByName(raceColumnName1), correctedPoints); + leaderboard.getScoreCorrection().correctScore(competitorWithBoat, leaderboard.getRaceColumnByName(raceColumnName1), correctedPoints); new MongoObjectFactoryImpl(db).storeLeaderboard(leaderboard); FlexibleLeaderboard loadedLeaderboard = (FlexibleLeaderboard) new DomainObjectFactoryImpl(db, DomainFactory.INSTANCE).loadLeaderboard(leaderboardName, /* regattaRegistry */ null, /* leaderboardRegistry */ null); // attach tracked race to leaderboard to ensure that competitor object is assigned properly RaceColumn loadedColumn1 = loadedLeaderboard.addRace(raceWithOneCompetitor1, raceColumnName1, /* medalRace, ignored */ false); RaceColumn loadedColumn2 = loadedLeaderboard.addRace(raceWithOneCompetitor2, raceColumnName2, /* medalRace, ignored */ false); assertEquals(leaderboardName, loadedLeaderboard.getName()); - assertTrue(loadedLeaderboard.getScoreCorrection().isScoreCorrected(competitor, loadedColumn1, MillisecondsTimePoint.now())); - assertEquals(correctedPoints, (double) loadedLeaderboard.getScoreCorrection().getExplicitScoreCorrection(competitor, loadedColumn1), 0.00000001); - assertFalse(loadedLeaderboard.getScoreCorrection().isScoreCorrected(competitor, loadedColumn2, MillisecondsTimePoint.now())); + assertTrue(loadedLeaderboard.getScoreCorrection().isScoreCorrected(competitorWithBoat, loadedColumn1, MillisecondsTimePoint.now())); + assertEquals(correctedPoints, (double) loadedLeaderboard.getScoreCorrection().getExplicitScoreCorrection(competitorWithBoat, loadedColumn1), 0.00000001); + assertFalse(loadedLeaderboard.getScoreCorrection().isScoreCorrected(competitorWithBoat, loadedColumn2, MillisecondsTimePoint.now())); } @Test @@ -218,7 +223,7 @@ public class TestStoringAndRetrievingLeaderboards extends AbstractMongoDBTest { final int[] discardIndexResultsStartingWithHowManyRaces = new int[] { 5, 8 }; FlexibleLeaderboardImpl leaderboard = new FlexibleLeaderboardImpl(leaderboardName, new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null); - Competitor competitor = createCompetitor(); + CompetitorWithBoat competitor = createCompetitorWithBoat(); TrackedRace raceWithOneCompetitor1 = new MockedTrackedRaceWithFixedRank(competitor, /* rank */ 1, /* started */ true); TrackedRace raceWithOneCompetitor2 = new MockedTrackedRaceWithFixedRank(competitor, /* rank */ 2, /* started */ true); leaderboard.addRace(raceWithOneCompetitor1, raceColumnName1, /* medalRace */ false); @@ -244,21 +249,21 @@ public class TestStoringAndRetrievingLeaderboards extends AbstractMongoDBTest { final int[] discardIndexResultsStartingWithHowManyRaces = new int[] { 5, 8 }; FlexibleLeaderboardImpl leaderboard = new FlexibleLeaderboardImpl(leaderboardName, new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null); - Competitor competitor = createCompetitor(); - TrackedRace raceWithOneCompetitor1 = new MockedTrackedRaceWithFixedRank(competitor, /* rank */ 1, /* started */ true); - TrackedRace raceWithOneCompetitor2 = new MockedTrackedRaceWithFixedRank(competitor, /* rank */ 2, /* started */ true); + CompetitorWithBoat competitorWithBoat = createCompetitorWithBoat(); + TrackedRace raceWithOneCompetitor1 = new MockedTrackedRaceWithFixedRank(competitorWithBoat, /* rank */ 1, /* started */ true); + TrackedRace raceWithOneCompetitor2 = new MockedTrackedRaceWithFixedRank(competitorWithBoat, /* rank */ 2, /* started */ true); leaderboard.addRace(raceWithOneCompetitor1, raceColumnName1, /* medalRace */ false); leaderboard.addRace(raceWithOneCompetitor2, raceColumnName2, /* medalRace */ true); - leaderboard.getScoreCorrection().setMaxPointsReason(competitor, leaderboard.getRaceColumnByName(raceColumnName2), maxPointsReason); + leaderboard.getScoreCorrection().setMaxPointsReason(competitorWithBoat, leaderboard.getRaceColumnByName(raceColumnName2), maxPointsReason); new MongoObjectFactoryImpl(db).storeLeaderboard(leaderboard); FlexibleLeaderboard loadedLeaderboard = (FlexibleLeaderboard) new DomainObjectFactoryImpl(db, DomainFactory.INSTANCE).loadLeaderboard(leaderboardName, /* regattaRegistry */ null, /* leaderboardRegistry */ null); // attach tracked race to leaderboard to ensure that competitor object is assigned properly RaceColumn loadedColumn1 = loadedLeaderboard.addRace(raceWithOneCompetitor1, raceColumnName1, /* medalRace, ignored */ false); RaceColumn loadedColumn2 = loadedLeaderboard.addRace(raceWithOneCompetitor2, raceColumnName2, /* medalRace, ignored */ false); assertEquals(leaderboardName, loadedLeaderboard.getName()); - assertFalse(loadedLeaderboard.getScoreCorrection().isScoreCorrected(competitor, loadedColumn1, MillisecondsTimePoint.now())); - assertTrue(loadedLeaderboard.getScoreCorrection().isScoreCorrected(competitor, loadedColumn2, MillisecondsTimePoint.now())); - assertEquals(maxPointsReason, loadedLeaderboard.getScoreCorrection().getMaxPointsReason(competitor, loadedColumn2, MillisecondsTimePoint.now())); + assertFalse(loadedLeaderboard.getScoreCorrection().isScoreCorrected(competitorWithBoat, loadedColumn1, MillisecondsTimePoint.now())); + assertTrue(loadedLeaderboard.getScoreCorrection().isScoreCorrected(competitorWithBoat, loadedColumn2, MillisecondsTimePoint.now())); + assertEquals(maxPointsReason, loadedLeaderboard.getScoreCorrection().getMaxPointsReason(competitorWithBoat, loadedColumn2, MillisecondsTimePoint.now())); } @Test @@ -271,23 +276,23 @@ public class TestStoringAndRetrievingLeaderboards extends AbstractMongoDBTest { final int[] discardIndexResultsStartingWithHowManyRaces = new int[] { 5, 8 }; FlexibleLeaderboardImpl leaderboard = new FlexibleLeaderboardImpl(leaderboardName, new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null); - Competitor competitor = createCompetitor(); - TrackedRace raceWithOneCompetitor1 = new MockedTrackedRaceWithFixedRank(competitor, /* rank */ 1, /* started */ true); - TrackedRace raceWithOneCompetitor2 = new MockedTrackedRaceWithFixedRank(competitor, /* rank */ 2, /* started */ true); + CompetitorWithBoat competitorWithBoat = createCompetitorWithBoat(); + TrackedRace raceWithOneCompetitor1 = new MockedTrackedRaceWithFixedRank(competitorWithBoat, /* rank */ 1, /* started */ true); + TrackedRace raceWithOneCompetitor2 = new MockedTrackedRaceWithFixedRank(competitorWithBoat, /* rank */ 2, /* started */ true); leaderboard.addRace(raceWithOneCompetitor1, raceColumnName1, /* medalRace */ false); leaderboard.addRace(raceWithOneCompetitor2, raceColumnName2, /* medalRace */ true); - leaderboard.getScoreCorrection().correctScore(competitor, leaderboard.getRaceColumnByName(raceColumnName1), correctedPoints); - leaderboard.getScoreCorrection().setMaxPointsReason(competitor, leaderboard.getRaceColumnByName(raceColumnName2), maxPointsReason); + leaderboard.getScoreCorrection().correctScore(competitorWithBoat, leaderboard.getRaceColumnByName(raceColumnName1), correctedPoints); + leaderboard.getScoreCorrection().setMaxPointsReason(competitorWithBoat, leaderboard.getRaceColumnByName(raceColumnName2), maxPointsReason); new MongoObjectFactoryImpl(db).storeLeaderboard(leaderboard); FlexibleLeaderboard loadedLeaderboard = (FlexibleLeaderboard) new DomainObjectFactoryImpl(db, DomainFactory.INSTANCE).loadLeaderboard(leaderboardName, /* regattaRegistry */ null, /* leaderboardRegistry */ null); // attach tracked race to leaderboard to ensure that competitor object is assigned properly RaceColumn loadedColumn1 = loadedLeaderboard.addRace(raceWithOneCompetitor1, raceColumnName1, /* medalRace, ignored */ false); RaceColumn loadedColumn2 = loadedLeaderboard.addRace(raceWithOneCompetitor2, raceColumnName2, /* medalRace, ignored */ false); assertEquals(leaderboardName, loadedLeaderboard.getName()); - assertTrue(loadedLeaderboard.getScoreCorrection().isScoreCorrected(competitor, loadedColumn1, MillisecondsTimePoint.now())); - assertEquals(correctedPoints, (double) loadedLeaderboard.getScoreCorrection().getExplicitScoreCorrection(competitor, loadedColumn1), 0.000000001); - assertTrue(loadedLeaderboard.getScoreCorrection().isScoreCorrected(competitor, loadedColumn2, MillisecondsTimePoint.now())); - assertEquals(maxPointsReason, loadedLeaderboard.getScoreCorrection().getMaxPointsReason(competitor, loadedColumn2, MillisecondsTimePoint.now())); + assertTrue(loadedLeaderboard.getScoreCorrection().isScoreCorrected(competitorWithBoat, loadedColumn1, MillisecondsTimePoint.now())); + assertEquals(correctedPoints, (double) loadedLeaderboard.getScoreCorrection().getExplicitScoreCorrection(competitorWithBoat, loadedColumn1), 0.000000001); + assertTrue(loadedLeaderboard.getScoreCorrection().isScoreCorrected(competitorWithBoat, loadedColumn2, MillisecondsTimePoint.now())); + assertEquals(maxPointsReason, loadedLeaderboard.getScoreCorrection().getMaxPointsReason(competitorWithBoat, loadedColumn2, MillisecondsTimePoint.now())); } /** @@ -309,13 +314,13 @@ public class TestStoringAndRetrievingLeaderboards extends AbstractMongoDBTest { final int[] discardIndexResultsStartingWithHowManyRaces = new int[] { 5, 8 }; FlexibleLeaderboardImpl leaderboard = new FlexibleLeaderboardImpl(leaderboardName, new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null); - Competitor competitor = createCompetitor(); - TrackedRace raceWithOneCompetitor1 = new MockedTrackedRaceWithFixedRank(competitor, /* rank */ 1, /* started */ true); - TrackedRace raceWithOneCompetitor2 = new MockedTrackedRaceWithFixedRank(competitor, /* rank */ 2, /* started */ true); + CompetitorWithBoat competitorWithBoat = createCompetitorWithBoat(); + TrackedRace raceWithOneCompetitor1 = new MockedTrackedRaceWithFixedRank(competitorWithBoat, /* rank */ 1, /* started */ true); + TrackedRace raceWithOneCompetitor2 = new MockedTrackedRaceWithFixedRank(competitorWithBoat, /* rank */ 2, /* started */ true); RaceColumn r1 = leaderboard.addRace(raceWithOneCompetitor1, raceColumnName1, /* medalRace */ false); RaceColumn r2 = leaderboard.addRace(raceWithOneCompetitor2, raceColumnName2, /* medalRace */ true); - leaderboard.getScoreCorrection().correctScore(competitor, leaderboard.getRaceColumnByName(raceColumnName1), correctedPoints); - leaderboard.getScoreCorrection().setMaxPointsReason(competitor, leaderboard.getRaceColumnByName(raceColumnName2), maxPointsReason); + leaderboard.getScoreCorrection().correctScore(competitorWithBoat, leaderboard.getRaceColumnByName(raceColumnName1), correctedPoints); + leaderboard.getScoreCorrection().setMaxPointsReason(competitorWithBoat, leaderboard.getRaceColumnByName(raceColumnName2), maxPointsReason); // now release the tracked races; leaving no competitors in leaderboard.getCompetitors() r1.releaseTrackedRace(leaderboard.getFleet(null)); r2.releaseTrackedRace(leaderboard.getFleet(null)); @@ -325,10 +330,10 @@ public class TestStoringAndRetrievingLeaderboards extends AbstractMongoDBTest { RaceColumn loadedColumn1 = loadedLeaderboard.addRace(raceWithOneCompetitor1, raceColumnName1, /* medalRace, ignored */ false); RaceColumn loadedColumn2 = loadedLeaderboard.addRace(raceWithOneCompetitor2, raceColumnName2, /* medalRace, ignored */ false); assertEquals(leaderboardName, loadedLeaderboard.getName()); - assertTrue(loadedLeaderboard.getScoreCorrection().isScoreCorrected(competitor, loadedColumn1, MillisecondsTimePoint.now())); - assertEquals(correctedPoints, (double) loadedLeaderboard.getScoreCorrection().getExplicitScoreCorrection(competitor, loadedColumn1), 0.000000001); - assertTrue(loadedLeaderboard.getScoreCorrection().isScoreCorrected(competitor, loadedColumn2, MillisecondsTimePoint.now())); - assertEquals(maxPointsReason, loadedLeaderboard.getScoreCorrection().getMaxPointsReason(competitor, loadedColumn2, MillisecondsTimePoint.now())); + assertTrue(loadedLeaderboard.getScoreCorrection().isScoreCorrected(competitorWithBoat, loadedColumn1, MillisecondsTimePoint.now())); + assertEquals(correctedPoints, (double) loadedLeaderboard.getScoreCorrection().getExplicitScoreCorrection(competitorWithBoat, loadedColumn1), 0.000000001); + assertTrue(loadedLeaderboard.getScoreCorrection().isScoreCorrected(competitorWithBoat, loadedColumn2, MillisecondsTimePoint.now())); + assertEquals(maxPointsReason, loadedLeaderboard.getScoreCorrection().getMaxPointsReason(competitorWithBoat, loadedColumn2, MillisecondsTimePoint.now())); } /** @@ -351,13 +356,15 @@ public class TestStoringAndRetrievingLeaderboards extends AbstractMongoDBTest { final DomainFactory domainFactory = new DomainFactoryImpl((srlid)->null); // create the competitor through the competitor store/factory here so that the DomainObjectFactory finds it and // resolves the score corrections appropriate - Competitor competitor = domainFactory.getOrCreateCompetitor(123, "$$$Dr. Wolfgang+Hunger$$$", Color.RED, "someone@nowhere.de", null, new TeamImpl("STG", Collections.singleton( + Competitor competitor = domainFactory.getOrCreateCompetitor(123, "$$$Dr. Wolfgang+Hunger$$$", "WH", Color.RED, "someone@nowhere.de", null, new TeamImpl("STG", Collections.singleton( new PersonImpl("$$$Dr. Wolfgang+Hunger$$$", new NationalityImpl("GER"), /* dateOfBirth */ null, "This is famous Dr. Wolfgang Hunger")), new PersonImpl("Rigo van Maas", new NationalityImpl("NED"), - /* dateOfBirth */ null, "This is Rigo, the coach")), new BoatImpl("Dr. Wolfgang Hunger's boat", new BoatClassImpl("505", /* typicallyStartsUpwind */ true), null), + /* dateOfBirth */ null, "This is Rigo, the coach")), /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null); - TrackedRace raceWithOneCompetitor1 = new MockedTrackedRaceWithFixedRank(competitor, /* rank */ 1, /* started */ true); - TrackedRace raceWithOneCompetitor2 = new MockedTrackedRaceWithFixedRank(competitor, /* rank */ 2, /* started */ true); + DynamicBoat boat = (DynamicBoat) domainFactory.getOrCreateBoat("boat", "Dr. Wolfgang Hunger's boat", new BoatClassImpl("505", /* typicallyStartsUpwind */ true), null, null); + CompetitorWithBoat competitorWithBoat = new CompetitorWithBoatImpl(competitor, boat); + TrackedRace raceWithOneCompetitor1 = new MockedTrackedRaceWithFixedRank(competitorWithBoat, /* rank */ 1, /* started */ true); + TrackedRace raceWithOneCompetitor2 = new MockedTrackedRaceWithFixedRank(competitorWithBoat, /* rank */ 2, /* started */ true); leaderboard.addRace(raceWithOneCompetitor1, raceColumnName1, /* medalRace */ false); leaderboard.addRace(raceWithOneCompetitor2, raceColumnName2, /* medalRace */ true); leaderboard.getScoreCorrection().correctScore(competitor, leaderboard.getRaceColumnByName(raceColumnName1), correctedPoints); @@ -378,12 +385,12 @@ public class TestStoringAndRetrievingLeaderboards extends AbstractMongoDBTest { assertEquals(maxPointsReason, loadedLeaderboard2.getScoreCorrection().getMaxPointsReason(competitor, loadedColumn2, MillisecondsTimePoint.now())); } - private Competitor createCompetitor() { - Competitor competitor = new CompetitorImpl(123, "$$$Dr. Wolfgang+Hunger$$$", Color.RED, null, null, new TeamImpl("STG", Collections.singleton( + private CompetitorWithBoat createCompetitorWithBoat() { + Competitor competitor = new CompetitorImpl(123, "$$$Dr. Wolfgang+Hunger$$$", "KYC", Color.RED, null, null, new TeamImpl("STG", Collections.singleton( new PersonImpl("$$$Dr. Wolfgang+Hunger$$$", new NationalityImpl("GER"), /* dateOfBirth */ null, "This is famous Dr. Wolfgang Hunger")), new PersonImpl("Rigo van Maas", new NationalityImpl("NED"), - /* dateOfBirth */ null, "This is Rigo, the coach")), new BoatImpl("Dr. Wolfgang Hunger's boat", new BoatClassImpl("505", /* typicallyStartsUpwind */ true), null), /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null); - return competitor; + /* dateOfBirth */ null, "This is Rigo, the coach")), /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null); + DynamicBoat boat = new BoatImpl(competitor.getId(), "Dr. Wolfgang Hunger's boat", new BoatClassImpl("505", /* typicallyStartsUpwind */ true), null); + return new CompetitorWithBoatImpl(competitor, boat); } - } diff --git a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndRetrievingRaceLogEventData.java b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndRetrievingRaceLogEventData.java index df5f58d9331..5aacf31eb5a 100644 --- a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndRetrievingRaceLogEventData.java +++ b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndRetrievingRaceLogEventData.java @@ -83,7 +83,7 @@ public class TestStoringAndRetrievingRaceLogEventData extends AbstractMongoDBTes DBCollection coll = db.getCollection(RACELOG_TEST_COLLECTION); assertNotNull(coll); DBObject object = coll.findOne(); - RaceLogFlagEvent readRcEvent = (RaceLogFlagEvent) ((DomainObjectFactoryImpl) PersistenceFactory.INSTANCE.getDefaultDomainObjectFactory()).loadRaceLogEvent(object); + RaceLogFlagEvent readRcEvent = (RaceLogFlagEvent) ((DomainObjectFactoryImpl) PersistenceFactory.INSTANCE.getDefaultDomainObjectFactory()).loadRaceLogEvent(object).getA(); assertEquals(rcEvent.getLogicalTimePoint(), readRcEvent.getLogicalTimePoint()); assertEquals(rcEvent.getId(), readRcEvent.getId()); assertEquals(rcEvent.getPassId(), readRcEvent.getPassId()); diff --git a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndRetrievingRaceLogInLeaderboards.java b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndRetrievingRaceLogInLeaderboards.java index 5b7c550d75c..192545561a3 100644 --- a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndRetrievingRaceLogInLeaderboards.java +++ b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndRetrievingRaceLogInLeaderboards.java @@ -305,8 +305,8 @@ public class TestStoringAndRetrievingRaceLogInLeaderboards extends RaceLogMongoD @Test public void testStoreAndRetrieveSimpleLeaderboardWithRaceLogFinishPositioningConfirmedEvent() { - Competitor storedCompetitor = DomainFactory.INSTANCE.getOrCreateCompetitor(UUID.randomUUID(), "SAP Extreme Sailing Team", - Color.RED, "someone@nowhere.de", null, null, null, + Competitor storedCompetitor = DomainFactory.INSTANCE.getOrCreateCompetitor(UUID.randomUUID(), "SAP Extreme Sailing Team", "SAP", + Color.RED, "someone@nowhere.de", null, null, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null); CompetitorResults storedPositioningList = new CompetitorResultsImpl(); storedPositioningList.add(new CompetitorResultImpl(storedCompetitor.getId(), storedCompetitor.getName(), /* rank */ @@ -329,8 +329,8 @@ public class TestStoringAndRetrievingRaceLogInLeaderboards extends RaceLogMongoD @Test public void testStoreAndRetrieveSimpleLeaderboardWithRaceLogFinishPositioningConfirmedEventWithContents() { - Competitor storedCompetitor = DomainFactory.INSTANCE.getOrCreateCompetitor(UUID.randomUUID(), "SAP Extreme Sailing Team", - Color.RED, "someone@nowhere.de", null, null, null, + Competitor storedCompetitor = DomainFactory.INSTANCE.getOrCreateCompetitor(UUID.randomUUID(), "SAP Extreme Sailing Team", "SAP", + Color.RED, "someone@nowhere.de", null, null, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null); CompetitorResults storedPositioningList = new CompetitorResultsImpl(); final TimePoint finishingTimePoint = MillisecondsTimePoint.now(); @@ -368,7 +368,7 @@ public class TestStoringAndRetrievingRaceLogInLeaderboards extends RaceLogMongoD RaceLogFinishPositioningConfirmedEvent loadedConfirmedEvent = (RaceLogFinishPositioningConfirmedEvent) loadedEvent; assertEquals(now, loadedConfirmedEvent.getLogicalTimePoint()); assertEquals(0, loadedConfirmedEvent.getPassId()); - assertEquals(0, loadedConfirmedEvent.getInvolvedBoats().size()); + assertEquals(0, loadedConfirmedEvent.getInvolvedCompetitors().size()); assertNull(event.getPositionedCompetitorsIDsNamesMaxPointsReasons()); assertNull(loadedConfirmedEvent.getPositionedCompetitorsIDsNamesMaxPointsReasons()); assertEquals(1, Util.size(loadedRaceLog.getFixes())); @@ -400,8 +400,8 @@ public class TestStoringAndRetrievingRaceLogInLeaderboards extends RaceLogMongoD @Test public void testStoreAndRetrieveSimpleLeaderboardWithRaceLogFinishPositioningListChangeEvent() { - Competitor storedCompetitor = DomainFactory.INSTANCE.getOrCreateCompetitor(UUID.randomUUID(), "SAP Extreme Sailing Team", Color.RED, - "someone@nowhere.de", null, null, null, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null); + Competitor storedCompetitor = DomainFactory.INSTANCE.getOrCreateCompetitor(UUID.randomUUID(), "SAP Extreme Sailing Team", "SAP", Color.RED, + "someone@nowhere.de", null, null, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null); CompetitorResults storedPositioningList = new CompetitorResultsImpl(); storedPositioningList.add(new CompetitorResultImpl(storedCompetitor.getId(), storedCompetitor.getName(), /* rank */ 1, MaxPointsReason.NONE, /* score */ null, /* finishingTimePoint */null, /* comment */null, MergeState.OK)); @@ -418,7 +418,7 @@ public class TestStoringAndRetrievingRaceLogInLeaderboards extends RaceLogMongoD assertEquals(event.getLogicalTimePoint(), loadedPositioningEvent.getLogicalTimePoint()); assertEquals(event.getPassId(), loadedPositioningEvent.getPassId()); assertEquals(event.getId(), loadedPositioningEvent.getId()); - assertEquals(event.getInvolvedBoats().size(), loadedPositioningEvent.getInvolvedBoats().size()); + assertEquals(event.getInvolvedCompetitors().size(), loadedPositioningEvent.getInvolvedCompetitors().size()); final CompetitorResults expectedCompetiorResults = event.getPositionedCompetitorsIDsNamesMaxPointsReasons(); final CompetitorResults loadedCompetitorResults = loadedPositioningEvent.getPositionedCompetitorsIDsNamesMaxPointsReasons(); assertCompetitorResultsEqual(expectedCompetiorResults, loadedCompetitorResults); diff --git a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndRetrievingRaceLogInRegatta.java b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndRetrievingRaceLogInRegatta.java index 6670ad26ebb..b9c1cd421de 100644 --- a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndRetrievingRaceLogInRegatta.java +++ b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndRetrievingRaceLogInRegatta.java @@ -206,8 +206,8 @@ public class TestStoringAndRetrievingRaceLogInRegatta extends AbstractTestStorin @Test public void testStoreAndRetrieveRegattaWithRaceLogFinishPositioningListChangeEvent() { - Competitor storedCompetitor = DomainFactory.INSTANCE.getOrCreateCompetitor(UUID.randomUUID(), "SAP Extreme Sailing Team", Color.RED, - "someone@nowhere.de", null, null, null, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null); + Competitor storedCompetitor = DomainFactory.INSTANCE.getOrCreateCompetitor(UUID.randomUUID(), "SAP Extreme Sailing Team", "SAP", Color.RED, + "someone@nowhere.de", null, null, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null); CompetitorResults storedPositioningList = new CompetitorResultsImpl(); storedPositioningList.add(new CompetitorResultImpl(storedCompetitor.getId(), storedCompetitor.getName(), /* rank */ 1, MaxPointsReason.NONE, /* score */ null, /* finishingTime */ null, /* comment */ null, MergeState.OK)); RaceLogFinishPositioningListChangedEvent event = new RaceLogFinishPositioningListChangedEventImpl(now, author, 0, storedPositioningList); @@ -220,7 +220,7 @@ public class TestStoringAndRetrievingRaceLogInRegatta extends AbstractTestStorin assertEquals(event.getLogicalTimePoint(), loadedPositioningEvent.getLogicalTimePoint()); assertEquals(event.getPassId(), loadedPositioningEvent.getPassId()); assertEquals(event.getId(), loadedPositioningEvent.getId()); - assertEquals(event.getInvolvedBoats().size(), loadedPositioningEvent.getInvolvedBoats().size()); + assertEquals(event.getInvolvedCompetitors().size(), loadedPositioningEvent.getInvolvedCompetitors().size()); assertCompetitorResultsEqual(event.getPositionedCompetitorsIDsNamesMaxPointsReasons(), loadedPositioningEvent.getPositionedCompetitorsIDsNamesMaxPointsReasons()); assertEquals(1, Util.size(loadedRaceLog.getFixes())); } finally { @@ -230,8 +230,8 @@ public class TestStoringAndRetrievingRaceLogInRegatta extends AbstractTestStorin @Test public void testStoreAndRetrieveRegattaWithRaceLogFinishPositioningConfirmedEvent() { - Competitor storedCompetitor = DomainFactory.INSTANCE.getOrCreateCompetitor(UUID.randomUUID(), "SAP Extreme Sailing Team", Color.RED, - "someone@nowhere.de", null, null, null, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null); + Competitor storedCompetitor = DomainFactory.INSTANCE.getOrCreateCompetitor(UUID.randomUUID(), "SAP Extreme Sailing Team", "SAP", Color.RED, + "someone@nowhere.de", null, null, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null); CompetitorResults storedPositioningList = new CompetitorResultsImpl(); storedPositioningList.add(new CompetitorResultImpl(storedCompetitor.getId(), storedCompetitor.getName(), /* rank */ 1, MaxPointsReason.NONE, /* score */ null, /* finishingTime */ null, /* comment */ null, MergeState.OK)); RaceLogFinishPositioningConfirmedEvent event = new RaceLogFinishPositioningConfirmedEventImpl(now, author, 0, storedPositioningList); @@ -244,7 +244,7 @@ public class TestStoringAndRetrievingRaceLogInRegatta extends AbstractTestStorin assertEquals(event.getLogicalTimePoint(), loadedConfirmedEvent.getLogicalTimePoint()); assertEquals(event.getPassId(), loadedConfirmedEvent.getPassId()); assertEquals(event.getId(), loadedConfirmedEvent.getId()); - assertEquals(event.getInvolvedBoats().size(), loadedConfirmedEvent.getInvolvedBoats().size()); + assertEquals(event.getInvolvedCompetitors().size(), loadedConfirmedEvent.getInvolvedCompetitors().size()); assertCompetitorResultsEqual(event.getPositionedCompetitorsIDsNamesMaxPointsReasons(), loadedConfirmedEvent.getPositionedCompetitorsIDsNamesMaxPointsReasons()); assertEquals(1, Util.size(loadedRaceLog.getFixes())); } finally { @@ -266,7 +266,7 @@ public class TestStoringAndRetrievingRaceLogInRegatta extends AbstractTestStorin RaceLogFinishPositioningConfirmedEvent loadedConfirmedEvent = (RaceLogFinishPositioningConfirmedEvent) loadedEvent; assertEquals(now, loadedConfirmedEvent.getLogicalTimePoint()); assertEquals(0, loadedConfirmedEvent.getPassId()); - assertEquals(0, loadedConfirmedEvent.getInvolvedBoats().size()); + assertEquals(0, loadedConfirmedEvent.getInvolvedCompetitors().size()); assertNull(event.getPositionedCompetitorsIDsNamesMaxPointsReasons()); assertNull(loadedConfirmedEvent.getPositionedCompetitorsIDsNamesMaxPointsReasons()); assertEquals(1, Util.size(loadedRaceLog.getFixes())); diff --git a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndRetrievingWindTracksTest.java b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndRetrievingWindTracksTest.java index e53c92b45da..9026ed84863 100755 --- a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndRetrievingWindTracksTest.java +++ b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndRetrievingWindTracksTest.java @@ -31,6 +31,7 @@ import com.sap.sailing.domain.common.impl.DegreePosition; import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl; import com.sap.sailing.domain.common.impl.WindImpl; import com.sap.sailing.domain.common.impl.WindSourceImpl; +import com.sap.sailing.domain.leaderboard.LeaderboardGroupResolver; import com.sap.sailing.domain.persistence.impl.DomainObjectFactoryImpl; import com.sap.sailing.domain.persistence.impl.MongoObjectFactoryImpl; import com.sap.sailing.domain.racelog.impl.EmptyRaceLogStore; @@ -91,7 +92,7 @@ public class TestStoringAndRetrievingWindTracksTest extends AbstractTracTracLive @Override public void addRaceDefinition(RaceDefinition race, DynamicTrackedRace trackedRace) { } - }, /* trackedRegattaRegistry */ null, mock(RaceLogResolver.class), + }, /* trackedRegattaRegistry */ null, mock(RaceLogResolver.class), mock(LeaderboardGroupResolver.class), /*courseDesignUpdateURI*/ null, /*tracTracUsername*/ null, /*tracTracPassword*/ null, getEventSubscriber(), getRaceSubscriber(), /*ignoreTracTracMarkPassings*/ false, RaceTracker.TIMEOUT_FOR_RECEIVING_RACE_DEFINITION_IN_MILLISECONDS, ReceiverType.RACECOURSE); addListenersForStoredDataAndStartController(typeControllers); for (final Receiver receiver : typeControllers) { diff --git a/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/windestimation/TestWindEstimationFromManeuversOnAFew505Races.java b/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/windestimation/TestWindEstimationFromManeuversOnAFew505Races.java index 82a89ca0a5f..9f42c59c552 100755 --- a/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/windestimation/TestWindEstimationFromManeuversOnAFew505Races.java +++ b/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/windestimation/TestWindEstimationFromManeuversOnAFew505Races.java @@ -18,7 +18,7 @@ import java.util.stream.Stream; import org.junit.Test; -import com.sap.sailing.domain.base.Competitor; +import com.sap.sailing.domain.base.CompetitorAndBoat; import com.sap.sailing.domain.common.Bearing; import com.sap.sailing.domain.common.ManeuverType; import com.sap.sailing.domain.common.Wind; @@ -118,7 +118,7 @@ public class TestWindEstimationFromManeuversOnAFew505Races extends OnlineTracTra setUp("event_20110609_KielerWoch-505_Race_3"); ManeuverBasedWindEstimationTrackImpl windTrack = new ManeuverBasedWindEstimationTrackImpl(new PolarDataServiceImpl(), getTrackedRace(), /* millisecondsOverWhichToAverage */ 30000, /* waitForLatest */ true); - final Map maneuvers = windTrack.getAllManeuvers(/* waitForLatest */ true); + final Map maneuvers = windTrack.getAllManeuvers(/* waitForLatest */ true); final int numberOfClusters = 16; KMeansMappingClusterer, Pair, ScalableBearingAndScalableDouble> clusterer = new KMeansMappingClusterer<>(numberOfClusters, diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/GPSFixMovingWithOriginInfo.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/GPSFixMovingWithOriginInfo.java index 69a0671379d..1c376b468d6 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/GPSFixMovingWithOriginInfo.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/GPSFixMovingWithOriginInfo.java @@ -1,5 +1,6 @@ package com.sap.sailing.polars.mining; +import com.sap.sailing.domain.base.Boat; import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.common.tracking.GPSFixMoving; import com.sap.sailing.domain.tracking.TrackedRace; @@ -14,11 +15,13 @@ public class GPSFixMovingWithOriginInfo { private final GPSFixMoving fix; private final TrackedRace trackedRace; private final Competitor competitor; + private final Boat boat; public GPSFixMovingWithOriginInfo(GPSFixMoving fix, TrackedRace trackedRace, Competitor competitor) { this.fix = fix; this.trackedRace = trackedRace; this.competitor = competitor; + this.boat = trackedRace.getBoatOfCompetitor(competitor); } public GPSFixMoving getFix() { @@ -32,4 +35,8 @@ public class GPSFixMovingWithOriginInfo { public Competitor getCompetitor() { return competitor; } + + public Boat getBoat() { + return boat; + } } diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataMiner.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataMiner.java index e4f240af998..0638666b94b 100755 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataMiner.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataMiner.java @@ -198,7 +198,7 @@ public class PolarDataMiner { element.getCompetitor(), backendPolarSheetGenerationSettings.getPctOfLeadingCompetitorsToInclude())) { result = true; - final BoatClass boatClass = element.getCompetitor().getBoat().getBoatClass(); + final BoatClass boatClass = element.getBoat().getBoatClass(); AtomicInteger count = stats.get(boatClass); if (count == null) { count = new AtomicInteger(1); diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/windestimation/ManeuverBasedWindEstimationTrackImpl.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/windestimation/ManeuverBasedWindEstimationTrackImpl.java index 5bfc2894a16..59678e3227c 100755 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/windestimation/ManeuverBasedWindEstimationTrackImpl.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/windestimation/ManeuverBasedWindEstimationTrackImpl.java @@ -12,10 +12,13 @@ import java.util.stream.Collectors; import java.util.stream.IntStream; import java.util.stream.Stream; +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.CompetitorAndBoat; import com.sap.sailing.domain.base.SpeedWithBearingWithConfidence; import com.sap.sailing.domain.base.Waypoint; +import com.sap.sailing.domain.base.impl.CompetitorAndBoatImpl; import com.sap.sailing.domain.base.impl.SpeedWithBearingWithConfidenceImpl; import com.sap.sailing.domain.common.Bearing; import com.sap.sailing.domain.common.Distance; @@ -169,6 +172,7 @@ public class ManeuverBasedWindEstimationTrackImpl extends WindTrackImpl { */ public class ManeuverClassification { private final Competitor competitor; + private final Boat boat; private final TimePoint timePoint; private final Position position; /** @@ -181,9 +185,10 @@ public class ManeuverBasedWindEstimationTrackImpl extends WindTrackImpl { private Pair>[] likelihoodAndTWSBasedOnSpeedAndAngleCache; private ScalableBearingAndScalableDouble scalableMiddleManeuverCourseAndManeuverAngleDegCache; - protected ManeuverClassification(Competitor competitor, Maneuver maneuver) { + protected ManeuverClassification(CompetitorAndBoat competitorWithBoat, Maneuver maneuver) { super(); - this.competitor = competitor; + this.competitor = competitorWithBoat.getCompetitor(); + this.boat = competitorWithBoat.getBoat(); this.timePoint = maneuver.getTimePoint(); this.position = maneuver.getPosition(); this.maneuverAngleDeg = maneuver.getDirectionChangeInDegrees(); @@ -201,6 +206,10 @@ public class ManeuverBasedWindEstimationTrackImpl extends WindTrackImpl { return competitor; } + public Boat getBoat() { + return boat; + } + public TimePoint getTimePoint() { return timePoint; } @@ -231,7 +240,7 @@ public class ManeuverBasedWindEstimationTrackImpl extends WindTrackImpl { public SpeedWithBearingWithConfidence getEstimatedWindSpeedAndBearing(ManeuverType maneuverType) { Pair> likelihoodAndTWSBasedOnSpeedAndAngle = polarService - .getManeuverLikelihoodAndTwsTwa(getCompetitor().getBoat().getBoatClass(), + .getManeuverLikelihoodAndTwsTwa(getBoat().getBoatClass(), getSpeedAtManeuverStart(), getManeuverAngleDeg(), maneuverType); final SpeedWithBearingWithConfidenceImpl result; // if no reasonable wind speed was found for the maneuver speed, return null @@ -277,7 +286,7 @@ public class ManeuverBasedWindEstimationTrackImpl extends WindTrackImpl { final ManeuverType maneuverType) { if (likelihoodAndTWSBasedOnSpeedAndAngleCache[maneuverType.ordinal()] == null) { likelihoodAndTWSBasedOnSpeedAndAngleCache[maneuverType.ordinal()] = polarService - .getManeuverLikelihoodAndTwsTwa(getCompetitor().getBoat().getBoatClass(), + .getManeuverLikelihoodAndTwsTwa(getBoat().getBoatClass(), getSpeedAtManeuverStart(), getManeuverAngleDeg(), maneuverType); } return likelihoodAndTWSBasedOnSpeedAndAngleCache[maneuverType.ordinal()]; @@ -378,7 +387,7 @@ public class ManeuverBasedWindEstimationTrackImpl extends WindTrackImpl { */ private Triple, Pair, ScalableBearingAndScalableDouble>>, List, Pair, ScalableBearingAndScalableDouble>>, List, Pair, ScalableBearingAndScalableDouble>>> analyzeRace( boolean waitForLatest) throws NotEnoughDataHasBeenAddedException { - final Map maneuvers = getAllManeuvers(waitForLatest); + final Map maneuvers = getAllManeuvers(waitForLatest); // cluster into eight clusters by middle COG first, then aggregate tack likelihoods for each, and jibe // likelihoods for opposite cluster final int numberOfClusters = 16; @@ -773,7 +782,7 @@ public class ManeuverBasedWindEstimationTrackImpl extends WindTrackImpl { /** * For each maneuver from maneuvers, a {@link ManeuverClassification object} is created. */ - Stream getManeuverClassifications(final Map maneuvers) { + Stream getManeuverClassifications(final Map maneuvers) { return maneuvers .entrySet() .stream() @@ -784,11 +793,13 @@ public class ManeuverBasedWindEstimationTrackImpl extends WindTrackImpl { /** * Package scope to let test fragment access it */ - Map getAllManeuvers(boolean waitForLatest) { - Map maneuvers = new HashMap<>(); + Map getAllManeuvers(boolean waitForLatest) { + Map maneuvers = new HashMap<>(); final Waypoint firstWaypoint = trackedRace.getRace().getCourse().getFirstWaypoint(); final Waypoint lastWaypoint = trackedRace.getRace().getCourse().getLastWaypoint(); - for (Competitor competitor : trackedRace.getRace().getCompetitors()) { + for (Map.Entry competitorAndBoat : trackedRace.getRace().getCompetitorsAndTheirBoats().entrySet()) { + Competitor competitor = competitorAndBoat.getKey(); + Boat boat = competitorAndBoat.getValue(); final TimePoint from = Util.getFirstNonNull( firstWaypoint == null ? null : trackedRace.getMarkPassing(competitor, firstWaypoint) == null ? null : trackedRace.getMarkPassing(competitor, firstWaypoint).getTimePoint(), trackedRace @@ -798,7 +809,7 @@ public class ManeuverBasedWindEstimationTrackImpl extends WindTrackImpl { : trackedRace.getMarkPassing(competitor, lastWaypoint).getTimePoint(), trackedRace .getEndOfRace(), trackedRace.getEndOfTracking(), MillisecondsTimePoint.now()); for (Maneuver maneuver : trackedRace.getManeuvers(competitor, from, to, waitForLatest)) { - maneuvers.put(maneuver, competitor); + maneuvers.put(maneuver, new CompetitorAndBoatImpl(competitor, boat)); } } return Collections.unmodifiableMap(maneuvers); diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/windestimation/ManeuverClassificationToHasConfidenceAndIsScalableAdapter.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/windestimation/ManeuverClassificationToHasConfidenceAndIsScalableAdapter.java index 6b218328d6e..e7ae57878f5 100755 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/windestimation/ManeuverClassificationToHasConfidenceAndIsScalableAdapter.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/windestimation/ManeuverClassificationToHasConfidenceAndIsScalableAdapter.java @@ -56,7 +56,7 @@ public class ManeuverClassificationToHasConfidenceAndIsScalableAdapter { + public static class BoatEntry extends DataEntryPO { + @FindBy(how = BySeleniumId.class, using = "ACTION_EDIT") + private WebElement editButton; + + private static final String NAME_COLUMN = "Name"; + private static final String SAILID_COLUMN = "Sail number"; + private static final String BOATCLASSNAME_COLUMN = "Boat Class"; + private static final String ID_COLUMN = "ID"; + + protected BoatEntry(TrackedRacesBoatTablePO table, WebElement element) { + super(table, element); + } + + @Override + public String getIdentifier() { + return getId(); + } + + public String getId() { + return getColumnContent(ID_COLUMN); + } + + public String getName() { + return getColumnContent(NAME_COLUMN); + } + + public String getSailId() { + return getColumnContent(SAILID_COLUMN); + } + + public String getBoatClassName() { + return getColumnContent(BOATCLASSNAME_COLUMN); + } + + public TrackedRacesCompetitorEditDialogPO clickEditButton() { + WebElement action = ActionsHelper.findEditAction(getWebElement()); + action.click(); + WebElement dialog = findElementBySeleniumId(this.driver, "BoatEditDialog"); + return new TrackedRacesCompetitorEditDialogPO(this.driver, dialog); + } + } + + public TrackedRacesBoatTablePO(WebDriver driver, WebElement element) { + super(driver, element); + } + + @Override + protected BoatEntry createDataEntry(WebElement element) { + return new BoatEntry(this, element); + } +} \ No newline at end of file diff --git a/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/adminconsole/tracking/TrackedRacesBoatsPanelPO.java b/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/adminconsole/tracking/TrackedRacesBoatsPanelPO.java new file mode 100644 index 00000000000..ad4fb610c34 --- /dev/null +++ b/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/adminconsole/tracking/TrackedRacesBoatsPanelPO.java @@ -0,0 +1,42 @@ +package com.sap.sailing.selenium.pages.adminconsole.tracking; + +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; + +import com.sap.sailing.selenium.core.BySeleniumId; +import com.sap.sailing.selenium.core.FindBy; +import com.sap.sailing.selenium.pages.PageArea; + +public class TrackedRacesBoatsPanelPO extends PageArea { + @FindBy(how = BySeleniumId.class, using = "RefreshButton") + private WebElement refreshButton; + + @FindBy(how = BySeleniumId.class, using = "AddBoatButton") + private WebElement addButton; + + @FindBy(how = BySeleniumId.class, using = "BoatsTable") + private WebElement boatsTable; + + public final WebDriver driver; + + public TrackedRacesBoatsPanelPO(WebDriver driver, WebElement element) { + super(driver, element); + this.driver = driver; + } + + public TrackedRacesBoatEditDialogPO pushAddButton() { + this.addButton.click(); + waitForAjaxRequests(); + WebElement dialog = findElementBySeleniumId(this.driver, "BoatEditDialog"); + return new TrackedRacesBoatEditDialogPO(this.driver, dialog); + } + + public void pushRefreshButton() { + this.refreshButton.click(); + waitForAjaxRequests(); + } + + public TrackedRacesBoatTablePO getBoatsTable() { + return new TrackedRacesBoatTablePO(this.driver, this.boatsTable); + } +} diff --git a/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/adminconsole/tracking/TrackedRacesCompetitorEditDialogPO.java b/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/adminconsole/tracking/TrackedRacesCompetitorEditDialogPO.java index 71d31591584..8da5ae7fc10 100644 --- a/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/adminconsole/tracking/TrackedRacesCompetitorEditDialogPO.java +++ b/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/adminconsole/tracking/TrackedRacesCompetitorEditDialogPO.java @@ -11,11 +11,8 @@ public class TrackedRacesCompetitorEditDialogPO extends DataEntryDialogPO { @FindBy(how = BySeleniumId.class, using = "NameTextBox") private WebElement nameTextBox; - @FindBy(how = BySeleniumId.class, using = "SailIdTextBox") - private WebElement sailIdTextBox; - - @FindBy(how = BySeleniumId.class, using = "BoatClassNameSuggestBox") - private WebElement boatClassNameSuggestBox; + @FindBy(how = BySeleniumId.class, using = "ShortNameTextBox") + private WebElement shortNameTextBox; @FindBy(how = BySeleniumId.class, using = "OkButton") private WebElement okButton; @@ -29,13 +26,8 @@ public class TrackedRacesCompetitorEditDialogPO extends DataEntryDialogPO { this.nameTextBox.sendKeys(name); } - public void setSailIdTextBox(String sailId) { - this.sailIdTextBox.clear(); - this.sailIdTextBox.sendKeys(sailId); - } - - public void setBoatClassNameSuggestBox(String boatClassName) { - this.boatClassNameSuggestBox.clear(); - this.boatClassNameSuggestBox.sendKeys(boatClassName); + public void setShortNameTextBox(String name) { + this.shortNameTextBox.clear(); + this.shortNameTextBox.sendKeys(name); } } diff --git a/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/adminconsole/tracking/TrackedRacesCompetitorTablePO.java b/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/adminconsole/tracking/TrackedRacesCompetitorTablePO.java index 031549d25e4..f1d65004fb4 100644 --- a/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/adminconsole/tracking/TrackedRacesCompetitorTablePO.java +++ b/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/adminconsole/tracking/TrackedRacesCompetitorTablePO.java @@ -16,6 +16,7 @@ public class TrackedRacesCompetitorTablePO extends CellTablePO private WebElement editButton; private static final String NAME_COLUMN = "Name"; + private static final String SHORT_NAME_COLUMN = "Short name"; private static final String SAILID_COLUMN = "Sail number"; private static final String BOATCLASSNAME_COLUMN = "Boat Class"; private static final String ID_COLUMN = "ID"; @@ -37,6 +38,10 @@ public class TrackedRacesCompetitorTablePO extends CellTablePO return getColumnContent(NAME_COLUMN); } + public String getShortName() { + return getColumnContent(SHORT_NAME_COLUMN); + } + public String getSailId() { return getColumnContent(SAILID_COLUMN); } @@ -51,7 +56,14 @@ public class TrackedRacesCompetitorTablePO extends CellTablePO WebElement dialog = findElementBySeleniumId(this.driver, "CompetitorEditDialog"); return new TrackedRacesCompetitorEditDialogPO(this.driver, dialog); } - } + + public TrackedRacesCompetitorWithBoatEditDialogPO clickEditWithBoatButton() { + WebElement action = ActionsHelper.findEditAction(getWebElement()); + action.click(); + WebElement dialog = findElementBySeleniumId(this.driver, "CompetitorWithBoatEditDialog"); + return new TrackedRacesCompetitorWithBoatEditDialogPO(this.driver, dialog); + } +} public TrackedRacesCompetitorTablePO(WebDriver driver, WebElement element) { super(driver, element); diff --git a/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/adminconsole/tracking/TrackedRacesCompetitorWithBoatEditDialogPO.java b/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/adminconsole/tracking/TrackedRacesCompetitorWithBoatEditDialogPO.java new file mode 100644 index 00000000000..ee1a69d090d --- /dev/null +++ b/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/adminconsole/tracking/TrackedRacesCompetitorWithBoatEditDialogPO.java @@ -0,0 +1,30 @@ +package com.sap.sailing.selenium.pages.adminconsole.tracking; + +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; + +import com.sap.sailing.selenium.core.BySeleniumId; +import com.sap.sailing.selenium.core.FindBy; + +public class TrackedRacesCompetitorWithBoatEditDialogPO extends TrackedRacesCompetitorEditDialogPO { + + @FindBy(how = BySeleniumId.class, using = "SailIdTextBox") + private WebElement sailIdTextBox; + + @FindBy(how = BySeleniumId.class, using = "BoatClassNameSuggestBox") + private WebElement boatClassNameSuggestBox; + + public TrackedRacesCompetitorWithBoatEditDialogPO(WebDriver driver, WebElement element) { + super(driver, element); + } + + public void setSailIdTextBox(String sailId) { + this.sailIdTextBox.clear(); + this.sailIdTextBox.sendKeys(sailId); + } + + public void setBoatClassNameSuggestBox(String boatClassName) { + this.boatClassNameSuggestBox.clear(); + this.boatClassNameSuggestBox.sendKeys(boatClassName); + } +} diff --git a/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/adminconsole/tracking/TrackedRacesCompetitorsPanelPO.java b/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/adminconsole/tracking/TrackedRacesCompetitorsPanelPO.java index 5da74444762..8dfb2de64ad 100644 --- a/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/adminconsole/tracking/TrackedRacesCompetitorsPanelPO.java +++ b/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/adminconsole/tracking/TrackedRacesCompetitorsPanelPO.java @@ -12,8 +12,8 @@ public class TrackedRacesCompetitorsPanelPO extends PageArea { private WebElement refreshButton; @FindBy(how = BySeleniumId.class, using = "AddCompetitorButton") - private WebElement addButton; - + private WebElement addCompetitorButton; + @FindBy(how = BySeleniumId.class, using = "CompetitorsTable") private WebElement competitorsTable; @@ -24,13 +24,13 @@ public class TrackedRacesCompetitorsPanelPO extends PageArea { this.driver = driver; } - public TrackedRacesCompetitorEditDialogPO pushAddButton() { - this.addButton.click(); + public TrackedRacesCompetitorEditDialogPO pushAddCompetitorButton() { + this.addCompetitorButton.click(); waitForAjaxRequests(); WebElement dialog = findElementBySeleniumId(this.driver, "CompetitorEditDialog"); return new TrackedRacesCompetitorEditDialogPO(this.driver, dialog); } - + public void pushRefreshButton() { this.refreshButton.click(); waitForAjaxRequests(); diff --git a/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/test/adminconsole/TestBoatCreation.java b/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/test/adminconsole/TestBoatCreation.java new file mode 100644 index 00000000000..012ed850ffb --- /dev/null +++ b/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/test/adminconsole/TestBoatCreation.java @@ -0,0 +1,60 @@ +package com.sap.sailing.selenium.test.adminconsole; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import org.junit.Before; +import org.junit.Test; + +import com.sap.sailing.selenium.pages.adminconsole.AdminConsolePage; +import com.sap.sailing.selenium.pages.adminconsole.tracking.TrackedRacesBoatEditDialogPO; +import com.sap.sailing.selenium.pages.adminconsole.tracking.TrackedRacesBoatTablePO.BoatEntry; +import com.sap.sailing.selenium.pages.adminconsole.tracking.TrackedRacesBoatsPanelPO; +import com.sap.sailing.selenium.test.AbstractSeleniumTest; + +public class TestBoatCreation extends AbstractSeleniumTest { + @Override + @Before + public void setUp() { + clearState(getContextRoot()); + super.setUp(); + } + + @Test + public void testOpenCreateBoatDialog() { + final TrackedRacesBoatsPanelPO competitorsPanel = goToBoatsPanel(); + final TrackedRacesBoatEditDialogPO dialog = competitorsPanel.pushAddButton(); // fails with an exception if the dialog is not found + assertNotNull(dialog); + } + + private TrackedRacesBoatsPanelPO goToBoatsPanel() { + final AdminConsolePage adminConsole = AdminConsolePage.goToPage(getWebDriver(), getContextRoot()); + final TrackedRacesBoatsPanelPO boatsPanel = adminConsole.goToTrackedRacesBoats(); + return boatsPanel; + } + + @Test + public void testBoatCreation() { + final TrackedRacesBoatsPanelPO boatsPanel = goToBoatsPanel(); + final TrackedRacesBoatEditDialogPO dialog = boatsPanel.pushAddButton(); + final String name = ""+System.currentTimeMillis(); + dialog.setNameTextBox(name); + String sailId = ""+System.currentTimeMillis(); + dialog.setSailIdTextBox(sailId); + final String boatClassName = "49er"; + dialog.setBoatClassNameSuggestBox(boatClassName); + dialog.pressOk(); + boolean found = false; + for (final BoatEntry it : boatsPanel.getBoatsTable().getEntries()) { + String itName = it.getName(); + if (itName.equals(name)) { + found = true; + // found a candidate: + assertEquals(sailId, it.getSailId()); + assertEquals(boatClassName, it.getBoatClassName()); + } + } + assertTrue(found); + } +} diff --git a/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/test/adminconsole/TestCompetitorCreation.java b/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/test/adminconsole/TestCompetitorCreation.java index 32a18c1eafb..d17cffa2bf1 100644 --- a/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/test/adminconsole/TestCompetitorCreation.java +++ b/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/test/adminconsole/TestCompetitorCreation.java @@ -24,7 +24,7 @@ public class TestCompetitorCreation extends AbstractSeleniumTest { @Test public void testOpenCreateCompetitorDialog() { final TrackedRacesCompetitorsPanelPO competitorsPanel = goToCompetitorsPanel(); - final TrackedRacesCompetitorEditDialogPO dialog = competitorsPanel.pushAddButton(); // fails with an exception if the dialog is not found + final TrackedRacesCompetitorEditDialogPO dialog = competitorsPanel.pushAddCompetitorButton(); // fails with an exception if the dialog is not found assertNotNull(dialog); } @@ -37,13 +37,11 @@ public class TestCompetitorCreation extends AbstractSeleniumTest { @Test public void testCompetitorCreation() { final TrackedRacesCompetitorsPanelPO competitorsPanel = goToCompetitorsPanel(); - final TrackedRacesCompetitorEditDialogPO dialog = competitorsPanel.pushAddButton(); + final TrackedRacesCompetitorEditDialogPO dialog = competitorsPanel.pushAddCompetitorButton(); final String name = ""+System.currentTimeMillis(); + final String shortName = "NRV"; dialog.setNameTextBox(name); - String sailId = ""+System.currentTimeMillis(); - dialog.setSailIdTextBox(sailId); - final String boatClassName = "Laser Int."; - dialog.setBoatClassNameSuggestBox(boatClassName); + dialog.setShortNameTextBox(shortName); dialog.pressOk(); boolean found = false; for (final CompetitorEntry it : competitorsPanel.getCompetitorTable().getEntries()) { @@ -51,10 +49,10 @@ public class TestCompetitorCreation extends AbstractSeleniumTest { if (itName.equals(name)) { found = true; // found a candidate: - assertEquals(sailId, it.getSailId()); - assertEquals(boatClassName, it.getBoatClassName()); + assertEquals(shortName, it.getShortName()); } } assertTrue(found); } + } diff --git a/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/test/adminconsole/TestRefreshableSelectionModel.java b/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/test/adminconsole/TestRefreshableSelectionModel.java index 4c51ba0da8a..4075640702c 100644 --- a/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/test/adminconsole/TestRefreshableSelectionModel.java +++ b/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/test/adminconsole/TestRefreshableSelectionModel.java @@ -21,13 +21,13 @@ import com.sap.sailing.selenium.pages.adminconsole.leaderboard.LeaderboardConfig import com.sap.sailing.selenium.pages.adminconsole.leaderboard.LeaderboardDetailsPanelPO; import com.sap.sailing.selenium.pages.adminconsole.leaderboard.LeaderboardDetailsPanelPO.RaceDescriptor; import com.sap.sailing.selenium.pages.adminconsole.regatta.RegattaDetailsCompositePO; +import com.sap.sailing.selenium.pages.adminconsole.regatta.RegattaListCompositePO.RegattaDescriptor; import com.sap.sailing.selenium.pages.adminconsole.regatta.RegattaStructureManagementPanelPO; import com.sap.sailing.selenium.pages.adminconsole.regatta.SeriesEditDialogPO; -import com.sap.sailing.selenium.pages.adminconsole.regatta.RegattaListCompositePO.RegattaDescriptor; import com.sap.sailing.selenium.pages.adminconsole.tracking.RaceColumnTableWrapperPO; import com.sap.sailing.selenium.pages.adminconsole.tracking.TrackedRacesCompetitorEditDialogPO; -import com.sap.sailing.selenium.pages.adminconsole.tracking.TrackedRacesCompetitorsPanelPO; import com.sap.sailing.selenium.pages.adminconsole.tracking.TrackedRacesCompetitorTablePO.CompetitorEntry; +import com.sap.sailing.selenium.pages.adminconsole.tracking.TrackedRacesCompetitorsPanelPO; import com.sap.sailing.selenium.pages.adminconsole.tracking.TrackedRacesListPO.TrackedRaceDescriptor; import com.sap.sailing.selenium.pages.adminconsole.tractrac.TracTracEventManagementPanelPO; import com.sap.sailing.selenium.pages.adminconsole.tractrac.TracTracEventManagementPanelPO.TrackableRaceDescriptor; @@ -84,20 +84,17 @@ public class TestRefreshableSelectionModel extends AbstractSeleniumTest { final TrackedRacesCompetitorsPanelPO competitorsPanel = goToCompetitorsPanel(); for (int i = 0; i < 2; i++) { - TrackedRacesCompetitorEditDialogPO dialog = competitorsPanel.pushAddButton(); + TrackedRacesCompetitorEditDialogPO dialog = competitorsPanel.pushAddCompetitorButton(); dialog.setNameTextBox("" + System.currentTimeMillis()); - dialog.setSailIdTextBox("" + System.currentTimeMillis()); - dialog.setBoatClassNameSuggestBox("RS-X"); + dialog.setShortNameTextBox("" + System.currentTimeMillis()); dialog.pressOk(); } - TrackedRacesCompetitorEditDialogPO dialog = competitorsPanel.pushAddButton(); + TrackedRacesCompetitorEditDialogPO dialog = competitorsPanel.pushAddCompetitorButton(); final String name = "" + System.currentTimeMillis(); dialog.setNameTextBox(name); - final String sailId = "" + System.currentTimeMillis(); - dialog.setSailIdTextBox(sailId); - final String boatClassName = "Laser Int."; - dialog.setBoatClassNameSuggestBox(boatClassName); + final String shortName = "" + System.currentTimeMillis(); + dialog.setShortNameTextBox(shortName); dialog.pressOk(); boolean found = false; @@ -127,15 +124,14 @@ public class TestRefreshableSelectionModel extends AbstractSeleniumTest { assertEquals(1, competitorPanelForSelection.getCompetitorTable().getSelectedEntries().size()); assertEquals(name, competitorPanelForSelection.getCompetitorTable().getSelectedEntries().get(0).getName()); - assertEquals(sailId, competitorPanelForSelection.getCompetitorTable().getSelectedEntries().get(0).getSailId()); - assertEquals(boatClassName, competitorPanelForSelection.getCompetitorTable().getSelectedEntries().get(0).getBoatClassName()); + assertEquals(shortName, competitorPanelForSelection.getCompetitorTable().getSelectedEntries().get(0).getShortName()); // change competitor windowForEdit.switchToWindow(); dialog = competitorEntry.clickEditButton(); final String changedName = "" + System.currentTimeMillis(); dialog.setNameTextBox(changedName); - final String changedSailId = "" + System.currentTimeMillis(); - dialog.setSailIdTextBox(changedSailId); + final String changedShortName = "" + System.currentTimeMillis(); + dialog.setShortNameTextBox(changedShortName); dialog.pressOk(); // assert selection @@ -162,8 +158,7 @@ public class TestRefreshableSelectionModel extends AbstractSeleniumTest { } assertTrue(found); assertEquals(changedName, competitorPanelForSelection.getCompetitorTable().getSelectedEntries().get(0).getName()); - assertEquals(changedSailId, competitorPanelForSelection.getCompetitorTable().getSelectedEntries().get(0).getSailId()); - assertEquals(boatClassName, competitorPanelForSelection.getCompetitorTable().getSelectedEntries().get(0).getBoatClassName()); + assertEquals(changedShortName, competitorPanelForSelection.getCompetitorTable().getSelectedEntries().get(0).getShortName()); } private void setUpTestRefreshOfDependingUIElements() { diff --git a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/impl/BoatJsonDeserializer.java b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/impl/BoatJsonDeserializer.java index 22759b45efc..f582565453c 100755 --- a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/impl/BoatJsonDeserializer.java +++ b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/impl/BoatJsonDeserializer.java @@ -1,31 +1,59 @@ package com.sap.sailing.server.gateway.deserialization.impl; +import java.io.Serializable; +import java.lang.reflect.Constructor; +import java.util.UUID; + import org.json.simple.JSONObject; import com.sap.sailing.domain.base.BoatClass; -import com.sap.sailing.domain.base.impl.BoatImpl; +import com.sap.sailing.domain.base.BoatFactory; +import com.sap.sailing.domain.base.SharedDomainFactory; import com.sap.sailing.domain.base.impl.DynamicBoat; import com.sap.sailing.server.gateway.deserialization.JsonDeserializationException; import com.sap.sailing.server.gateway.deserialization.JsonDeserializer; import com.sap.sailing.server.gateway.serialization.impl.BoatJsonSerializer; +import com.sap.sse.common.Color; +import com.sap.sse.common.impl.RGBColor; public class BoatJsonDeserializer implements JsonDeserializer { private final BoatClassJsonDeserializer boatClassDeserializer; + private final BoatFactory boatFactory; + + public static BoatJsonDeserializer create(SharedDomainFactory baseDomainFactory) { + return new BoatJsonDeserializer(baseDomainFactory, new BoatClassJsonDeserializer(baseDomainFactory)); + } - public BoatJsonDeserializer(BoatClassJsonDeserializer boatClassDeserializer) { + public BoatJsonDeserializer(BoatFactory boatFactory, BoatClassJsonDeserializer boatClassDeserializer) { super(); + this.boatFactory = boatFactory; this.boatClassDeserializer = boatClassDeserializer; } public DynamicBoat deserialize(JSONObject object) throws JsonDeserializationException { - String name = (String) object.get(BoatJsonSerializer.FIELD_NAME); - final Object sailID = object.get(BoatJsonSerializer.FIELD_SAIL_ID); - String sailId = sailID == null ? null : sailID.toString(); - BoatClass boatClass = null; - if (boatClassDeserializer != null) { - boatClass = boatClassDeserializer.deserialize(Helpers.getNestedObjectSafe(object, BoatJsonSerializer.FIELD_BOAT_CLASS)); + Serializable boatId = (Serializable) object.get(BoatJsonSerializer.FIELD_ID); + try { + Class idClass = Class.forName((String) object.get(BoatJsonSerializer.FIELD_ID_TYPE)); + if (Number.class.isAssignableFrom(idClass)) { + Constructor constructorFromString = idClass.getConstructor(String.class); + boatId = (Serializable) constructorFromString.newInstance(boatId.toString()); + } else if (UUID.class.isAssignableFrom(idClass)) { + boatId = Helpers.tryUuidConversion(boatId); + } + String name = (String) object.get(BoatJsonSerializer.FIELD_NAME); + String sailId = (String) object.get(BoatJsonSerializer.FIELD_SAIL_ID); + BoatClass boatClass = null; + if (boatClassDeserializer != null) { + boatClass = boatClassDeserializer.deserialize(Helpers.getNestedObjectSafe(object, BoatJsonSerializer.FIELD_BOAT_CLASS)); + } + String colorAsString = (String) object.get(BoatJsonSerializer.FIELD_COLOR); + final Color color = colorAsString == null || colorAsString.isEmpty() ? null : new RGBColor(colorAsString); + DynamicBoat boat = (DynamicBoat) boatFactory.getOrCreateBoat(boatId, name, boatClass, sailId, color); + + return boat; + } catch (Exception e) { + throw new JsonDeserializationException(e); } - return new BoatImpl(name, boatClass, sailId); } } diff --git a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/impl/CompetitorAndBoatJsonDeserializer.java b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/impl/CompetitorAndBoatJsonDeserializer.java new file mode 100644 index 00000000000..1eb176b3e8a --- /dev/null +++ b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/impl/CompetitorAndBoatJsonDeserializer.java @@ -0,0 +1,33 @@ +package com.sap.sailing.server.gateway.deserialization.impl; + +import org.json.simple.JSONObject; + +import com.sap.sailing.domain.base.Boat; +import com.sap.sailing.domain.base.Competitor; +import com.sap.sailing.domain.base.SharedDomainFactory; +import com.sap.sailing.domain.base.impl.DynamicBoat; +import com.sap.sailing.server.gateway.deserialization.JsonDeserializationException; +import com.sap.sailing.server.gateway.deserialization.JsonDeserializer; +import com.sap.sailing.server.gateway.serialization.impl.CompetitorAndBoatJsonSerializer; +import com.sap.sse.common.Util.Pair; + +public class CompetitorAndBoatJsonDeserializer implements JsonDeserializer> { + private final JsonDeserializer competitorDeserializer; + private final JsonDeserializer boatDeserializer; + + public static CompetitorAndBoatJsonDeserializer create(SharedDomainFactory baseDomainFactory) { + return new CompetitorAndBoatJsonDeserializer(CompetitorJsonDeserializer.create(baseDomainFactory), BoatJsonDeserializer.create(baseDomainFactory)); + } + + public CompetitorAndBoatJsonDeserializer(JsonDeserializer competitorDeserializer, JsonDeserializer boatDeserializer) { + this.competitorDeserializer = competitorDeserializer; + this.boatDeserializer = boatDeserializer; + } + + @Override + public Pair deserialize(JSONObject competitorAndBoatJsonObject) throws JsonDeserializationException { + Competitor competitor = competitorDeserializer.deserialize(Helpers.getNestedObjectSafe(competitorAndBoatJsonObject, CompetitorAndBoatJsonSerializer.FIELD_COMPETITOR)); + Boat boat = boatDeserializer.deserialize(Helpers.getNestedObjectSafe(competitorAndBoatJsonObject, CompetitorAndBoatJsonSerializer.FIELD_BOAT)); + return new Pair(competitor, boat); + } +} diff --git a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/impl/CompetitorJsonDeserializer.java b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/impl/CompetitorJsonDeserializer.java index 6a74171ecfc..8242ac9576b 100644 --- a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/impl/CompetitorJsonDeserializer.java +++ b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/impl/CompetitorJsonDeserializer.java @@ -10,9 +10,7 @@ import org.json.simple.JSONObject; import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.CompetitorFactory; -import com.sap.sailing.domain.base.CompetitorStore; import com.sap.sailing.domain.base.SharedDomainFactory; -import com.sap.sailing.domain.base.impl.DynamicBoat; import com.sap.sailing.domain.base.impl.DynamicTeam; import com.sap.sailing.domain.common.tracking.impl.CompetitorJsonConstants; import com.sap.sailing.server.gateway.deserialization.JsonDeserializationException; @@ -23,24 +21,23 @@ import com.sap.sse.common.impl.MillisecondsDurationImpl; import com.sap.sse.common.impl.RGBColor; public class CompetitorJsonDeserializer implements JsonDeserializer { - protected final CompetitorFactory competitorStore; - protected final JsonDeserializer teamJsonDeserializer; - protected final JsonDeserializer boatJsonDeserializer; + private final CompetitorFactory competitorFactory; + private final JsonDeserializer teamJsonDeserializer; + private static final Logger logger = Logger.getLogger(CompetitorJsonDeserializer.class.getName()); public static CompetitorJsonDeserializer create(SharedDomainFactory baseDomainFactory) { return new CompetitorJsonDeserializer(baseDomainFactory, new TeamJsonDeserializer(new PersonJsonDeserializer( - new NationalityJsonDeserializer(baseDomainFactory))), new BoatJsonDeserializer(new BoatClassJsonDeserializer(baseDomainFactory))); + new NationalityJsonDeserializer(baseDomainFactory)))); } - public CompetitorJsonDeserializer(CompetitorStore store) { - this(store, null, /* boatDeserializer */ null); + public CompetitorJsonDeserializer(CompetitorFactory competitorFactory) { + this(competitorFactory, null); } - public CompetitorJsonDeserializer(CompetitorFactory competitorStore, JsonDeserializer teamJsonDeserializer, JsonDeserializer boatDeserializer) { - this.competitorStore = competitorStore; + public CompetitorJsonDeserializer(CompetitorFactory competitorFactory, JsonDeserializer teamJsonDeserializer) { + this.competitorFactory = competitorFactory; this.teamJsonDeserializer = teamJsonDeserializer; - this.boatJsonDeserializer = boatDeserializer; } @Override @@ -55,6 +52,7 @@ public class CompetitorJsonDeserializer implements JsonDeserializer competitorId = Helpers.tryUuidConversion(competitorId); } String name = (String) object.get(CompetitorJsonConstants.FIELD_NAME); + String shortName = (String) object.get(CompetitorJsonConstants.FIELD_SHORT_NAME); String displayColorAsString = (String) object.get(CompetitorJsonConstants.FIELD_DISPLAY_COLOR); String email = (String) object.get(CompetitorJsonConstants.FIELD_EMAIL); String searchTag = (String) object.get(CompetitorJsonConstants.FIELD_SEARCHTAG); @@ -76,20 +74,15 @@ public class CompetitorJsonDeserializer implements JsonDeserializer displayColor = new RGBColor(displayColorAsString); } DynamicTeam team = null; - DynamicBoat boat = null; if (teamJsonDeserializer != null && object.get(CompetitorJsonConstants.FIELD_TEAM) != null) { team = teamJsonDeserializer.deserialize(Helpers.getNestedObjectSafe(object, CompetitorJsonConstants.FIELD_TEAM)); } - if (boatJsonDeserializer != null && object.get(CompetitorJsonConstants.FIELD_BOAT) != null) { - boat = boatJsonDeserializer.deserialize(Helpers.getNestedObjectSafe(object, - CompetitorJsonConstants.FIELD_BOAT)); - } final Double timeOnTimeFactor = (Double) object.get(CompetitorJsonConstants.FIELD_TIME_ON_TIME_FACTOR); final Double timeOnDistanceAllowanceInSecondsPerNauticalMile = (Double) object .get(CompetitorJsonConstants.FIELD_TIME_ON_DISTANCE_ALLOWANCE_IN_SECONDS_PER_NAUTICAL_MILE); - Competitor competitor = competitorStore.getOrCreateCompetitor(competitorId, name, displayColor, email, - flagImageURI, team, boat, timeOnTimeFactor, + Competitor competitor = competitorFactory.getOrCreateCompetitor(competitorId, name, shortName, displayColor, email, + flagImageURI, team, timeOnTimeFactor, timeOnDistanceAllowanceInSecondsPerNauticalMile == null ? null : new MillisecondsDurationImpl((long) (timeOnDistanceAllowanceInSecondsPerNauticalMile*1000)), searchTag); return competitor; diff --git a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/impl/CompetitorWithBoatJsonDeserializer.java b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/impl/CompetitorWithBoatJsonDeserializer.java new file mode 100644 index 00000000000..ea74b819490 --- /dev/null +++ b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/impl/CompetitorWithBoatJsonDeserializer.java @@ -0,0 +1,103 @@ +package com.sap.sailing.server.gateway.deserialization.impl; + +import java.io.Serializable; +import java.lang.reflect.Constructor; +import java.net.URI; +import java.util.UUID; +import java.util.logging.Logger; + +import org.json.simple.JSONObject; + +import com.sap.sailing.domain.base.CompetitorFactory; +import com.sap.sailing.domain.base.CompetitorWithBoat; +import com.sap.sailing.domain.base.SharedDomainFactory; +import com.sap.sailing.domain.base.impl.DynamicBoat; +import com.sap.sailing.domain.base.impl.DynamicTeam; +import com.sap.sailing.domain.common.tracking.impl.CompetitorJsonConstants; +import com.sap.sailing.server.gateway.deserialization.JsonDeserializationException; +import com.sap.sailing.server.gateway.deserialization.JsonDeserializer; +import com.sap.sailing.server.gateway.serialization.impl.CompetitorJsonSerializer; +import com.sap.sse.common.Color; +import com.sap.sse.common.impl.MillisecondsDurationImpl; +import com.sap.sse.common.impl.RGBColor; + +public class CompetitorWithBoatJsonDeserializer implements JsonDeserializer { + private final CompetitorFactory competitorWithBoatFactory; + private final JsonDeserializer teamJsonDeserializer; + private final JsonDeserializer boatJsonDeserializer; + private static final Logger logger = Logger.getLogger(CompetitorWithBoatJsonDeserializer.class.getName()); + + public static CompetitorWithBoatJsonDeserializer create(SharedDomainFactory baseDomainFactory) { + return new CompetitorWithBoatJsonDeserializer(baseDomainFactory, new TeamJsonDeserializer(new PersonJsonDeserializer( + new NationalityJsonDeserializer(baseDomainFactory))), new BoatJsonDeserializer(baseDomainFactory, new BoatClassJsonDeserializer(baseDomainFactory))); + } + + public CompetitorWithBoatJsonDeserializer(CompetitorFactory competitorWithBoatFactory) { + this(competitorWithBoatFactory, null, /* boatDeserializer */ null); + } + + public CompetitorWithBoatJsonDeserializer(CompetitorFactory competitorWithBoatFactory, JsonDeserializer teamJsonDeserializer, JsonDeserializer boatDeserializer) { + this.competitorWithBoatFactory = competitorWithBoatFactory; + this.teamJsonDeserializer = teamJsonDeserializer; + this.boatJsonDeserializer = boatDeserializer; + } + + @Override + public CompetitorWithBoat deserialize(JSONObject object) throws JsonDeserializationException { + Serializable competitorId = (Serializable) object.get(CompetitorJsonSerializer.FIELD_ID); + try { + Class idClass = Class.forName((String) object.get(CompetitorJsonConstants.FIELD_ID_TYPE)); + if (Number.class.isAssignableFrom(idClass)) { + Constructor constructorFromString = idClass.getConstructor(String.class); + competitorId = (Serializable) constructorFromString.newInstance(competitorId.toString()); + } else if (UUID.class.isAssignableFrom(idClass)) { + competitorId = Helpers.tryUuidConversion(competitorId); + } + String name = (String) object.get(CompetitorJsonConstants.FIELD_NAME); + String shortName = (String) object.get(CompetitorJsonConstants.FIELD_SHORT_NAME); + String displayColorAsString = (String) object.get(CompetitorJsonConstants.FIELD_DISPLAY_COLOR); + String email = (String) object.get(CompetitorJsonConstants.FIELD_EMAIL); + String searchTag = (String) object.get(CompetitorJsonConstants.FIELD_SEARCHTAG); + + URI flagImageURI = null; + String flagImageURIAsString = (String) object.get(CompetitorJsonConstants.FIELD_FLAG_IMAGE_URI); + if (flagImageURIAsString != null) { + try { + flagImageURI = URI.create(flagImageURIAsString); + } catch (IllegalArgumentException e) { + logger.warning("Illegal flag image URI " + e.getMessage()); + } + } + + final Color displayColor; + if (displayColorAsString == null || displayColorAsString.isEmpty()) { + displayColor = null; + } else { + displayColor = new RGBColor(displayColorAsString); + } + DynamicTeam team = null; + DynamicBoat boat = null; + if (teamJsonDeserializer != null && object.get(CompetitorJsonConstants.FIELD_TEAM) != null) { + team = teamJsonDeserializer.deserialize(Helpers.getNestedObjectSafe(object, + CompetitorJsonConstants.FIELD_TEAM)); + } + if (boatJsonDeserializer != null && object.get(CompetitorJsonConstants.FIELD_BOAT) != null) { + boat = boatJsonDeserializer.deserialize(Helpers.getNestedObjectSafe(object, + CompetitorJsonConstants.FIELD_BOAT)); + String sailID = (String) object.get(CompetitorJsonConstants.FIELD_SAIL_ID); + boat.setSailId(sailID); + } + final Double timeOnTimeFactor = (Double) object.get(CompetitorJsonConstants.FIELD_TIME_ON_TIME_FACTOR); + final Double timeOnDistanceAllowanceInSecondsPerNauticalMile = (Double) object + .get(CompetitorJsonConstants.FIELD_TIME_ON_DISTANCE_ALLOWANCE_IN_SECONDS_PER_NAUTICAL_MILE); + CompetitorWithBoat competitorWithBoat = competitorWithBoatFactory.getOrCreateCompetitorWithBoat(competitorId, name, shortName, displayColor, email, + flagImageURI, team, timeOnTimeFactor, + timeOnDistanceAllowanceInSecondsPerNauticalMile == null ? null : + new MillisecondsDurationImpl((long) (timeOnDistanceAllowanceInSecondsPerNauticalMile*1000)), searchTag, boat); + + return competitorWithBoat; + } catch (Exception e) { + throw new JsonDeserializationException(e); + } + } +} diff --git a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/impl/CompetitorWithBoatRefJsonDeserializer.java b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/impl/CompetitorWithBoatRefJsonDeserializer.java new file mode 100644 index 00000000000..53d3281087d --- /dev/null +++ b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/impl/CompetitorWithBoatRefJsonDeserializer.java @@ -0,0 +1,115 @@ +package com.sap.sailing.server.gateway.deserialization.impl; + +import java.io.Serializable; +import java.lang.reflect.Constructor; +import java.net.URI; +import java.util.UUID; +import java.util.logging.Logger; + +import org.json.simple.JSONObject; + +import com.sap.sailing.domain.base.Boat; +import com.sap.sailing.domain.base.BoatFactory; +import com.sap.sailing.domain.base.Competitor; +import com.sap.sailing.domain.base.CompetitorFactory; +import com.sap.sailing.domain.base.SharedDomainFactory; +import com.sap.sailing.domain.base.impl.DynamicBoat; +import com.sap.sailing.domain.base.impl.DynamicTeam; +import com.sap.sailing.domain.common.tracking.impl.CompetitorJsonConstants; +import com.sap.sailing.server.gateway.deserialization.JsonDeserializationException; +import com.sap.sailing.server.gateway.deserialization.JsonDeserializer; +import com.sap.sailing.server.gateway.serialization.impl.CompetitorJsonSerializer; +import com.sap.sse.common.Color; +import com.sap.sse.common.impl.MillisecondsDurationImpl; +import com.sap.sse.common.impl.RGBColor; + +public class CompetitorWithBoatRefJsonDeserializer implements JsonDeserializer { + private final CompetitorFactory competitorFactory; + private final BoatFactory boatFactory; + private final JsonDeserializer teamJsonDeserializer; + private static final Logger logger = Logger.getLogger(CompetitorWithBoatRefJsonDeserializer.class.getName()); + + public static CompetitorWithBoatRefJsonDeserializer create(SharedDomainFactory baseDomainFactory) { + return new CompetitorWithBoatRefJsonDeserializer(baseDomainFactory, baseDomainFactory, new TeamJsonDeserializer(new PersonJsonDeserializer( + new NationalityJsonDeserializer(baseDomainFactory)))); + } + + public CompetitorWithBoatRefJsonDeserializer(CompetitorFactory competitorFactory, BoatFactory boatFactory) { + this(competitorFactory, boatFactory, null); + } + + public CompetitorWithBoatRefJsonDeserializer(CompetitorFactory competitorFactory, BoatFactory boatFactory, JsonDeserializer teamJsonDeserializer) { + this.competitorFactory = competitorFactory; + this.boatFactory = boatFactory; + this.teamJsonDeserializer = teamJsonDeserializer; + } + + @Override + public Competitor deserialize(JSONObject object) throws JsonDeserializationException { + Serializable competitorId = (Serializable) object.get(CompetitorJsonSerializer.FIELD_ID); + try { + { + Class competitorIdClass = Class.forName((String) object.get(CompetitorJsonConstants.FIELD_ID_TYPE)); + if (Number.class.isAssignableFrom(competitorIdClass)) { + Constructor constructorFromString = competitorIdClass.getConstructor(String.class); + competitorId = (Serializable) constructorFromString.newInstance(competitorId.toString()); + } else if (UUID.class.isAssignableFrom(competitorIdClass)) { + competitorId = Helpers.tryUuidConversion(competitorId); + } + } + String name = (String) object.get(CompetitorJsonConstants.FIELD_NAME); + String shortName = (String) object.get(CompetitorJsonConstants.FIELD_SHORT_NAME); + String displayColorAsString = (String) object.get(CompetitorJsonConstants.FIELD_DISPLAY_COLOR); + String email = (String) object.get(CompetitorJsonConstants.FIELD_EMAIL); + String searchTag = (String) object.get(CompetitorJsonConstants.FIELD_SEARCHTAG); + URI flagImageURI = null; + String flagImageURIAsString = (String) object.get(CompetitorJsonConstants.FIELD_FLAG_IMAGE_URI); + if (flagImageURIAsString != null) { + try { + flagImageURI = URI.create(flagImageURIAsString); + } catch (IllegalArgumentException e) { + logger.warning("Illegal flag image URI " + e.getMessage()); + } + } + final Color displayColor; + if (displayColorAsString == null || displayColorAsString.isEmpty()) { + displayColor = null; + } else { + displayColor = new RGBColor(displayColorAsString); + } + DynamicTeam team = null; + if (teamJsonDeserializer != null && object.get(CompetitorJsonConstants.FIELD_TEAM) != null) { + team = teamJsonDeserializer.deserialize(Helpers.getNestedObjectSafe(object, + CompetitorJsonConstants.FIELD_TEAM)); + } + final Double timeOnTimeFactor = (Double) object.get(CompetitorJsonConstants.FIELD_TIME_ON_TIME_FACTOR); + final Double timeOnDistanceAllowanceInSecondsPerNauticalMile = (Double) object + .get(CompetitorJsonConstants.FIELD_TIME_ON_DISTANCE_ALLOWANCE_IN_SECONDS_PER_NAUTICAL_MILE); + // if we find a boat identifier we try to find the boat with the boatfactory + Serializable boatId = (Serializable) object.get(CompetitorJsonConstants.FIELD_BOAT_ID); + if (boatId != null) { + Class boatIdClass = Class.forName((String) object.get(CompetitorJsonConstants.FIELD_BOAT_ID_TYPE)); + if (Number.class.isAssignableFrom(boatIdClass)) { + Constructor constructorFromString = boatIdClass.getConstructor(String.class); + boatId = (Serializable) constructorFromString.newInstance(boatId.toString()); + } else if (UUID.class.isAssignableFrom(boatIdClass)) { + boatId = Helpers.tryUuidConversion(boatId); + } + } + final Boat existingBoat = boatId != null ? boatFactory.getExistingBoatById(boatId) : null; + if (existingBoat != null) { + return competitorFactory.getOrCreateCompetitorWithBoat(competitorId, name, shortName, displayColor, email, + flagImageURI, team, timeOnTimeFactor, + timeOnDistanceAllowanceInSecondsPerNauticalMile == null ? null : + new MillisecondsDurationImpl((long) (timeOnDistanceAllowanceInSecondsPerNauticalMile*1000)), searchTag, (DynamicBoat) existingBoat); + } else { + return competitorFactory.getOrCreateCompetitor(competitorId, name, shortName, displayColor, email, + flagImageURI, team, timeOnTimeFactor, + timeOnDistanceAllowanceInSecondsPerNauticalMile == null ? null : + new MillisecondsDurationImpl((long) (timeOnDistanceAllowanceInSecondsPerNauticalMile*1000)), searchTag); + } + } catch (Exception e) { + throw new JsonDeserializationException(e); + } + } +} diff --git a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/impl/LegacyBoatJsonDeserializer.java b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/impl/LegacyBoatJsonDeserializer.java new file mode 100644 index 00000000000..120db7ee34a --- /dev/null +++ b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/impl/LegacyBoatJsonDeserializer.java @@ -0,0 +1,64 @@ +package com.sap.sailing.server.gateway.deserialization.impl; + +import java.io.Serializable; +import java.lang.reflect.Constructor; +import java.util.UUID; + +import org.json.simple.JSONObject; + +import com.sap.sailing.domain.base.BoatClass; +import com.sap.sailing.domain.base.BoatFactory; +import com.sap.sailing.domain.base.SharedDomainFactory; +import com.sap.sailing.domain.base.impl.BoatImpl; +import com.sap.sailing.domain.base.impl.DynamicBoat; +import com.sap.sailing.server.gateway.deserialization.JsonDeserializationException; +import com.sap.sailing.server.gateway.deserialization.JsonDeserializer; +import com.sap.sailing.server.gateway.serialization.impl.BoatJsonSerializer; +import com.sap.sse.common.Color; +import com.sap.sse.common.impl.RGBColor; + +public class LegacyBoatJsonDeserializer implements JsonDeserializer { + private final BoatClassJsonDeserializer boatClassDeserializer; + + public static LegacyBoatJsonDeserializer create(SharedDomainFactory baseDomainFactory) { + return new LegacyBoatJsonDeserializer(baseDomainFactory, new BoatClassJsonDeserializer(baseDomainFactory)); + } + + public LegacyBoatJsonDeserializer(BoatFactory boatFactory, BoatClassJsonDeserializer boatClassDeserializer) { + super(); + this.boatClassDeserializer = boatClassDeserializer; + } + + public DynamicBoat deserialize(JSONObject object) throws JsonDeserializationException { + Serializable boatId = (Serializable) object.get(BoatJsonSerializer.FIELD_ID); + try { + if (boatId == null) { + // no boatId - probably a legacy boat -> create an UUID + boatId = UUID.randomUUID(); + } else { + Class idClass = Class.forName((String) object.get(BoatJsonSerializer.FIELD_ID_TYPE)); + if (Number.class.isAssignableFrom(idClass)) { + Constructor constructorFromString = idClass.getConstructor(String.class); + boatId = (Serializable) constructorFromString.newInstance(boatId.toString()); + } else if (UUID.class.isAssignableFrom(idClass)) { + boatId = Helpers.tryUuidConversion(boatId); + } + } + String name = (String) object.get(BoatJsonSerializer.FIELD_NAME); + final Object sailID = object.get(BoatJsonSerializer.FIELD_SAIL_ID); + String sailId = sailID == null ? null : sailID.toString(); + BoatClass boatClass = null; + if (boatClassDeserializer != null) { + boatClass = boatClassDeserializer.deserialize(Helpers.getNestedObjectSafe(object, BoatJsonSerializer.FIELD_BOAT_CLASS)); + } + String colorAsString = (String) object.get(BoatJsonSerializer.FIELD_COLOR); + final Color color = colorAsString == null || colorAsString.isEmpty() ? null : new RGBColor(colorAsString); + DynamicBoat boat = new BoatImpl(boatId, name, boatClass, sailId, color); + + return boat; + } catch (Exception e) { + throw new JsonDeserializationException(e); + } + } + +} diff --git a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/impl/LegacyCompetitorWithContainedBoatJsonDeserializer.java b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/impl/LegacyCompetitorWithContainedBoatJsonDeserializer.java new file mode 100644 index 00000000000..3fb9f7221aa --- /dev/null +++ b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/impl/LegacyCompetitorWithContainedBoatJsonDeserializer.java @@ -0,0 +1,105 @@ +package com.sap.sailing.server.gateway.deserialization.impl; + +import java.io.Serializable; +import java.lang.reflect.Constructor; +import java.net.URI; +import java.util.UUID; +import java.util.logging.Logger; + +import org.json.simple.JSONObject; + +import com.sap.sailing.domain.base.CompetitorFactory; +import com.sap.sailing.domain.base.CompetitorWithBoat; +import com.sap.sailing.domain.base.SharedDomainFactory; +import com.sap.sailing.domain.base.impl.CompetitorWithBoatImpl; +import com.sap.sailing.domain.base.impl.DynamicBoat; +import com.sap.sailing.domain.base.impl.DynamicTeam; +import com.sap.sailing.domain.common.tracking.impl.CompetitorJsonConstants; +import com.sap.sailing.server.gateway.deserialization.JsonDeserializationException; +import com.sap.sailing.server.gateway.deserialization.JsonDeserializer; +import com.sap.sailing.server.gateway.serialization.impl.CompetitorJsonSerializer; +import com.sap.sse.common.Color; +import com.sap.sse.common.impl.MillisecondsDurationImpl; +import com.sap.sse.common.impl.RGBColor; + +public class LegacyCompetitorWithContainedBoatJsonDeserializer implements JsonDeserializer { + protected final CompetitorFactory competitorWithBoatFactory; + protected final JsonDeserializer teamJsonDeserializer; + protected final JsonDeserializer boatJsonDeserializer; + private static final Logger logger = Logger.getLogger(LegacyCompetitorWithContainedBoatJsonDeserializer.class.getName()); + + public static LegacyCompetitorWithContainedBoatJsonDeserializer create(SharedDomainFactory baseDomainFactory) { + return new LegacyCompetitorWithContainedBoatJsonDeserializer(baseDomainFactory, new TeamJsonDeserializer(new PersonJsonDeserializer( + new NationalityJsonDeserializer(baseDomainFactory))), new LegacyBoatJsonDeserializer(baseDomainFactory, new BoatClassJsonDeserializer(baseDomainFactory))); + } + + public LegacyCompetitorWithContainedBoatJsonDeserializer(CompetitorFactory competitorWithBoatFactory) { + this(competitorWithBoatFactory, null, /* boatDeserializer */ null); + } + + public LegacyCompetitorWithContainedBoatJsonDeserializer(CompetitorFactory competitorWithBoatFactory, JsonDeserializer teamJsonDeserializer, JsonDeserializer boatDeserializer) { + this.competitorWithBoatFactory = competitorWithBoatFactory; + this.teamJsonDeserializer = teamJsonDeserializer; + this.boatJsonDeserializer = boatDeserializer; + } + + @Override + public CompetitorWithBoat deserialize(JSONObject object) throws JsonDeserializationException { + Serializable competitorId = (Serializable) object.get(CompetitorJsonSerializer.FIELD_ID); + try { + Class idClass = Class.forName((String) object.get(CompetitorJsonConstants.FIELD_ID_TYPE)); + if (Number.class.isAssignableFrom(idClass)) { + Constructor constructorFromString = idClass.getConstructor(String.class); + competitorId = (Serializable) constructorFromString.newInstance(competitorId.toString()); + } else if (UUID.class.isAssignableFrom(idClass)) { + competitorId = Helpers.tryUuidConversion(competitorId); + } + String name = (String) object.get(CompetitorJsonConstants.FIELD_NAME); + String shortName = (String) object.get(CompetitorJsonConstants.FIELD_SHORT_NAME); + String displayColorAsString = (String) object.get(CompetitorJsonConstants.FIELD_DISPLAY_COLOR); + String email = (String) object.get(CompetitorJsonConstants.FIELD_EMAIL); + String searchTag = (String) object.get(CompetitorJsonConstants.FIELD_SEARCHTAG); + + URI flagImageURI = null; + String flagImageURIAsString = (String) object.get(CompetitorJsonConstants.FIELD_FLAG_IMAGE_URI); + if (flagImageURIAsString != null) { + try { + flagImageURI = URI.create(flagImageURIAsString); + } catch (IllegalArgumentException e) { + logger.warning("Illegal flag image URI " + e.getMessage()); + } + } + + final Color displayColor; + if (displayColorAsString == null || displayColorAsString.isEmpty()) { + displayColor = null; + } else { + displayColor = new RGBColor(displayColorAsString); + } + DynamicTeam team = null; + DynamicBoat boat = null; + if (teamJsonDeserializer != null && object.get(CompetitorJsonConstants.FIELD_TEAM) != null) { + team = teamJsonDeserializer.deserialize(Helpers.getNestedObjectSafe(object, + CompetitorJsonConstants.FIELD_TEAM)); + } + if (boatJsonDeserializer != null && object.get(CompetitorJsonConstants.FIELD_BOAT) != null) { + boat = boatJsonDeserializer.deserialize(Helpers.getNestedObjectSafe(object, + CompetitorJsonConstants.FIELD_BOAT)); + String sailID = (String) object.get(CompetitorJsonConstants.FIELD_SAIL_ID); + boat.setSailId(sailID); + } + final Double timeOnTimeFactor = (Double) object.get(CompetitorJsonConstants.FIELD_TIME_ON_TIME_FACTOR); + final Double timeOnDistanceAllowanceInSecondsPerNauticalMile = (Double) object + .get(CompetitorJsonConstants.FIELD_TIME_ON_DISTANCE_ALLOWANCE_IN_SECONDS_PER_NAUTICAL_MILE); + + CompetitorWithBoat competitorWithBoat = new CompetitorWithBoatImpl(competitorId, name, shortName, displayColor, email, + flagImageURI, team, timeOnTimeFactor, + timeOnDistanceAllowanceInSecondsPerNauticalMile == null ? null : + new MillisecondsDurationImpl((long) (timeOnDistanceAllowanceInSecondsPerNauticalMile*1000)), searchTag, boat); + + return competitorWithBoat; + } catch (Exception e) { + throw new JsonDeserializationException(e); + } + } +} diff --git a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/racegroup/impl/RaceGroupDeserializer.java b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/racegroup/impl/RaceGroupDeserializer.java index 20eb3745964..fa39ca07b68 100644 --- a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/racegroup/impl/RaceGroupDeserializer.java +++ b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/racegroup/impl/RaceGroupDeserializer.java @@ -53,6 +53,7 @@ public class RaceGroupDeserializer implements JsonDeserializer { String name = object.get(RaceGroupJsonSerializer.FIELD_NAME).toString(); BoatClass boatClass = null; String displayName = null; + Boolean canBoatsOfCompetitorsChangePerRace = false; CourseArea courseArea = null; RegattaConfiguration configuration = null; if (object.containsKey(RaceGroupJsonSerializer.FIELD_COURSE_AREA)) { @@ -76,6 +77,10 @@ public class RaceGroupDeserializer implements JsonDeserializer { final Object displayNameJson = object.get(RaceGroupJsonSerializer.FIELD_DISPLAY_NAME); displayName = displayNameJson == null ? null : displayNameJson.toString(); } - return new RaceGroupImpl(name, displayName, boatClass, courseArea, series, configuration); + if (object.containsKey(RaceGroupJsonSerializer.FIELD_CAN_BOATS_OF_COMPETITORS_CHANGE_PER_RACE)) { + final Object canBoatsOfCompetitorsChangePerRaceJson = object.get(RaceGroupJsonSerializer.FIELD_CAN_BOATS_OF_COMPETITORS_CHANGE_PER_RACE); + canBoatsOfCompetitorsChangePerRace = canBoatsOfCompetitorsChangePerRaceJson == null ? null : (Boolean) canBoatsOfCompetitorsChangePerRaceJson; + } + return new RaceGroupImpl(name, displayName, boatClass, canBoatsOfCompetitorsChangePerRace, courseArea, series, configuration); } } \ No newline at end of file diff --git a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/racelog/impl/RaceLogEventDeserializer.java b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/racelog/impl/RaceLogEventDeserializer.java index 048608e5046..3477ce46d7d 100755 --- a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/racelog/impl/RaceLogEventDeserializer.java +++ b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/racelog/impl/RaceLogEventDeserializer.java @@ -4,6 +4,7 @@ import org.json.simple.JSONObject; import com.sap.sailing.domain.abstractlog.race.RaceLogEvent; import com.sap.sailing.domain.base.Competitor; +import com.sap.sailing.domain.base.CompetitorWithBoat; import com.sap.sailing.domain.base.SharedDomainFactory; import com.sap.sailing.domain.common.DeviceIdentifier; import com.sap.sailing.domain.racelogtracking.SmartphoneUUIDIdentifier; @@ -14,7 +15,9 @@ import com.sap.sailing.server.gateway.deserialization.coursedata.impl.CourseBase import com.sap.sailing.server.gateway.deserialization.coursedata.impl.GateDeserializer; import com.sap.sailing.server.gateway.deserialization.coursedata.impl.MarkDeserializer; import com.sap.sailing.server.gateway.deserialization.coursedata.impl.WaypointDeserializer; +import com.sap.sailing.server.gateway.deserialization.impl.BoatJsonDeserializer; import com.sap.sailing.server.gateway.deserialization.impl.CompetitorJsonDeserializer; +import com.sap.sailing.server.gateway.deserialization.impl.CompetitorWithBoatJsonDeserializer; import com.sap.sailing.server.gateway.deserialization.impl.DeviceIdentifierJsonDeserializer; import com.sap.sailing.server.gateway.deserialization.impl.PositionJsonDeserializer; import com.sap.sailing.server.gateway.deserialization.impl.WindJsonDeserializer; @@ -55,6 +58,7 @@ public class RaceLogEventDeserializer implements JsonDeserializer public static RaceLogEventDeserializer create(SharedDomainFactory domainFactory, JsonDeserializer deviceDeserializer) { JsonDeserializer competitorDeserializer = CompetitorJsonDeserializer.create(domainFactory); + JsonDeserializer competitorWithBoatDeserializer = CompetitorWithBoatJsonDeserializer.create(domainFactory); return new RaceLogEventDeserializer( new RaceLogFlagEventDeserializer(competitorDeserializer), new RaceLogStartTimeEventDeserializer(competitorDeserializer), @@ -78,7 +82,7 @@ public class RaceLogEventDeserializer implements JsonDeserializer new RaceLogDenoteForTrackingEventDeserializer(competitorDeserializer, domainFactory), new RaceLogStartTrackingEventDeserializer(competitorDeserializer), new RaceLogRevokeEventDeserializer(competitorDeserializer), - new RaceLogRegisterCompetitorEventDeserializer(competitorDeserializer), + new RaceLogRegisterCompetitorEventDeserializer(competitorDeserializer, competitorWithBoatDeserializer, BoatJsonDeserializer.create(domainFactory)), new RaceLogAdditionalScoringInformationEventDeserializer(competitorDeserializer), new RaceLogFixedMarkPassingEventDeserializer(competitorDeserializer), new RaceLogSuppressedMarkPassingsEventDeserializer(competitorDeserializer), diff --git a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/racelog/impl/RaceLogRegisterCompetitorEventDeserializer.java b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/racelog/impl/RaceLogRegisterCompetitorEventDeserializer.java index 6272c1e5dbb..ede21992220 100644 --- a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/racelog/impl/RaceLogRegisterCompetitorEventDeserializer.java +++ b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/racelog/impl/RaceLogRegisterCompetitorEventDeserializer.java @@ -8,20 +8,40 @@ import org.json.simple.JSONObject; import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor; import com.sap.sailing.domain.abstractlog.race.RaceLogEvent; import com.sap.sailing.domain.abstractlog.race.tracking.impl.RaceLogRegisterCompetitorEventImpl; +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.server.gateway.deserialization.JsonDeserializationException; import com.sap.sailing.server.gateway.deserialization.JsonDeserializer; +import com.sap.sailing.server.gateway.deserialization.impl.BoatJsonDeserializer; +import com.sap.sailing.server.gateway.serialization.racelog.impl.RaceLogRegisterCompetitorEventSerializer; import com.sap.sse.common.TimePoint; public class RaceLogRegisterCompetitorEventDeserializer extends BaseRaceLogEventDeserializer { - - public RaceLogRegisterCompetitorEventDeserializer(JsonDeserializer competitorDeserializer) { - super(competitorDeserializer); - } + private final JsonDeserializer competitorWithBoatDeserializer; + private final BoatJsonDeserializer boatDeserializer; + public RaceLogRegisterCompetitorEventDeserializer(JsonDeserializer competitorDeserializer, + JsonDeserializer competitorWithBoatDeserializer, BoatJsonDeserializer boatDeserializer) { + super(competitorDeserializer); + this.competitorWithBoatDeserializer = competitorWithBoatDeserializer; + this.boatDeserializer = boatDeserializer; + } + @Override - protected RaceLogEvent deserialize(JSONObject object, Serializable id, TimePoint createdAt, AbstractLogEventAuthor author, TimePoint timePoint, int passId, List competitors) { + protected RaceLogEvent deserialize(JSONObject object, Serializable id, TimePoint createdAt, AbstractLogEventAuthor author, TimePoint timePoint, int passId, List competitors) throws JsonDeserializationException { assert competitors.size() == 1 : "Expected exactly one competitor for RegisterCompetitorEvent"; - return new RaceLogRegisterCompetitorEventImpl(createdAt, timePoint, author, id, passId, competitors.get(0)); + JSONObject competitorWithBoatObject = (JSONObject) object.get(RaceLogRegisterCompetitorEventSerializer.FIELD_COMPETITOR_WITHBOAT); + if (competitorWithBoatObject != null) { + CompetitorWithBoat competitorWithBoat = competitorWithBoatDeserializer.deserialize(competitorWithBoatObject); + return new RaceLogRegisterCompetitorEventImpl(createdAt, timePoint, author, id, passId, competitorWithBoat); + } else { + JSONObject competitorObject = (JSONObject) object.get(RaceLogRegisterCompetitorEventSerializer.FIELD_COMPETITOR); + JSONObject boatObject = (JSONObject) object.get(RaceLogRegisterCompetitorEventSerializer.FIELD_BOAT); + Competitor competitor = competitorDeserializer.deserialize(competitorObject); + Boat boat = boatDeserializer.deserialize(boatObject); + return new RaceLogRegisterCompetitorEventImpl(createdAt, timePoint, author, id, passId, competitor, boat); + } } } diff --git a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/impl/BoatJsonSerializer.java b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/impl/BoatJsonSerializer.java index 39a866a2419..4cc0c63591e 100644 --- a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/impl/BoatJsonSerializer.java +++ b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/impl/BoatJsonSerializer.java @@ -1,15 +1,25 @@ package com.sap.sailing.server.gateway.serialization.impl; +import java.io.Serializable; +import java.util.Set; +import java.util.UUID; +import java.util.Map.Entry; + import org.json.simple.JSONObject; import com.sap.sailing.domain.base.Boat; import com.sap.sailing.domain.base.BoatClass; +import com.sap.sailing.domain.common.tracking.impl.CompetitorJsonConstants; import com.sap.sailing.server.gateway.serialization.JsonSerializer; +import com.sap.sse.common.Color; public class BoatJsonSerializer implements JsonSerializer { + public static final String FIELD_ID = "id"; + public static final String FIELD_ID_TYPE = "idtype"; public static final String FIELD_NAME = "name"; public static final String FIELD_SAIL_ID = "sailId"; public static final String FIELD_BOAT_CLASS = "boatClass"; + public static final String FIELD_COLOR = "color"; private final JsonSerializer boatClassJsonSerializer; @@ -21,15 +31,32 @@ public class BoatJsonSerializer implements JsonSerializer { this.boatClassJsonSerializer = boatClassJsonSerializer; } + public static JSONObject getBoatIdQuery(Boat boat) { + JSONObject result = new JSONObject(); + Serializable competitorId = boat.getId() instanceof UUID ? boat.getId().toString() : boat.getId(); + result.put(FIELD_ID, competitorId); + return result; + } + @Override public JSONObject serialize(Boat boat) { JSONObject result = new JSONObject(); + // Special treatment for UUIDs. They are represented as String because JSON doesn't have a way to represent them otherwise. + // However, other, e.g., numeric, types used to encode a serializable ID must be preserved according to JSON semantics. + // Also see the corresponding case distinction in the deserialized which first tries to parse a string as a UUID before + // returning the ID as is. + result.put(CompetitorJsonConstants.FIELD_ID_TYPE, boat.getId().getClass().getName()); + Set> entries = getBoatIdQuery(boat).entrySet(); + for (Entry idKeyAndValue : entries) { + result.put(idKeyAndValue.getKey(), idKeyAndValue.getValue()); + } result.put(FIELD_NAME, boat.getName()); result.put(FIELD_SAIL_ID, boat.getSailID()); + Color color = boat.getColor(); + result.put(FIELD_COLOR, color != null ? color.getAsHtml() : null); if (boatClassJsonSerializer != null) { result.put(FIELD_BOAT_CLASS, boatClassJsonSerializer.serialize(boat.getBoatClass())); } - return result; } } diff --git a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/impl/CompetitorAndBoatJsonSerializer.java b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/impl/CompetitorAndBoatJsonSerializer.java new file mode 100644 index 00000000000..e0c7f3a2b8e --- /dev/null +++ b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/impl/CompetitorAndBoatJsonSerializer.java @@ -0,0 +1,41 @@ +package com.sap.sailing.server.gateway.serialization.impl; + +import org.json.simple.JSONObject; + +import com.sap.sailing.domain.base.Boat; +import com.sap.sailing.domain.base.Competitor; +import com.sap.sailing.server.gateway.serialization.JsonSerializer; +import com.sap.sse.common.Util.Pair; + +/** + * Serializes a competitor AND a boat as a kind of pair (e.g. in the context of a race) + */ +public class CompetitorAndBoatJsonSerializer implements JsonSerializer> { + private final JsonSerializer competitorJsonSerializer; + private final JsonSerializer boatJsonSerializer; + + public static final String FIELD_BOAT = "boat"; + public static final String FIELD_COMPETITOR = "competitor"; + + public static CompetitorAndBoatJsonSerializer create() { + return new CompetitorAndBoatJsonSerializer(CompetitorJsonSerializer.create(), BoatJsonSerializer.create()); + } + + public CompetitorAndBoatJsonSerializer(JsonSerializer competitorJsonSerializer, JsonSerializer boatJsonSerializer) { + this.competitorJsonSerializer = competitorJsonSerializer; + this.boatJsonSerializer = boatJsonSerializer; + } + + @Override + public JSONObject serialize(Pair competitorAndBoat) { + JSONObject serializedCompetitorAndBoat = new JSONObject(); + + JSONObject serializedCompetitor = competitorJsonSerializer.serialize(competitorAndBoat.getA()); + JSONObject serializedBoat = boatJsonSerializer.serialize(competitorAndBoat.getB()); + + serializedCompetitorAndBoat.put(FIELD_COMPETITOR, serializedCompetitor); + serializedCompetitorAndBoat.put(FIELD_BOAT, serializedBoat); + + return serializedCompetitorAndBoat; + } +} diff --git a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/impl/CompetitorJsonSerializer.java b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/impl/CompetitorJsonSerializer.java index d1c7cacb58c..f907d6ddcf2 100644 --- a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/impl/CompetitorJsonSerializer.java +++ b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/impl/CompetitorJsonSerializer.java @@ -7,7 +7,6 @@ import java.util.UUID; import org.json.simple.JSONObject; -import com.sap.sailing.domain.base.Boat; import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.Nationality; import com.sap.sailing.domain.base.Team; @@ -20,24 +19,23 @@ public class CompetitorJsonSerializer implements JsonSerializer { public static final String FIELD_ID = "id"; private final JsonSerializer teamJsonSerializer; - private final JsonSerializer boatJsonSerializer; public static CompetitorJsonSerializer create() { - return new CompetitorJsonSerializer(TeamJsonSerializer.create(), BoatJsonSerializer.create()); + return new CompetitorJsonSerializer(TeamJsonSerializer.create()); } public CompetitorJsonSerializer() { - this(null, null); + this(null); } - public CompetitorJsonSerializer(JsonSerializer teamJsonSerializer, JsonSerializer teamBoatSerializer) { + public CompetitorJsonSerializer(JsonSerializer teamJsonSerializer) { this.teamJsonSerializer = teamJsonSerializer; - this.boatJsonSerializer = teamBoatSerializer; } public static JSONObject getCompetitorIdQuery(Competitor competitor) { JSONObject result = new JSONObject(); - result.put(FIELD_ID, getPersistentCompetitorId(competitor)); + Serializable competitorId = competitor.getId() instanceof UUID ? competitor.getId().toString() : competitor.getId(); + result.put(FIELD_ID, competitorId); return result; } @@ -54,11 +52,11 @@ public class CompetitorJsonSerializer implements JsonSerializer { result.put(idKeyAndValue.getKey(), idKeyAndValue.getValue()); } result.put(CompetitorJsonConstants.FIELD_NAME, competitor.getName()); + result.put(CompetitorJsonConstants.FIELD_SHORT_NAME, competitor.getShortName()); Color color = getColor(competitor); result.put(CompetitorJsonConstants.FIELD_DISPLAY_COLOR, color == null ? null : color.getAsHtml()); result.put(CompetitorJsonConstants.FIELD_EMAIL, competitor.getEmail()); result.put(CompetitorJsonConstants.FIELD_SEARCHTAG, competitor.getSearchTag()); - result.put(CompetitorJsonConstants.FIELD_SAIL_ID, competitor.getBoat() == null ? "" : competitor.getBoat().getSailID()); final Nationality nationality = competitor.getTeam() == null ? null : competitor.getTeam().getNationality(); result.put(CompetitorJsonConstants.FIELD_NATIONALITY, nationality == null ? "" : nationality.getThreeLetterIOCAcronym()); CountryCode countryCode = nationality == null ? null : nationality.getCountryCode(); @@ -70,9 +68,6 @@ public class CompetitorJsonSerializer implements JsonSerializer { if (teamJsonSerializer != null) { result.put(CompetitorJsonConstants.FIELD_TEAM, teamJsonSerializer.serialize(competitor.getTeam())); } - if (boatJsonSerializer != null) { - result.put(CompetitorJsonConstants.FIELD_BOAT, boatJsonSerializer.serialize(getBoat(competitor))); - } result.put(CompetitorJsonConstants.FIELD_TIME_ON_TIME_FACTOR, competitor.getTimeOnTimeFactor()); result.put(CompetitorJsonConstants.FIELD_TIME_ON_DISTANCE_ALLOWANCE_IN_SECONDS_PER_NAUTICAL_MILE, competitor.getTimeOnDistanceAllowancePerNauticalMile() == null ? null : @@ -80,17 +75,7 @@ public class CompetitorJsonSerializer implements JsonSerializer { return result; } - private static Serializable getPersistentCompetitorId(Competitor competitor) { - Serializable competitorId = competitor.getId() instanceof UUID ? competitor.getId().toString() : competitor.getId(); - return competitorId; - } - protected Color getColor(Competitor competitor ) { return competitor.getColor(); } - - protected Boat getBoat(Competitor competitor ) { - return competitor.getBoat(); - } - } diff --git a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/impl/CompetitorWithBoatJsonSerializer.java b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/impl/CompetitorWithBoatJsonSerializer.java new file mode 100644 index 00000000000..5750b0d9ab2 --- /dev/null +++ b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/impl/CompetitorWithBoatJsonSerializer.java @@ -0,0 +1,33 @@ +package com.sap.sailing.server.gateway.serialization.impl; + +import org.json.simple.JSONObject; + +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.common.tracking.impl.CompetitorJsonConstants; +import com.sap.sailing.server.gateway.serialization.JsonSerializer; + +public class CompetitorWithBoatJsonSerializer implements JsonSerializer { + private final JsonSerializer boatJsonSerializer; + private final JsonSerializer competitorJsonSerializer; + + public static CompetitorWithBoatJsonSerializer create() { + return new CompetitorWithBoatJsonSerializer(CompetitorJsonSerializer.create(), BoatJsonSerializer.create()); + } + + public CompetitorWithBoatJsonSerializer(JsonSerializer competitorJsonSerializer, JsonSerializer boatJsonSerializer) { + this.competitorJsonSerializer = competitorJsonSerializer; + this.boatJsonSerializer = boatJsonSerializer; + } + + @Override + public JSONObject serialize(CompetitorWithBoat competitor) { + JSONObject serializedCompetitor = competitorJsonSerializer.serialize(competitor); + serializedCompetitor.put(CompetitorJsonConstants.FIELD_SAIL_ID, competitor.getBoat() == null ? "" : competitor.getBoat().getSailID()); + if (boatJsonSerializer != null && competitor.getBoat() != null) { + serializedCompetitor.put(CompetitorJsonConstants.FIELD_BOAT, boatJsonSerializer.serialize(competitor.getBoat())); + } + return serializedCompetitor; + } +} diff --git a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/impl/CompetitorWithBoatRefJsonSerializer.java b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/impl/CompetitorWithBoatRefJsonSerializer.java new file mode 100644 index 00000000000..375c45008c5 --- /dev/null +++ b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/impl/CompetitorWithBoatRefJsonSerializer.java @@ -0,0 +1,33 @@ +package com.sap.sailing.server.gateway.serialization.impl; + +import org.json.simple.JSONObject; + +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.common.tracking.impl.CompetitorJsonConstants; +import com.sap.sailing.server.gateway.serialization.JsonSerializer; + +public class CompetitorWithBoatRefJsonSerializer implements JsonSerializer { + private final JsonSerializer competitorJsonSerializer; + + public static CompetitorWithBoatRefJsonSerializer create() { + return new CompetitorWithBoatRefJsonSerializer(CompetitorJsonSerializer.create()); + } + + public CompetitorWithBoatRefJsonSerializer(JsonSerializer competitorJsonSerializer) { + this.competitorJsonSerializer = competitorJsonSerializer; + } + + @Override + public JSONObject serialize(CompetitorWithBoat competitor) { + JSONObject serializedCompetitor = competitorJsonSerializer.serialize(competitor); + + Boat boat = competitor.getBoat(); + if (boat != null) { + serializedCompetitor.put(CompetitorJsonConstants.FIELD_BOAT_ID_TYPE, boat.getId().getClass().getName()); + serializedCompetitor.put(CompetitorJsonConstants.FIELD_BOAT_ID, boat.getId().toString()); + } + return serializedCompetitor; + } +} diff --git a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/racegroup/impl/RaceGroupJsonSerializer.java b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/racegroup/impl/RaceGroupJsonSerializer.java index e93cabfb901..2ecc544e4d8 100644 --- a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/racegroup/impl/RaceGroupJsonSerializer.java +++ b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/racegroup/impl/RaceGroupJsonSerializer.java @@ -15,7 +15,8 @@ public class RaceGroupJsonSerializer extends ExtendableJsonSerializer public static final String FIELD_COURSE_AREA = "courseArea"; public static final String FIELD_BOAT_CLASS = "boatClass"; public static final String FIELD_REGATTA_CONFIGURATION = "procedures"; - public static final Object FIELD_DISPLAY_NAME = "displayName"; + public static final String FIELD_DISPLAY_NAME = "displayName"; + public static final String FIELD_CAN_BOATS_OF_COMPETITORS_CHANGE_PER_RACE = "canBoatsOfCompetitorsChangePerRace"; private final JsonSerializer boatClassSerializer; private final JsonSerializer courseAreaSerializer; @@ -47,6 +48,7 @@ public class RaceGroupJsonSerializer extends ExtendableJsonSerializer configurationSerializer.serialize(object.getRegattaConfiguration())); } result.put(FIELD_DISPLAY_NAME, object.getDisplayName()); + result.put(FIELD_CAN_BOATS_OF_COMPETITORS_CHANGE_PER_RACE, object.canBoatsOfCompetitorsChangePerRace()); return result; } diff --git a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/racelog/impl/BaseRaceLogEventSerializer.java b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/racelog/impl/BaseRaceLogEventSerializer.java index 04b0f436ecb..6ac4fb4accd 100644 --- a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/racelog/impl/BaseRaceLogEventSerializer.java +++ b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/racelog/impl/BaseRaceLogEventSerializer.java @@ -37,7 +37,7 @@ public abstract class BaseRaceLogEventSerializer implements JsonSerializer, Rac new RaceLogDenoteForTrackingEventSerializer(competitorSerializer), new RaceLogStartTrackingEventSerializer(competitorSerializer), new RaceLogRevokeEventSerializer(competitorSerializer), - new RaceLogRegisterCompetitorEventSerializer(competitorSerializer), + new RaceLogRegisterCompetitorEventSerializer(competitorSerializer, CompetitorWithBoatJsonSerializer.create(), BoatJsonSerializer.create()), new RaceLogAdditionalScoringInformationSerializer(competitorSerializer), new RaceLogFixedMarkPassingEventSerializer(competitorSerializer), new RaceLogSuppressedMarkPassingsEventSerializer(competitorSerializer), @@ -248,11 +250,12 @@ public class RaceLogEventSerializer implements JsonSerializer, Rac public void visit(RaceLogRevokeEvent event) { chosenSerializer = revokeSerializer; } + @Override public void visit(RaceLogRegisterCompetitorEvent event) { chosenSerializer = registerCompetitorSerializer; } - + @Override public void visit(RaceLogAdditionalScoringInformationEvent additionalScoringInformation) { chosenSerializer = additionalScoringInformationSerializer; diff --git a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/racelog/impl/RaceLogRegisterCompetitorEventSerializer.java b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/racelog/impl/RaceLogRegisterCompetitorEventSerializer.java index 66d90a0528d..ea62ed472d6 100644 --- a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/racelog/impl/RaceLogRegisterCompetitorEventSerializer.java +++ b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/racelog/impl/RaceLogRegisterCompetitorEventSerializer.java @@ -1,20 +1,50 @@ package com.sap.sailing.server.gateway.serialization.racelog.impl; +import org.json.simple.JSONObject; + +import com.sap.sailing.domain.abstractlog.race.RaceLogEvent; +import com.sap.sailing.domain.abstractlog.race.tracking.RaceLogRegisterCompetitorEvent; import com.sap.sailing.domain.abstractlog.shared.events.RegisterCompetitorEvent; import com.sap.sailing.domain.base.Competitor; +import com.sap.sailing.domain.base.CompetitorWithBoat; 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.CompetitorWithBoatJsonSerializer; public class RaceLogRegisterCompetitorEventSerializer extends BaseRaceLogEventSerializer { - public static final String VALUE_CLASS = RegisterCompetitorEvent.class.getSimpleName(); + public static final String FIELD_COMPETITOR_WITHBOAT = "competitorWithBoat"; + public static final String FIELD_COMPETITOR = "competitor"; + public static final String FIELD_BOAT = "boat"; - public RaceLogRegisterCompetitorEventSerializer( - JsonSerializer competitorSerializer) { + private final CompetitorWithBoatJsonSerializer competitorWithBoatSerializer; + private final BoatJsonSerializer boatSerializer; + + public RaceLogRegisterCompetitorEventSerializer(JsonSerializer competitorSerializer, + CompetitorWithBoatJsonSerializer competitorWithBoatSerializer, BoatJsonSerializer boatSerializer) { super(competitorSerializer); + this.competitorWithBoatSerializer = competitorWithBoatSerializer; + this.boatSerializer = boatSerializer; } @Override protected String getClassFieldValue() { return VALUE_CLASS; } + + @Override + public JSONObject serialize(RaceLogEvent object) { + RaceLogRegisterCompetitorEvent registerCompetitorEvent = (RaceLogRegisterCompetitorEvent) object; + + JSONObject result = super.serialize(registerCompetitorEvent); + if (registerCompetitorEvent.getCompetitor().hasBoat()) { + CompetitorWithBoat competitorWithBoat = (CompetitorWithBoat) registerCompetitorEvent.getCompetitor(); + result.put(FIELD_COMPETITOR_WITHBOAT, competitorWithBoatSerializer.serialize(competitorWithBoat)); + } else { + result.put(FIELD_COMPETITOR, competitorSerializer.serialize(registerCompetitorEvent.getCompetitor())); + result.put(FIELD_BOAT, boatSerializer.serialize(registerCompetitorEvent.getBoat())); + } + + return result; + } } diff --git a/java/com.sap.sailing.server.gateway.serialization.test/src/com/sap/sailing/server/gateway/serialization/test/racelog/AbstractEventSerializerTest.java b/java/com.sap.sailing.server.gateway.serialization.test/src/com/sap/sailing/server/gateway/serialization/test/racelog/AbstractEventSerializerTest.java index 1f9d5c80c20..9d3b6f72e38 100644 --- a/java/com.sap.sailing.server.gateway.serialization.test/src/com/sap/sailing/server/gateway/serialization/test/racelog/AbstractEventSerializerTest.java +++ b/java/com.sap.sailing.server.gateway.serialization.test/src/com/sap/sailing/server/gateway/serialization/test/racelog/AbstractEventSerializerTest.java @@ -54,7 +54,7 @@ public abstract class AbstractEventSerializerTest emptyList()); + when(event.getInvolvedCompetitors()).thenReturn(Collections. emptyList()); when(event.getAuthor()).thenReturn(author); } diff --git a/java/com.sap.sailing.server.gateway.serialization.test/src/com/sap/sailing/server/gateway/serialization/test/racelog/RaceLogCourseDesignChangedEventSerializerTest.java b/java/com.sap.sailing.server.gateway.serialization.test/src/com/sap/sailing/server/gateway/serialization/test/racelog/RaceLogCourseDesignChangedEventSerializerTest.java index c0b8084f8a3..9e365d43ca4 100644 --- a/java/com.sap.sailing.server.gateway.serialization.test/src/com/sap/sailing/server/gateway/serialization/test/racelog/RaceLogCourseDesignChangedEventSerializerTest.java +++ b/java/com.sap.sailing.server.gateway.serialization.test/src/com/sap/sailing/server/gateway/serialization/test/racelog/RaceLogCourseDesignChangedEventSerializerTest.java @@ -4,8 +4,6 @@ import static org.junit.Assert.assertEquals; import java.util.UUID; -import junit.framework.Assert; - import org.json.simple.JSONObject; import org.junit.Before; import org.junit.Test; @@ -46,6 +44,8 @@ import com.sap.sse.common.Util; import com.sap.sse.common.impl.AbstractColor; import com.sap.sse.common.impl.MillisecondsTimePoint; +import junit.framework.Assert; + public class RaceLogCourseDesignChangedEventSerializerTest { private RaceLogCourseDesignChangedEventSerializer serializer; @@ -78,8 +78,8 @@ public class RaceLogCourseDesignChangedEventSerializerTest { assertEquals(event.getPassId(), deserializedEvent.getPassId()); assertEquals(event.getLogicalTimePoint(), deserializedEvent.getLogicalTimePoint()); assertEquals(event.getCourseDesignerMode(), deserializedEvent.getCourseDesignerMode()); - assertEquals(0, Util.size(event.getInvolvedBoats())); - assertEquals(0, Util.size(deserializedEvent.getInvolvedBoats())); + assertEquals(0, Util.size(event.getInvolvedCompetitors())); + assertEquals(0, Util.size(deserializedEvent.getInvolvedCompetitors())); compareCourseData(event.getCourseDesign(), deserializedEvent.getCourseDesign()); } diff --git a/java/com.sap.sailing.server.gateway.serialization.test/src/com/sap/sailing/server/gateway/serialization/test/racelog/RaceLogEventSerializerTest.java b/java/com.sap.sailing.server.gateway.serialization.test/src/com/sap/sailing/server/gateway/serialization/test/racelog/RaceLogEventSerializerTest.java index ff8b0983862..078923639e8 100644 --- a/java/com.sap.sailing.server.gateway.serialization.test/src/com/sap/sailing/server/gateway/serialization/test/racelog/RaceLogEventSerializerTest.java +++ b/java/com.sap.sailing.server.gateway.serialization.test/src/com/sap/sailing/server/gateway/serialization/test/racelog/RaceLogEventSerializerTest.java @@ -27,7 +27,13 @@ import com.sap.sailing.domain.abstractlog.race.impl.RaceLogWindFixEventImpl; import com.sap.sailing.domain.abstractlog.race.tracking.impl.RaceLogDenoteForTrackingEventImpl; import com.sap.sailing.domain.abstractlog.race.tracking.impl.RaceLogRegisterCompetitorEventImpl; import com.sap.sailing.domain.abstractlog.race.tracking.impl.RaceLogStartTrackingEventImpl; +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.DomainFactory; +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.common.CourseDesignerMode; import com.sap.sailing.domain.common.racelog.RaceLogRaceStatus; import com.sap.sailing.domain.common.racelog.RacingProcedureType; @@ -99,9 +105,10 @@ public class RaceLogEventSerializerTest { pathfinderEventSerializer, gateLineOpeningTimeEventSerializer, startProcedureTypeChangedEventSerializer, protestStartTimeEventSerializer, windFixEventSerializer, denoteForTrackingEventSerializer, startTrackingEventSerializer, revokeEventSerializer, - registerCompetitorEventSerializer, fixedMarkPassingEventSerializer, suppressedMarkPassingsSerializer, + registerCompetitorEventSerializer, + fixedMarkPassingEventSerializer, suppressedMarkPassingsSerializer, additionalScoringInformationSerializer, dependentStartTimeEventSerializer, - startOfTrackingEventSerializer, useCompetitorsFromRaceLogEventSerializer, + startOfTrackingEventSerializer, useCompetitorsFromRaceLogEventSerializer, endOfTrackingEventSerializer); } @@ -229,11 +236,17 @@ public class RaceLogEventSerializerTest { @Test public void testRegisterCompetitorEventSerializer() { // we use the real event type here because we do not want to re-implement the dispatching. - RaceLogEvent event = new RaceLogRegisterCompetitorEventImpl(null, author, 0, - DomainFactory.INSTANCE - .getOrCreateCompetitor("comp", "comp", null, null, null, null, null, /* timeOnTimeFactor */null, /* timeOnDistanceAllowancePerNauticalMile */ - null, null)); + Competitor c = DomainFactory.INSTANCE.getOrCreateCompetitor("comp", "comp", "c", null, null, null, null, + /* timeOnTimeFactor */null, /* timeOnDistanceAllowancePerNauticalMile */ null, null); + Boat b = DomainFactory.INSTANCE.getOrCreateBoat("boat", "b", new BoatClassImpl("505", /* typicallyStartsUpwind */ true), null, null); + RaceLogEvent event = new RaceLogRegisterCompetitorEventImpl(null, author, 0, c, b); serializer.serialize(event); verify(registerCompetitorEventSerializer).serialize(event); + Boat b2 = new BoatImpl("boat", "b", new BoatClassImpl("505", /* typicallyStartsUpwind */ true), null, null); + CompetitorWithBoat c2 = DomainFactory.INSTANCE.getOrCreateCompetitorWithBoat("comp2", "comp", "c", null, null, + null, null, /* timeOnTimeFactor */null, /* timeOnDistanceAllowancePerNauticalMile */ null, null, (DynamicBoat) b2); + RaceLogEvent event2 = new RaceLogRegisterCompetitorEventImpl(null, author, 0, c2, b2); + serializer.serialize(event2); + verify(registerCompetitorEventSerializer).serialize(event2); } } diff --git a/java/com.sap.sailing.server.gateway.serialization.test/src/com/sap/sailing/server/gateway/serialization/test/racelog/RaceLogFinishPositioningConfirmedEventSerializerTest.java b/java/com.sap.sailing.server.gateway.serialization.test/src/com/sap/sailing/server/gateway/serialization/test/racelog/RaceLogFinishPositioningConfirmedEventSerializerTest.java index 08541b3796a..6998cda3509 100644 --- a/java/com.sap.sailing.server.gateway.serialization.test/src/com/sap/sailing/server/gateway/serialization/test/racelog/RaceLogFinishPositioningConfirmedEventSerializerTest.java +++ b/java/com.sap.sailing.server.gateway.serialization.test/src/com/sap/sailing/server/gateway/serialization/test/racelog/RaceLogFinishPositioningConfirmedEventSerializerTest.java @@ -49,8 +49,8 @@ public class RaceLogFinishPositioningConfirmedEventSerializerTest { public void setUp() { SharedDomainFactory factory = DomainFactory.INSTANCE; serializer = new RaceLogFinishPositioningConfirmedEventSerializer(new CompetitorJsonSerializer( - new TeamJsonSerializer(new PersonJsonSerializer(new NationalityJsonSerializer())), null)); - deserializer = new RaceLogFinishPositioningConfirmedEventDeserializer(new CompetitorJsonDeserializer(factory.getCompetitorStore(), null, /* boatDeserializer */ null)); + new TeamJsonSerializer(new PersonJsonSerializer(new NationalityJsonSerializer())))); + deserializer = new RaceLogFinishPositioningConfirmedEventDeserializer(new CompetitorJsonDeserializer(factory.getCompetitorStore(), null)); now = MillisecondsTimePoint.now(); positioningList = new CompetitorResultsImpl(); @@ -77,8 +77,8 @@ public class RaceLogFinishPositioningConfirmedEventSerializerTest { assertEquals(event.getId(), deserializedEvent.getId()); assertEquals(event.getPassId(), deserializedEvent.getPassId()); assertEquals(event.getLogicalTimePoint(), deserializedEvent.getLogicalTimePoint()); - assertEquals(0, Util.size(event.getInvolvedBoats())); - assertEquals(0, Util.size(deserializedEvent.getInvolvedBoats())); + assertEquals(0, Util.size(event.getInvolvedCompetitors())); + assertEquals(0, Util.size(deserializedEvent.getInvolvedCompetitors())); assertNotNull(event.getPositionedCompetitorsIDsNamesMaxPointsReasons()); assertNotNull(deserializedEvent.getPositionedCompetitorsIDsNamesMaxPointsReasons()); assertEquals(1, Util.size(event.getPositionedCompetitorsIDsNamesMaxPointsReasons())); @@ -96,8 +96,8 @@ public class RaceLogFinishPositioningConfirmedEventSerializerTest { assertEquals(event.getId(), deserializedEvent.getId()); assertEquals(event.getPassId(), deserializedEvent.getPassId()); assertEquals(event.getLogicalTimePoint(), deserializedEvent.getLogicalTimePoint()); - assertEquals(0, Util.size(event.getInvolvedBoats())); - assertEquals(0, Util.size(deserializedEvent.getInvolvedBoats())); + assertEquals(0, Util.size(event.getInvolvedCompetitors())); + assertEquals(0, Util.size(deserializedEvent.getInvolvedCompetitors())); assertNull(event.getPositionedCompetitorsIDsNamesMaxPointsReasons()); assertNotNull(deserializedEvent.getPositionedCompetitorsIDsNamesMaxPointsReasons()); assertTrue(Util.isEmpty(deserializedEvent.getPositionedCompetitorsIDsNamesMaxPointsReasons())); @@ -114,8 +114,8 @@ public class RaceLogFinishPositioningConfirmedEventSerializerTest { assertEquals(event.getId(), deserializedEvent.getId()); assertEquals(event.getPassId(), deserializedEvent.getPassId()); assertEquals(event.getLogicalTimePoint(), deserializedEvent.getLogicalTimePoint()); - assertEquals(0, Util.size(event.getInvolvedBoats())); - assertEquals(0, Util.size(deserializedEvent.getInvolvedBoats())); + assertEquals(0, Util.size(event.getInvolvedCompetitors())); + assertEquals(0, Util.size(deserializedEvent.getInvolvedCompetitors())); assertNotNull(event.getPositionedCompetitorsIDsNamesMaxPointsReasons()); assertNotNull(deserializedEvent.getPositionedCompetitorsIDsNamesMaxPointsReasons()); assertTrue(Util.isEmpty(event.getPositionedCompetitorsIDsNamesMaxPointsReasons())); diff --git a/java/com.sap.sailing.server.gateway.serialization.test/src/com/sap/sailing/server/gateway/serialization/test/racelog/RaceLogWindFixEventSerializerTest.java b/java/com.sap.sailing.server.gateway.serialization.test/src/com/sap/sailing/server/gateway/serialization/test/racelog/RaceLogWindFixEventSerializerTest.java index adabeedbc05..6b2e7f8abc4 100644 --- a/java/com.sap.sailing.server.gateway.serialization.test/src/com/sap/sailing/server/gateway/serialization/test/racelog/RaceLogWindFixEventSerializerTest.java +++ b/java/com.sap.sailing.server.gateway.serialization.test/src/com/sap/sailing/server/gateway/serialization/test/racelog/RaceLogWindFixEventSerializerTest.java @@ -49,9 +49,9 @@ public class RaceLogWindFixEventSerializerTest { public void setUp() { SharedDomainFactory factory = DomainFactory.INSTANCE; serializer = new RaceLogWindFixEventSerializer(new CompetitorJsonSerializer(new TeamJsonSerializer( - new PersonJsonSerializer(new NationalityJsonSerializer())), null), new WindJsonSerializer( + new PersonJsonSerializer(new NationalityJsonSerializer()))), new WindJsonSerializer( new PositionJsonSerializer())); - deserializer = new RaceLogWindFixEventDeserializer(new CompetitorJsonDeserializer(factory.getCompetitorStore(), null, /* boatDeserializer */ null), + deserializer = new RaceLogWindFixEventDeserializer(new CompetitorJsonDeserializer(factory.getCompetitorStore(), null), new WindJsonDeserializer(new PositionJsonDeserializer())); now = MillisecondsTimePoint.now(); @@ -67,8 +67,8 @@ public class RaceLogWindFixEventSerializerTest { assertEquals(event.getId(), deserializedEvent.getId()); assertEquals(event.getPassId(), deserializedEvent.getPassId()); assertEquals(event.getLogicalTimePoint(), deserializedEvent.getLogicalTimePoint()); - assertEquals(0, Util.size(event.getInvolvedBoats())); - assertEquals(0, Util.size(deserializedEvent.getInvolvedBoats())); + assertEquals(0, Util.size(event.getInvolvedCompetitors())); + assertEquals(0, Util.size(deserializedEvent.getInvolvedCompetitors())); compareWind(event.getWindFix(), deserializedEvent.getWindFix()); } diff --git a/java/com.sap.sailing.server.gateway.serialization.test/src/com/sap/sailing/server/gateway/serialization/test/racelog/TrackingTimesEventSerializerTest.java b/java/com.sap.sailing.server.gateway.serialization.test/src/com/sap/sailing/server/gateway/serialization/test/racelog/TrackingTimesEventSerializerTest.java index d29585148ed..a384bd5d4f2 100644 --- a/java/com.sap.sailing.server.gateway.serialization.test/src/com/sap/sailing/server/gateway/serialization/test/racelog/TrackingTimesEventSerializerTest.java +++ b/java/com.sap.sailing.server.gateway.serialization.test/src/com/sap/sailing/server/gateway/serialization/test/racelog/TrackingTimesEventSerializerTest.java @@ -20,8 +20,6 @@ import com.sap.sailing.server.gateway.deserialization.impl.CompetitorJsonDeseria import com.sap.sailing.server.gateway.deserialization.racelog.impl.RaceLogEndOfTrackingEventDeserializer; import com.sap.sailing.server.gateway.deserialization.racelog.impl.RaceLogStartOfTrackingEventDeserializer; import com.sap.sailing.server.gateway.serialization.JsonSerializer; -import com.sap.sailing.server.gateway.serialization.impl.BoatClassJsonSerializer; -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.NationalityJsonSerializer; import com.sap.sailing.server.gateway.serialization.impl.PersonJsonSerializer; @@ -54,8 +52,7 @@ public class TrackingTimesEventSerializerTest { private CompetitorJsonSerializer createCompetitorSerializer() { return new CompetitorJsonSerializer(new TeamJsonSerializer( - new PersonJsonSerializer(new NationalityJsonSerializer())), new BoatJsonSerializer( - new BoatClassJsonSerializer())); + new PersonJsonSerializer(new NationalityJsonSerializer()))); } @Test diff --git a/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/CompetitorWithChangingBoatJsonSerializer.java b/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/CompetitorWithChangingBoatJsonSerializer.java deleted file mode 100644 index eaf61dd60b3..00000000000 --- a/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/CompetitorWithChangingBoatJsonSerializer.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.sap.sailing.server.gateway.serialization.impl; - -import com.sap.sailing.domain.base.Boat; -import com.sap.sailing.domain.base.Competitor; -import com.sap.sailing.domain.base.Team; -import com.sap.sailing.server.gateway.serialization.JsonSerializer; -import com.sap.sailing.domain.base.RaceDefinition; -import com.sap.sse.common.Color; - -/** - * This is a workaround for the serialization of a competitor with changing boats. - * The class should be removed after bug https://bugzilla.sapsailing.com/bugzilla/show_bug.cgi?id=2822 is resolved. - * @author Frank - * - */ -public class CompetitorWithChangingBoatJsonSerializer extends CompetitorJsonSerializer { - private final RaceDefinition race; - - public CompetitorWithChangingBoatJsonSerializer() { - this(null, null, null); - } - - public CompetitorWithChangingBoatJsonSerializer(RaceDefinition race, JsonSerializer teamJsonSerializer, JsonSerializer boatSerializer) { - super(teamJsonSerializer, boatSerializer); - this.race = race; - } - - @Override - protected Color getColor(Competitor competitor) { - if (race != null) { - Boat boatOfCompetitor = race.getBoatOfCompetitor(competitor); - if (boatOfCompetitor != null) { - return boatOfCompetitor.getColor(); - } - } - return super.getColor(competitor); - } - - @Override - protected Boat getBoat(Competitor competitor) { - if (race != null) { - Boat boatOfCompetitor = race.getBoatOfCompetitor(competitor); - if (boatOfCompetitor != null) { - return boatOfCompetitor; - } - } - return super.getBoat(competitor); - } -} diff --git a/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/ManeuversJsonSerializer.java b/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/ManeuversJsonSerializer.java index 8259f3528cb..c4bb45d2071 100755 --- a/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/ManeuversJsonSerializer.java +++ b/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/ManeuversJsonSerializer.java @@ -1,9 +1,14 @@ package com.sap.sailing.server.gateway.serialization.impl; +import java.util.Map.Entry; + import org.json.simple.JSONArray; import org.json.simple.JSONObject; +import com.sap.sailing.domain.base.Boat; import com.sap.sailing.domain.base.Competitor; +import com.sap.sailing.domain.base.impl.CompetitorWithBoatImpl; +import com.sap.sailing.domain.base.impl.DynamicBoat; import com.sap.sailing.domain.tracking.Maneuver; import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sse.common.TimePoint; @@ -12,10 +17,10 @@ import com.sap.sse.common.impl.MillisecondsTimePoint; public class ManeuversJsonSerializer extends AbstractTrackedRaceDataJsonSerializer { public final static String MANEUVERS = "maneuvers"; - private final CompetitorJsonSerializer competitorSerializer; + private final CompetitorWithBoatJsonSerializer competitorSerializer; private final ManeuverJsonSerializer maneuverSerializer; - public ManeuversJsonSerializer(CompetitorJsonSerializer competitorSerializer, ManeuverJsonSerializer maneuverSerializer) { + public ManeuversJsonSerializer(CompetitorWithBoatJsonSerializer competitorSerializer, ManeuverJsonSerializer maneuverSerializer) { super(); this.competitorSerializer = competitorSerializer; this.maneuverSerializer = maneuverSerializer; @@ -26,10 +31,12 @@ public class ManeuversJsonSerializer extends AbstractTrackedRaceDataJsonSerializ final JSONObject result = new JSONObject(); JSONArray byCompetitorJson = new JSONArray(); result.put(BYCOMPETITOR, byCompetitorJson); - for (Competitor competitor : trackedRace.getRace().getCompetitors()) { + for (Entry competitorAndBoatEntry : trackedRace.getRace().getCompetitorsAndTheirBoats().entrySet()) { + Competitor competitor = competitorAndBoatEntry.getKey(); + Boat boat = competitorAndBoatEntry.getValue(); final JSONObject forCompetitorJson = new JSONObject(); byCompetitorJson.add(forCompetitorJson); - forCompetitorJson.put(COMPETITOR, competitorSerializer.serialize(competitor)); + forCompetitorJson.put(COMPETITOR, competitorSerializer.serialize(new CompetitorWithBoatImpl(competitor, (DynamicBoat) boat))); final JSONArray maneuvers = new JSONArray(); forCompetitorJson.put(MANEUVERS, maneuvers); for (final Maneuver maneuver : getManeuversDuringRace(trackedRace, competitor)) { diff --git a/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/ManeuversWithEstimationDataJsonSerializer.java b/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/ManeuversWithEstimationDataJsonSerializer.java index 33107144020..16cfc9cde54 100644 --- a/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/ManeuversWithEstimationDataJsonSerializer.java +++ b/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/ManeuversWithEstimationDataJsonSerializer.java @@ -40,7 +40,7 @@ public class ManeuversWithEstimationDataJsonSerializer extends AbstractTrackedRa final JSONObject forCompetitorJson = new JSONObject(); byCompetitorJson.add(forCompetitorJson); forCompetitorJson.put(COMPETITOR_NAME, competitor.getName()); - forCompetitorJson.put(BOAT_CLASS, boatClassJsonSerializer.serialize(competitor.getBoat().getBoatClass())); + forCompetitorJson.put(BOAT_CLASS, boatClassJsonSerializer.serialize(trackedRace.getRace().getBoatOfCompetitor(competitor).getBoatClass())); final JSONArray maneuvers = new JSONArray(); forCompetitorJson.put(MANEUVERS, maneuvers); for (final ManeuverWithEstimationData maneuver : getManeuversWithEstimationData(trackedRace, competitor)) { diff --git a/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/MarkPassingsJsonSerializer.java b/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/MarkPassingsJsonSerializer.java index 97ba39bbae6..7e656c4fcc3 100755 --- a/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/MarkPassingsJsonSerializer.java +++ b/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/MarkPassingsJsonSerializer.java @@ -1,14 +1,18 @@ package com.sap.sailing.server.gateway.serialization.impl; import java.text.SimpleDateFormat; +import java.util.Map.Entry; import java.util.NavigableSet; import org.json.simple.JSONArray; import org.json.simple.JSONObject; +import com.sap.sailing.domain.base.Boat; import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.Course; import com.sap.sailing.domain.base.Waypoint; +import com.sap.sailing.domain.base.impl.CompetitorWithBoatImpl; +import com.sap.sailing.domain.base.impl.DynamicBoat; import com.sap.sailing.domain.tracking.MarkPassing; import com.sap.sailing.domain.tracking.TrackedRace; @@ -23,13 +27,16 @@ public class MarkPassingsJsonSerializer extends AbstractTrackedRaceDataJsonSeria public JSONObject serialize(TrackedRace trackedRace) { final Course course = trackedRace.getRace().getCourse(); JSONObject result = new JSONObject(); - CompetitorJsonSerializer competitorSerializer = new CompetitorJsonSerializer(null, BoatJsonSerializer.create()); + CompetitorWithBoatJsonSerializer competitorWithBoatSerializer = CompetitorWithBoatJsonSerializer.create(); + CompetitorJsonSerializer competitorSerializer = CompetitorJsonSerializer.create(); JSONArray byCompetitorJson = new JSONArray(); result.put(BYCOMPETITOR, byCompetitorJson); - for (Competitor competitor : trackedRace.getRace().getCompetitors()) { + for (Entry competitorAndBoatEntry : trackedRace.getRace().getCompetitorsAndTheirBoats().entrySet()) { + Competitor competitor = competitorAndBoatEntry.getKey(); + Boat boat = competitorAndBoatEntry.getValue(); JSONObject forCompetitorJson = new JSONObject(); byCompetitorJson.add(forCompetitorJson); - forCompetitorJson.put(COMPETITOR, competitorSerializer.serialize(competitor)); + forCompetitorJson.put(COMPETITOR, competitorWithBoatSerializer.serialize(new CompetitorWithBoatImpl(competitor, (DynamicBoat) boat))); final NavigableSet markPassingsForCompetitor = trackedRace.getMarkPassings(competitor); JSONArray markPassingsForCompetitorJson = new JSONArray(); forCompetitorJson.put(MARKPASSINGS, markPassingsForCompetitorJson); diff --git a/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/RaceEntriesJsonSerializer.java b/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/RaceEntriesJsonSerializer.java index 37f9c7bc4eb..231fbec82a3 100644 --- a/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/RaceEntriesJsonSerializer.java +++ b/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/RaceEntriesJsonSerializer.java @@ -1,33 +1,40 @@ package com.sap.sailing.server.gateway.serialization.impl; +import java.util.Map.Entry; + import org.json.simple.JSONArray; import org.json.simple.JSONObject; +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.RaceDefinition; +import com.sap.sailing.domain.base.impl.CompetitorWithBoatImpl; +import com.sap.sailing.domain.base.impl.DynamicBoat; import com.sap.sailing.server.gateway.serialization.JsonSerializer; public class RaceEntriesJsonSerializer implements JsonSerializer { public static final String FIELD_NAME = "name"; public static final String FIELD_COMPETITORS = "competitors"; - private final JsonSerializer competitorSerializer; + private final JsonSerializer competitorWithBoatSerializer; public RaceEntriesJsonSerializer() { this(null); } - public RaceEntriesJsonSerializer(JsonSerializer competitorSerializer) { - this.competitorSerializer = competitorSerializer; + public RaceEntriesJsonSerializer(JsonSerializer competitorWithBoatSerializer) { + this.competitorWithBoatSerializer = competitorWithBoatSerializer; } public JSONObject serialize(RaceDefinition race) { JSONObject result = new JSONObject(); result.put(FIELD_NAME, race.getName()); - if(competitorSerializer != null) { + if(competitorWithBoatSerializer != null) { JSONArray competitorsJson = new JSONArray(); - for (Competitor competitor: race.getCompetitors()) { - competitorsJson.add(competitorSerializer.serialize(competitor)); + for (Entry competitorAndBoatEntry: race.getCompetitorsAndTheirBoats().entrySet()) { + competitorsJson.add(competitorWithBoatSerializer.serialize(new CompetitorWithBoatImpl(competitorAndBoatEntry.getKey(), + (DynamicBoat) competitorAndBoatEntry.getValue()))); } result.put(FIELD_COMPETITORS, competitorsJson); } diff --git a/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/test/AbstractJsonExportTest.java b/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/test/AbstractJsonExportTest.java index c808ee1d767..3444b7e43b8 100644 --- a/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/test/AbstractJsonExportTest.java +++ b/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/test/AbstractJsonExportTest.java @@ -19,10 +19,7 @@ import java.util.UUID; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import com.sap.sailing.domain.base.BoatClass; import com.sap.sailing.domain.base.Competitor; -import com.sap.sailing.domain.base.impl.BoatClassImpl; -import com.sap.sailing.domain.base.impl.BoatImpl; import com.sap.sailing.domain.base.impl.CompetitorImpl; import com.sap.sailing.domain.base.impl.NationalityImpl; import com.sap.sailing.domain.base.impl.PersonImpl; @@ -86,15 +83,14 @@ public abstract class AbstractJsonExportTest { protected List createCompetitors(int numberOfCompetitorsToCreate) { List result = new ArrayList(); - BoatClass boatClass = new BoatClassImpl("505", /* typicallyStartsUpwind */ true); for (int i = 1; i <= numberOfCompetitorsToCreate; i++) { String competitorName = "C" + i; - Competitor competitor = new CompetitorImpl(UUID.randomUUID(), competitorName, Color.RED, null, null, new TeamImpl("STG", Collections.singleton( + Competitor competitor = new CompetitorImpl(UUID.randomUUID(), competitorName, "KYC", Color.RED, null, null, new TeamImpl("STG", Collections.singleton( new PersonImpl(competitorName, new NationalityImpl("GER"), /* dateOfBirth */ null, "This is famous "+competitorName)), new PersonImpl("Rigo van Maas", new NationalityImpl("NED"), - /* dateOfBirth */null, "This is Rigo, the coach")), new BoatImpl(competitorName + "'s boat", - boatClass, null), /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null); + /* dateOfBirth */null, "This is Rigo, the coach")), + /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null); result.add(competitor); } return result; diff --git a/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/test/jaxrs/AbstractJaxRsApiTest.java b/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/test/jaxrs/AbstractJaxRsApiTest.java index 2b53d229cf1..a24f93ba4d2 100644 --- a/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/test/jaxrs/AbstractJaxRsApiTest.java +++ b/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/test/jaxrs/AbstractJaxRsApiTest.java @@ -13,10 +13,7 @@ import java.util.List; import javax.ws.rs.core.Response; -import com.sap.sailing.domain.base.BoatClass; import com.sap.sailing.domain.base.Competitor; -import com.sap.sailing.domain.base.impl.BoatClassImpl; -import com.sap.sailing.domain.base.impl.BoatImpl; import com.sap.sailing.domain.base.impl.CompetitorImpl; import com.sap.sailing.domain.base.impl.NationalityImpl; import com.sap.sailing.domain.base.impl.PersonImpl; @@ -74,15 +71,14 @@ public abstract class AbstractJaxRsApiTest { protected List createCompetitors(int numberOfCompetitorsToCreate) { List result = new ArrayList(); - BoatClass boatClass = new BoatClassImpl("505", /* typicallyStartsUpwind */ true); for (int i = 1; i <= numberOfCompetitorsToCreate; i++) { String competitorName = "C" + i; - Competitor competitor = new CompetitorImpl(new Integer(i), competitorName, Color.RED, null, null, new TeamImpl("STG", Collections.singleton( + Competitor competitor = new CompetitorImpl(new Integer(i), competitorName, "KYC", Color.RED, null, null, new TeamImpl("STG", Collections.singleton( new PersonImpl(competitorName, new NationalityImpl("GER"), /* dateOfBirth */ null, "This is famous "+competitorName)), new PersonImpl("Rigo van Maas", new NationalityImpl("NED"), - /* dateOfBirth */null, "This is Rigo, the coach")), new BoatImpl(competitorName + "'s boat", - boatClass, null), /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null); + /* dateOfBirth */null, "This is Rigo, the coach")), + /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null); result.add(competitor); } return result; diff --git a/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/test/jaxrs/BoatResourceTest.java b/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/test/jaxrs/BoatResourceTest.java new file mode 100644 index 00000000000..8e375dcc6b3 --- /dev/null +++ b/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/test/jaxrs/BoatResourceTest.java @@ -0,0 +1,46 @@ +package com.sap.sailing.server.gateway.test.jaxrs; + +import static org.junit.Assert.assertTrue; + +import org.json.simple.JSONObject; +import org.json.simple.JSONValue; +import org.junit.Before; +import org.junit.Test; + +import com.sap.sailing.domain.base.BoatClass; +import com.sap.sailing.domain.base.impl.BoatClassImpl; +import com.sap.sailing.domain.base.impl.DynamicBoat; +import com.sap.sailing.server.gateway.deserialization.impl.BoatJsonDeserializer; +import com.sap.sailing.server.gateway.deserialization.impl.Helpers; +import com.sap.sailing.server.gateway.jaxrs.api.BoatsResource; + +public class BoatResourceTest extends AbstractJaxRsApiTest { + private final String id = "af855a56-9726-4a9c-a77e-da955bd289be"; + private final String boatName = "My boat"; + private final String boatClassName = "49er"; + private final String sailId = "GER 1"; + private final BoatClass boatClass = new BoatClassImpl(boatClassName, true); + + @Before + public void setUp() throws Exception { + super.setUp(); + racingEventService.getBaseDomainFactory().getOrCreateBoat(id, boatName, boatClass, sailId, null); + } + + @Test + public void testGetBoatAsJson() throws Exception { + BoatsResource resource = spyResource(new BoatsResource()); + String jsonString = resource.getBoat(id).getEntity().toString(); + + BoatJsonDeserializer boatJsonDeserializer = BoatJsonDeserializer.create(racingEventService.getBaseDomainFactory()); + JSONObject jsonObject = Helpers.toJSONObjectSafe(JSONValue.parse(jsonString)); + DynamicBoat boat = boatJsonDeserializer.deserialize(jsonObject); + + assertTrue(boat.getId().equals(id)); + assertTrue(boat.getName().equals(boatName)); + assertTrue(boat.getSailID().equals(sailId)); + assertTrue(boat.getSailID().equals(sailId)); + assertTrue(boat.getBoatClass().getName().equals(boatClassName)); + } + +} diff --git a/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/test/jaxrs/CompetitorsResourceTeamImageTest.java b/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/test/jaxrs/CompetitorsResourceTeamImageTest.java index 98067313a97..fa62917c017 100644 --- a/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/test/jaxrs/CompetitorsResourceTeamImageTest.java +++ b/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/test/jaxrs/CompetitorsResourceTeamImageTest.java @@ -18,9 +18,6 @@ import org.json.simple.parser.ParseException; import org.junit.Before; import org.junit.Test; -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.DynamicTeam; import com.sap.sailing.domain.base.impl.NationalityImpl; import com.sap.sailing.domain.base.impl.PersonImpl; @@ -36,8 +33,6 @@ import com.sap.sse.filestorage.testsupport.AmazonS3TestSupport; public class CompetitorsResourceTeamImageTest extends AbstractJaxRsApiTest { private static final String name = "Heiko KRÖGER"; private static final String id = "af855a56-9726-4a9c-a77e-da955bd289be"; - private static final String boatClassName = "49er"; - private static final String sailID = "GER 1"; private static final String nationality = "GER"; private static final String teamImageFile = "Bundesliga2014_Regatta6_eventteaser.jpg"; @@ -53,8 +48,7 @@ public class CompetitorsResourceTeamImageTest extends AbstractJaxRsApiTest { doReturn(storageService).when(fsmsMock).getActiveFileStorageService(); DynamicTeam team = new TeamImpl(null, Collections.singleton(new PersonImpl(null, new NationalityImpl( nationality), null, null)), null); - DynamicBoat boat = new BoatImpl(null, new BoatClassImpl(boatClassName, false), sailID); - racingEventService.getBaseDomainFactory().getOrCreateCompetitor(id, name, null, null, null, team, boat, + racingEventService.getBaseDomainFactory().getOrCreateCompetitor(id, name, null, null, null, null, team, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null); } diff --git a/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/test/jaxrs/CompetitorsResourceTest.java b/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/test/jaxrs/CompetitorsResourceTest.java index bae9cc2bbea..c23005436c3 100644 --- a/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/test/jaxrs/CompetitorsResourceTest.java +++ b/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/test/jaxrs/CompetitorsResourceTest.java @@ -1,7 +1,6 @@ package com.sap.sailing.server.gateway.test.jaxrs; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.equalTo; +import static org.junit.Assert.assertTrue; import java.util.Collections; @@ -10,9 +9,6 @@ import org.json.simple.JSONValue; import org.junit.Before; import org.junit.Test; -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.DynamicTeam; import com.sap.sailing.domain.base.impl.NationalityImpl; import com.sap.sailing.domain.base.impl.PersonImpl; @@ -23,31 +19,26 @@ import com.sap.sailing.server.gateway.jaxrs.api.CompetitorsResource; public class CompetitorsResourceTest extends AbstractJaxRsApiTest { private final String name = "Heiko KRÖGER"; private final String id = "af855a56-9726-4a9c-a77e-da955bd289be"; - private final String boatClassName = "49er"; - private final String sailID = "GER 1"; private final String nationality = "GER"; private final String countryCode = "DE"; - + private final String shortName = "HK"; @Before public void setUp() throws Exception { super.setUp(); DynamicTeam team = new TeamImpl(null, Collections.singleton(new PersonImpl(null, new NationalityImpl(nationality), null, null)), null); - DynamicBoat boat = new BoatImpl(null, new BoatClassImpl(boatClassName, false), sailID); - racingEventService.getBaseDomainFactory().getOrCreateCompetitor(id, name, null, null, null, team, boat, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null); + racingEventService.getBaseDomainFactory().getOrCreateCompetitor(id, name, shortName, null, null, null, team, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null); } @Test - public void testExportRegattasAsJson() throws Exception { + public void testGetCompetitorAsJson() throws Exception { CompetitorsResource resource = spyResource(new CompetitorsResource()); String jsonString = resource.getCompetitor(id).getEntity().toString(); JSONObject json = Helpers.toJSONObjectSafe(JSONValue.parse(jsonString)); - assertThat("id is correct", json.get("id"), equalTo(id)); - assertThat("name is correct", json.get("name"), equalTo(name)); - assertThat("boatClass is correct", json.get("boatClassName"), equalTo(boatClassName)); - assertThat("sailID is correct", json.get("sailID"), equalTo(sailID)); - assertThat("nationality is correct", json.get("nationality"), equalTo(nationality)); - assertThat("nationality is correct", json.get("countryCode"), equalTo(countryCode)); + assertTrue(json.get("id").equals(id)); + assertTrue(json.get("name").equals(name)); + assertTrue(json.get("nationality").equals(nationality)); + assertTrue(json.get("countryCode").equals(countryCode)); } } diff --git a/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/test/jaxrs/LeaderboardGroupResourceTest.java b/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/test/jaxrs/LeaderboardGroupResourceTest.java index 3a490a83462..8af652a9185 100644 --- a/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/test/jaxrs/LeaderboardGroupResourceTest.java +++ b/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/test/jaxrs/LeaderboardGroupResourceTest.java @@ -50,7 +50,7 @@ public class LeaderboardGroupResourceTest extends AbstractJaxRsApiTest { raceColumnNames, /* trackedRegattaRegistry */null); series.add(testSeries); regatta = racingEventService.createRegatta(RegattaImpl.getDefaultName(regattaName, boatClassName), boatClassName, - /*startDate*/ null, /*endDate*/ null, UUID.randomUUID(), series, /*persistent*/ true, + /* canBoatsOfCompetitorsChangePerRace */ true, /*startDate*/ null, /*endDate*/ null, UUID.randomUUID(), series, /*persistent*/ true, DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), null, /*buoyZoneRadiusInHullLengths*/2.0, /* useStartTimeInference */ true, /* controlTrackingFromStartAndFinishTimes */ false, OneDesignRankingMetric::new); testSeries.addRaceColumn("R1", /* trackedRegattaRegistry */ null); diff --git a/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/test/jaxrs/LeaderboardsResourceCheckinAndOutTest.java b/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/test/jaxrs/LeaderboardsResourceCheckinAndOutTest.java index ac3c8f217a7..6bfccf4152c 100644 --- a/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/test/jaxrs/LeaderboardsResourceCheckinAndOutTest.java +++ b/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/test/jaxrs/LeaderboardsResourceCheckinAndOutTest.java @@ -9,7 +9,6 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; -import java.util.Set; import java.util.UUID; import javax.ws.rs.core.Response; @@ -21,7 +20,10 @@ import org.junit.Test; import com.sap.sailing.domain.abstractlog.regatta.RegattaLog; import com.sap.sailing.domain.abstractlog.regatta.tracking.analyzing.impl.RegattaLogDeviceCompetitorMappingFinder; import com.sap.sailing.domain.abstractlog.shared.analyzing.CompetitorsInLogAnalyzer; +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.Regatta; import com.sap.sailing.domain.base.impl.BoatClassImpl; import com.sap.sailing.domain.base.impl.DynamicBoat; @@ -37,21 +39,24 @@ import com.sap.sailing.domain.racelogtracking.DeviceMappingWithRegattaLogEvent; import com.sap.sailing.domain.racelogtracking.SmartphoneUUIDIdentifier; import com.sap.sailing.domain.ranking.OneDesignRankingMetric; import com.sap.sailing.server.gateway.jaxrs.api.LeaderboardsResource; +import com.sap.sse.common.Util; import com.sap.sse.common.impl.MillisecondsTimePoint; public class LeaderboardsResourceCheckinAndOutTest extends AbstractJaxRsApiTest { - private Competitor competitor; + private CompetitorWithBoat competitor; private RegattaLog log; private RegattaLeaderboard leaderboard; - + private BoatClass boatClass = new BoatClassImpl("49er", false); + @Before public void setUp() throws Exception { super.setUp(); Competitor c = createCompetitors(1).get(0); - competitor = racingEventService.getBaseDomainFactory().getOrCreateCompetitor(c.getId(), c.getName(), - c.getColor(), c.getEmail(), c.getFlagImage(), (DynamicTeam) c.getTeam(), (DynamicBoat) c.getBoat(), - /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null); - Regatta regatta = new RegattaImpl("regatta", new BoatClassImpl("49er", false), MillisecondsTimePoint.now(), + Boat boat = racingEventService.getBaseDomainFactory().getOrCreateBoat("boat", "boat", boatClass, "GER1", null); + competitor = racingEventService.getBaseDomainFactory().getOrCreateCompetitorWithBoat(c.getId(), c.getName(), c.getShortName(), + c.getColor(), c.getEmail(), c.getFlagImage(), (DynamicTeam) c.getTeam(), + /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null, (DynamicBoat) boat); + Regatta regatta = new RegattaImpl("regatta", boatClass, /* canBoatsOfCompetitorsChangePerRace */ false, MillisecondsTimePoint.now(), MillisecondsTimePoint.now(), Collections.singleton(new SeriesImpl("series", false, /* isFleetsCanRunInParallel */ true, Collections .singleton(new FleetImpl("fleet")), Arrays.asList("column"), racingEventService)), false, new HighPoint(), 0, null, OneDesignRankingMetric::new); @@ -76,11 +81,11 @@ public class LeaderboardsResourceCheckinAndOutTest extends AbstractJaxRsApiTest Response response = resource.postCheckin(json.toString(), leaderboard.getName()); assertThat("checkin returns OK", response.getStatus(), equalTo(Response.Status.OK.getStatusCode())); - Set registeredCompetitors = new CompetitorsInLogAnalyzer<>(log).analyze(); + Iterable registeredCompetitors = new CompetitorsInLogAnalyzer<>(log).analyze(); Map>> mappings = new RegattaLogDeviceCompetitorMappingFinder( log).analyze(); - assertThat("competitor was registered", registeredCompetitors.size(), equalTo(1)); + assertThat("competitor was registered", Util.size(registeredCompetitors), equalTo(1)); assertThat("device mappings for competitor exist", mappings.size(), equalTo(1)); List> mappingsForC = mappings.get(competitor); assertThat("exactly one device mapping for competitor exists", mappingsForC.size(), equalTo(1)); diff --git a/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/test/jaxrs/LeaderboardsResourcePingMarkTest.java b/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/test/jaxrs/LeaderboardsResourcePingMarkTest.java index c42d2e57884..732fdf86af2 100644 --- a/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/test/jaxrs/LeaderboardsResourcePingMarkTest.java +++ b/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/test/jaxrs/LeaderboardsResourcePingMarkTest.java @@ -60,7 +60,7 @@ public class LeaderboardsResourcePingMarkTest extends AbstractJaxRsApiTest { public void setUp() throws Exception { super.setUp(); mark = racingEventService.getBaseDomainFactory().getOrCreateMark("id", "name"); - Regatta regatta = new RegattaImpl("regatta", new BoatClassImpl("49er", false), MillisecondsTimePoint.now(), + Regatta regatta = new RegattaImpl("regatta", new BoatClassImpl("49er", false), /* canBoatsOfCompetitorsChangePerRace */ true, MillisecondsTimePoint.now(), MillisecondsTimePoint.now(), Collections.singleton(new SeriesImpl("series", false, /* isFleetsCanRunInParallel */ true, Collections .singleton(new FleetImpl("fleet")), Arrays.asList("column"), racingEventService)), false, new HighPoint(), 0, null, OneDesignRankingMetric::new); diff --git a/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/test/jaxrs/LeaderboardsResourceTest.java b/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/test/jaxrs/LeaderboardsResourceTest.java index 7078e3224d7..f7d855a5091 100644 --- a/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/test/jaxrs/LeaderboardsResourceTest.java +++ b/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/test/jaxrs/LeaderboardsResourceTest.java @@ -57,7 +57,7 @@ public class LeaderboardsResourceTest extends AbstractJaxRsApiTest { raceColumnNames, /* trackedRegattaRegistry */null); series.add(testSeries); regatta = racingEventService.createRegatta(RegattaImpl.getDefaultName(regattaBaseName, boatClassName), boatClassName, - /*startDate*/ null, /*endDate*/ null, UUID.randomUUID(), series, /*persistent*/ true, + /* canBoatsOfCompetitorsChangePerRace */ true, /*startDate*/ null, /*endDate*/ null, UUID.randomUUID(), series, /*persistent*/ true, DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), null, /*buoyZoneRadiusInHullLengths*/2.0, /* useStartTimeInference */ true, /* controlTrackingFromStartAndFinishTimes */ false, OneDesignRankingMetric::new); testSeries.addRaceColumn("R1", /* trackedRegattaRegistry */ null); diff --git a/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/test/jaxrs/RegattasResourceTest.java b/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/test/jaxrs/RegattasResourceTest.java index f0001bd0b7c..0b28ae7e9b8 100644 --- a/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/test/jaxrs/RegattasResourceTest.java +++ b/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/test/jaxrs/RegattasResourceTest.java @@ -48,7 +48,7 @@ public class RegattasResourceTest extends AbstractJaxRsApiTest { Series testSeries = new SeriesImpl("TestSeries", /* isMedal */false, /* isFleetsCanRunInParallel */ true, fleets, raceColumnNames, /* trackedRegattaRegistry */null); series.add(testSeries); racingEventService.createRegatta(RegattaImpl.getDefaultName(regattaName, boatClassName), boatClassName, - startDate, endDate, UUID.randomUUID(), series, /*persistent*/ true, + /* canBoatsOfCompetitorsChangePerRace */ true, startDate, endDate, UUID.randomUUID(), series, /*persistent*/ true, DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), null, /*buoyZoneRadiusInHullLengths*/2.0, /* useStartTimeInference */ true, /* controlTrackingFromStartAndFinishTimes */ false, OneDesignRankingMetric::new); testSeries.addRaceColumn("R1", /* trackedRegattaRegistry */ null); testSeries.addRaceColumn("R2", /* trackedRegattaRegistry */ null); diff --git a/java/com.sap.sailing.server.gateway/WEB-INF/web.xml b/java/com.sap.sailing.server.gateway/WEB-INF/web.xml index 4d8d4732fba..51d75160911 100644 --- a/java/com.sap.sailing.server.gateway/WEB-INF/web.xml +++ b/java/com.sap.sailing.server.gateway/WEB-INF/web.xml @@ -102,7 +102,14 @@ RCCompetitors /rc/competitors - + + RCCompetitorsAndBoats + com.sap.sailing.server.gateway.impl.rc.CompetitorsAndBoatsJsonExportServlet + + + RCCompetitorsAndBoats + /rc/competitorsAndBoats + ExpeditionThroughHttpPostServlet diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/rc/CompetitorsAndBoatsJsonExportServlet.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/rc/CompetitorsAndBoatsJsonExportServlet.java new file mode 100644 index 00000000000..83edf1d48ad --- /dev/null +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/rc/CompetitorsAndBoatsJsonExportServlet.java @@ -0,0 +1,76 @@ +package com.sap.sailing.server.gateway.impl.rc; + +import java.io.IOException; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; + +import com.sap.sailing.domain.base.Boat; +import com.sap.sailing.domain.base.Competitor; +import com.sap.sailing.domain.base.Fleet; +import com.sap.sailing.domain.base.RaceColumn; +import com.sap.sailing.domain.common.racelog.RaceLogServletConstants; +import com.sap.sailing.domain.leaderboard.Leaderboard; +import com.sap.sailing.server.RacingEventService; +import com.sap.sailing.server.gateway.AbstractJsonHttpServlet; +import com.sap.sailing.server.gateway.serialization.impl.CompetitorAndBoatJsonSerializer; +import com.sap.sse.common.Util.Pair; + +public class CompetitorsAndBoatsJsonExportServlet extends AbstractJsonHttpServlet { + private static final long serialVersionUID = 4510175441769759252L; + + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + String leaderboardName = request.getParameter(RaceLogServletConstants.PARAMS_LEADERBOARD_NAME); + if (leaderboardName == null) { + response.sendError(HttpServletResponse.SC_BAD_REQUEST, + String.format("Missing parameter '%s'.", RaceLogServletConstants.PARAMS_LEADERBOARD_NAME)); + return; + } + String raceColumnName = request.getParameter(RaceLogServletConstants.PARAMS_RACE_COLUMN_NAME); + if (raceColumnName == null) { + response.sendError(HttpServletResponse.SC_BAD_REQUEST, + String.format("Missing parameter '%s'.", RaceLogServletConstants.PARAMS_RACE_COLUMN_NAME)); + return; + } + String fleetName = request.getParameter(RaceLogServletConstants.PARAMS_RACE_FLEET_NAME); + if (fleetName == null) { + response.sendError(HttpServletResponse.SC_BAD_REQUEST, + String.format("Missing parameter '%s'.", RaceLogServletConstants.PARAMS_RACE_FLEET_NAME)); + return; + } + RacingEventService service = getService(); + Leaderboard leaderboard = service.getLeaderboardByName(leaderboardName); + if (leaderboard == null) { + response.sendError(HttpServletResponse.SC_NOT_FOUND, "No such leaderboard found."); + return; + } + RaceColumn raceColumn = leaderboard.getRaceColumnByName(raceColumnName); + if (raceColumn == null) { + response.sendError(HttpServletResponse.SC_NOT_FOUND, "No such race column found."); + return; + } + Fleet fleet = raceColumn.getFleetByName(fleetName); + if (fleet == null) { + response.sendError(HttpServletResponse.SC_NOT_FOUND, "No such fleet found."); + return; + } + + CompetitorAndBoatJsonSerializer competitorsAndBoatsSerializer = CompetitorAndBoatJsonSerializer.create(); + + JSONArray result = new JSONArray(); + for (Competitor competitor : leaderboard.getCompetitors(raceColumn, fleet)) { + Boat boat = leaderboard.getBoatOfCompetitor(competitor, raceColumn, fleet); + + JSONObject serializeCompetitorAndBoat = competitorsAndBoatsSerializer.serialize(new Pair(competitor, boat)); + result.add(serializeCompetitorAndBoat); + } + + setJsonResponseHeader(response); + result.writeJSONString(response.getWriter()); + } +} diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/rc/CompetitorsJsonExportServlet.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/rc/CompetitorsJsonExportServlet.java index 8efd5107173..c081d134603 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/rc/CompetitorsJsonExportServlet.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/rc/CompetitorsJsonExportServlet.java @@ -15,12 +15,7 @@ import com.sap.sailing.domain.common.racelog.RaceLogServletConstants; import com.sap.sailing.domain.leaderboard.Leaderboard; import com.sap.sailing.server.RacingEventService; import com.sap.sailing.server.gateway.AbstractJsonHttpServlet; -import com.sap.sailing.server.gateway.serialization.impl.BoatClassJsonSerializer; -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.NationalityJsonSerializer; -import com.sap.sailing.server.gateway.serialization.impl.PersonJsonSerializer; -import com.sap.sailing.server.gateway.serialization.impl.TeamJsonSerializer; public class CompetitorsJsonExportServlet extends AbstractJsonHttpServlet { private static final long serialVersionUID = 4510175441769759252L; @@ -62,8 +57,7 @@ public class CompetitorsJsonExportServlet extends AbstractJsonHttpServlet { return; } JSONArray result = new JSONArray(); - CompetitorJsonSerializer serializer = new CompetitorJsonSerializer(new TeamJsonSerializer( - new PersonJsonSerializer(new NationalityJsonSerializer())), new BoatJsonSerializer(new BoatClassJsonSerializer())); + CompetitorJsonSerializer serializer = CompetitorJsonSerializer.create(); for (Competitor competitor : leaderboard.getCompetitors(raceColumn, fleet)) { result.add(serializer.serialize(competitor)); } diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/rc/RaceGroupFactory.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/rc/RaceGroupFactory.java index 7532e14ed75..5909486602d 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/rc/RaceGroupFactory.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/rc/RaceGroupFactory.java @@ -52,8 +52,8 @@ public class RaceGroupFactory { CourseArea courseArea = leaderboard.getDefaultCourseArea(); Regatta regatta = ((RegattaLeaderboard) leaderboard).getRegatta(); Iterable series = getSeries(leaderboard); - return new RaceGroupImpl(name, leaderboard.getDisplayName(), regatta.getBoatClass(), courseArea, - series, regatta.getRegattaConfiguration()); + return new RaceGroupImpl(name, leaderboard.getDisplayName(), regatta.getBoatClass(), regatta.canBoatsOfCompetitorsChangePerRace(), + courseArea, series, regatta.getRegattaConfiguration()); } /** @@ -67,7 +67,7 @@ public class RaceGroupFactory { BoatClass boatClass = null; RegattaConfiguration configuration = null; Iterable series = getSeries(leaderboard); - return new RaceGroupImpl(name, leaderboard.getDisplayName(), boatClass, courseArea, series, configuration); + return new RaceGroupImpl(name, leaderboard.getDisplayName(), boatClass, false, courseArea, series, configuration); } private Iterable getSeries(Leaderboard leaderboard) { diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/AbstractLeaderboardsResource.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/AbstractLeaderboardsResource.java index 78f5a05f636..542249ca0b2 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/AbstractLeaderboardsResource.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/AbstractLeaderboardsResource.java @@ -8,6 +8,7 @@ import org.json.simple.JSONArray; import org.json.simple.JSONObject; import com.sap.sailing.domain.base.Competitor; +import com.sap.sailing.domain.base.CompetitorWithBoat; import com.sap.sailing.domain.base.Fleet; import com.sap.sailing.domain.base.Nationality; import com.sap.sailing.domain.base.RaceColumn; @@ -54,10 +55,14 @@ public abstract class AbstractLeaderboardsResource extends AbstractSailingServer for (Competitor competitor : leaderboard.getCompetitors()) { JSONObject jsonCompetitor = new JSONObject(); jsonCompetitor.put("name", competitor.getName()); + jsonCompetitor.put("shortName", competitor.getShortName()); final String displayName = leaderboard.getDisplayName(competitor); jsonCompetitor.put("displayName", displayName == null ? competitor.getName() : displayName); jsonCompetitor.put("id", competitor.getId().toString()); - jsonCompetitor.put("sailID", competitor.getBoat().getSailID()); + if (competitor.hasBoat()) { + CompetitorWithBoat competitorWithBoat = (CompetitorWithBoat) competitor; + jsonCompetitor.put("sailID", competitorWithBoat.getBoat().getSailID()); + } Nationality nationality = competitor.getTeam().getNationality(); jsonCompetitor.put("nationality", nationality != null ? nationality.getThreeLetterIOCAcronym() : null); jsonCompetitor.put("countryCode", nationality != null ? (nationality.getCountryCode() != null ? nationality @@ -105,6 +110,7 @@ public abstract class AbstractLeaderboardsResource extends AbstractSailingServer protected void writeCompetitorBaseData(JSONObject jsonCompetitor, CompetitorDTO competitor, LeaderboardDTO leaderboard) { jsonCompetitor.put("name", competitor.getName()); + jsonCompetitor.put("shortName", competitor.getShortName()); final String displayName = leaderboard.getDisplayName(competitor); jsonCompetitor.put("displayName", displayName == null ? competitor.getName() : displayName); jsonCompetitor.put("id", competitor.getIdAsString()); diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/BoatsResource.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/BoatsResource.java new file mode 100644 index 00000000000..338adf1f103 --- /dev/null +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/BoatsResource.java @@ -0,0 +1,37 @@ +package com.sap.sailing.server.gateway.jaxrs.api; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; + +import org.apache.commons.lang.StringEscapeUtils; + +import com.sap.sailing.domain.base.Boat; +import com.sap.sailing.server.gateway.jaxrs.AbstractSailingServerResource; +import com.sap.sailing.server.gateway.serialization.impl.BoatJsonSerializer; + +@Path("/v1/boats") +public class BoatsResource extends AbstractSailingServerResource { + + @GET + @Produces("application/json;charset=UTF-8") + @Path("{boatId}") + public Response getBoat(@PathParam("boatId") String boatIdAsString) { + Response response; + Boat boat = getService().getCompetitorStore().getExistingBoatByIdAsString(boatIdAsString); + if (boat == null) { + response = Response.status(Status.NOT_FOUND) + .entity("Could not find a boat with id '" + StringEscapeUtils.escapeHtml(boatIdAsString) + "'.") + .type(MediaType.TEXT_PLAIN).build(); + } else { + BoatJsonSerializer boatJsonSerializer = BoatJsonSerializer.create(); + String jsonString = boatJsonSerializer.serialize(boat).toJSONString(); + response = Response.ok(jsonString).header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build(); + } + return response; + } +} diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/CompetitorsResource.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/CompetitorsResource.java index b3f84fbe9b2..7cf7572c234 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/CompetitorsResource.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/CompetitorsResource.java @@ -52,11 +52,10 @@ public class CompetitorsResource extends AbstractSailingServerResource { JSONObject json = new JSONObject(); json.put(CompetitorJsonConstants.FIELD_ID, competitor.getId().toString()); json.put(CompetitorJsonConstants.FIELD_NAME, competitor.getName()); - json.put(CompetitorJsonConstants.FIELD_SAIL_ID, competitor.getBoat().getSailID()); + json.put(CompetitorJsonConstants.FIELD_SHORT_NAME, competitor.getShortName()); final Nationality nationality = competitor.getTeam().getNationality(); json.put(CompetitorJsonConstants.FIELD_NATIONALITY, nationality==null?null:nationality.getThreeLetterIOCAcronym()); json.put(CompetitorJsonConstants.FIELD_COUNTRY_CODE, nationality==null?null:nationality.getCountryCode().getTwoLetterISOCode()); - json.put(CompetitorJsonConstants.FIELD_BOAT_CLASS_NAME, competitor.getBoat().getBoatClass().getName()); json.put(CompetitorJsonConstants.FIELD_COLOR, competitor.getColor() != null ? competitor.getColor().getAsHtml() : null); if (competitor.getFlagImage() != null) { json.put(CompetitorJsonConstants.FIELD_FLAG_IMAGE, competitor.getFlagImage().toString()); @@ -164,8 +163,9 @@ public class CompetitorsResource extends AbstractSailingServerResource { .entity("Could not store competitor image").type(MediaType.TEXT_PLAIN).build()); } - getService().getCompetitorStore().updateCompetitor(competitorId, competitor.getName(), competitor.getColor(), competitor.getEmail(), - competitor.getBoat().getSailID(), competitor.getTeam().getNationality(), imageUri, competitor.getFlagImage(), + getService().getCompetitorStore().updateCompetitor(competitorId, competitor.getName(), competitor.getShortName(), + competitor.getColor(), competitor.getEmail(), + competitor.getTeam().getNationality(), imageUri, competitor.getFlagImage(), /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, competitor.getSearchTag()); logger.log(Level.INFO, "Set team image for competitor " + competitor.getName()); diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/EventsResource.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/EventsResource.java index ddf7bc694c8..4c914b56539 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/EventsResource.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/EventsResource.java @@ -354,7 +354,8 @@ public class EventsResource extends AbstractSailingServerResource { RegattaCreationParametersDTO regattaCreationParametersDTO = new RegattaCreationParametersDTO( createDefaultSeriesCreationParameters(regattaName, numberOfRaces)); UUID regattaId = UUID.randomUUID(); - addRegatta(regattaName, controlTrackingFromStartAndFinishTimes, useStartTimeInterference, + boolean canBoatsOfCompetitorsChangePerRace = false; + addRegatta(regattaName, canBoatsOfCompetitorsChangePerRace, controlTrackingFromStartAndFinishTimes, useStartTimeInterference, buoyZoneRadiusInHullLengths, courseAreaId, boatClassName, /* startDate */ null, /* endDate */ null, scoringScheme, rankingMetric, regattaId, regattaCreationParametersDTO, leaderboardDiscardThresholds, numberOfRaces); final RegattaLeaderboard leaderboard = addLeaderboard(regattaName, leaderboardDiscardThresholds); @@ -536,7 +537,7 @@ public class EventsResource extends AbstractSailingServerResource { return getService().apply(new AddCourseAreas(eventId, courseAreaNames, courseAreaIds))[0]; } - private Regatta addRegatta(String regattaName, boolean controlTrackingFromStartAndFinishTimes, boolean useStartTimeInterference, + private Regatta addRegatta(String regattaName, boolean canBoatsOfCompetitorsChangePerRace, boolean controlTrackingFromStartAndFinishTimes, boolean useStartTimeInterference, double buoyZoneRadiusInHullLengths, UUID courseAreaId, String boatClassName, MillisecondsTimePoint startDate, MillisecondsTimePoint endDate, ScoringScheme scoringScheme, RankingMetrics rankingMetric, UUID regattaId, RegattaCreationParametersDTO regattaCreationParametersDTO, @@ -544,7 +545,7 @@ public class EventsResource extends AbstractSailingServerResource { if (enforceSecurityChecks) { SecurityUtils.getSubject().checkPermission(Permission.REGATTA.getStringPermissionForObjects(Mode.CREATE, regattaName)); } - Regatta regatta = getService().apply(new AddSpecificRegatta(regattaName, boatClassName, startDate, endDate, regattaId, + Regatta regatta = getService().apply(new AddSpecificRegatta(regattaName, boatClassName, canBoatsOfCompetitorsChangePerRace, startDate, endDate, regattaId, regattaCreationParametersDTO, /* isPersistent */ true, scoringScheme, courseAreaId, buoyZoneRadiusInHullLengths, useStartTimeInterference, controlTrackingFromStartAndFinishTimes, rankingMetric)); return regatta; diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/LeaderboardsResource.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/LeaderboardsResource.java index 28bbe8e64b6..50980f2f49a 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/LeaderboardsResource.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/LeaderboardsResource.java @@ -49,9 +49,11 @@ import com.sap.sailing.domain.abstractlog.regatta.RegattaLog; import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMappingEventImpl; import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogCloseOpenEndedDeviceMappingEventImpl; import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogDefineMarkEventImpl; +import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogDeviceBoatMappingEventImpl; 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.OpenEndedDeviceMappingFinder; +import com.sap.sailing.domain.base.Boat; import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.ControlPoint; import com.sap.sailing.domain.base.Course; @@ -62,7 +64,9 @@ import com.sap.sailing.domain.base.RaceColumn; import com.sap.sailing.domain.base.RaceDefinition; import com.sap.sailing.domain.base.Regatta; import com.sap.sailing.domain.base.Waypoint; +import com.sap.sailing.domain.base.impl.CompetitorWithBoatImpl; import com.sap.sailing.domain.base.impl.CourseImpl; +import com.sap.sailing.domain.base.impl.DynamicBoat; import com.sap.sailing.domain.common.Bearing; import com.sap.sailing.domain.common.CourseDesignerMode; import com.sap.sailing.domain.common.DeviceIdentifier; @@ -111,7 +115,7 @@ import com.sap.sailing.server.gateway.serialization.coursedata.impl.CourseJsonSe import com.sap.sailing.server.gateway.serialization.coursedata.impl.GateJsonSerializer; import com.sap.sailing.server.gateway.serialization.coursedata.impl.MarkJsonSerializer; import com.sap.sailing.server.gateway.serialization.coursedata.impl.WaypointJsonSerializer; -import com.sap.sailing.server.gateway.serialization.impl.CompetitorJsonSerializer; +import com.sap.sailing.server.gateway.serialization.impl.CompetitorWithBoatJsonSerializer; import com.sap.sailing.server.gateway.serialization.impl.FlatGPSFixJsonSerializer; import com.sap.sailing.server.gateway.serialization.impl.MarkJsonSerializerWithPosition; import com.sap.sse.InvalidDateException; @@ -263,6 +267,7 @@ public class LeaderboardsResource extends AbstractLeaderboardsResource { } final TimePoint now = MillisecondsTimePoint.now(); String competitorId = (String) requestObject.get(DeviceMappingConstants.JSON_COMPETITOR_ID_AS_STRING); + String boatId = (String) requestObject.get(DeviceMappingConstants.JSON_BOAT_ID_AS_STRING); String markId = (String) requestObject.get(DeviceMappingConstants.JSON_MARK_ID_AS_STRING); String deviceUuid = (String) requestObject.get(DeviceMappingConstants.JSON_DEVICE_UUID); Long fromMillis = (Long) requestObject.get(DeviceMappingConstants.JSON_FROM_MILLIS); @@ -271,7 +276,7 @@ public class LeaderboardsResource extends AbstractLeaderboardsResource { // String deviceType = (String) requestObject.get(DeviceMappingConstants.JSON_DEVICE_TYPE); // String pushDeviceId = (String) requestObject.get(DeviceMappingConstants.JSON_PUSH_DEVICE_ID); - if ((competitorId == null && markId == null) || deviceUuid == null || fromMillis == null) { + if ((competitorId == null && boatId == null && markId == null) || deviceUuid == null || fromMillis == null) { // || deviceType == null logger.warning("Invalid JSON body in request"); return Response.status(Status.BAD_REQUEST).entity("Invalid JSON body in request") @@ -299,6 +304,24 @@ public class LeaderboardsResource extends AbstractLeaderboardsResource { } event = new RegattaLogDeviceCompetitorMappingEventImpl(now, now, author, UUID.randomUUID(), mappedToCompetitor, device, from, /* to */ null); + } else if (boatId != null) { + // map to a boat + final Boat mappedToBoat = domainFactory.getCompetitorStore().getExistingBoatByIdAsString(boatId); + mappedTo = mappedToBoat; + if (mappedToBoat == null) { + logger.warning("No boat found for id " + boatId); + return Response.status(Status.BAD_REQUEST).entity("No boat found for id " + StringEscapeUtils.escapeHtml(boatId)) + .type(MediaType.TEXT_PLAIN).build(); + } + Iterable registered = hasRegattaLike.getAllBoats(); + if (!Util.contains(registered, mappedToBoat)) { + logger.warning("Boat found but not registered for leaderboard " + leaderboardName); + return Response.status(Status.BAD_REQUEST) + .entity("Boat found but not registered for leaderboard " + StringEscapeUtils.escapeHtml(leaderboardName)) + .type(MediaType.TEXT_PLAIN).build(); + } + event = new RegattaLogDeviceBoatMappingEventImpl(now, now, author, UUID.randomUUID(), mappedToBoat, device, + from, /* to */ null); } else { // map to a mark final Mark mappedToMark = domainFactory.getExistingMarkById(Helpers.tryUuidConversion(markId)); @@ -345,11 +368,12 @@ public class LeaderboardsResource extends AbstractLeaderboardsResource { logger.fine("JSON requestObject is: " + requestObject.toString()); Long toMillis = (Long) requestObject.get(DeviceMappingConstants.JSON_TO_MILLIS); String competitorId = (String) requestObject.get(DeviceMappingConstants.JSON_COMPETITOR_ID_AS_STRING); + String boatId = (String) requestObject.get(DeviceMappingConstants.JSON_BOAT_ID_AS_STRING); String markId = (String) requestObject.get(DeviceMappingConstants.JSON_MARK_ID_AS_STRING); String deviceUuid = (String) requestObject.get(DeviceMappingConstants.JSON_DEVICE_UUID); TimePoint closingTimePointInclusive = new MillisecondsTimePoint(toMillis); if (toMillis == null || deviceUuid == null || closingTimePointInclusive == null || - (competitorId == null && markId == null)) { + (competitorId == null && boatId == null && markId == null)) { logger.warning("Invalid JSON body in request"); return Response.status(Status.BAD_REQUEST).entity("Invalid JSON body in request") .type(MediaType.TEXT_PLAIN).build(); @@ -363,6 +387,14 @@ public class LeaderboardsResource extends AbstractLeaderboardsResource { return Response.status(Status.BAD_REQUEST).entity("No competitor found for id " + competitorId) .type(MediaType.TEXT_PLAIN).build(); } + } else if (boatId != null) { + final Boat mappedToBoat = getService().getCompetitorStore().getExistingBoatByIdAsString(boatId); + mappedTo = mappedToBoat; + if (mappedToBoat == null) { + logger.warning("No boat found for id " + boatId); + return Response.status(Status.BAD_REQUEST).entity("No boat found for id " + boatId) + .type(MediaType.TEXT_PLAIN).build(); + } } else { // map to mark DomainFactory domainFactory = getService().getDomainObjectFactory().getBaseDomainFactory(); @@ -375,18 +407,24 @@ public class LeaderboardsResource extends AbstractLeaderboardsResource { } } + final String mappedToTypeString; + if (competitorId != null) { + mappedToTypeString = "competitor"; + } else { + mappedToTypeString = (markId != null) ? "mark" : "boat"; + } OpenEndedDeviceMappingFinder finder = new OpenEndedDeviceMappingFinder(isRegattaLike.getRegattaLog(), mappedTo, deviceUuid); Serializable deviceMappingEventId = finder.analyze(); if (deviceMappingEventId == null) { - logger.warning("No corresponding open "+((markId!=null)?"mark":"competitor")+" to device mapping has been found"); + logger.warning("No corresponding open " + mappedToTypeString + " to device mapping has been found"); return Response.status(Status.BAD_REQUEST) - .entity("No corresponding open "+((markId!=null)?"mark":"competitor")+" to device mapping has been found") + .entity("No corresponding open " + mappedToTypeString + " to device mapping has been found") .type(MediaType.TEXT_PLAIN).build(); } RegattaLogCloseOpenEndedDeviceMappingEventImpl event = new RegattaLogCloseOpenEndedDeviceMappingEventImpl(now, author, deviceMappingEventId, closingTimePointInclusive); isRegattaLike.getRegattaLog().add(event); - logger.fine("Successfully checked out "+((markId!=null)?"mark ":"competitor ") + mappedTo.getName()); + logger.fine("Successfully checked out " + mappedToTypeString + mappedTo.getName()); return Response.status(Status.OK).build(); } @@ -992,10 +1030,11 @@ public class LeaderboardsResource extends AbstractLeaderboardsResource { return result; }); } - CompetitorJsonSerializer serializer = new CompetitorJsonSerializer(); + CompetitorWithBoatJsonSerializer serializer = CompetitorWithBoatJsonSerializer.create(); JSONArray result = new JSONArray(); for (final Competitor c : competitors) { - JSONObject jsonCompetitor = serializer.serialize(c); + Boat boat = trackedRace.getBoatOfCompetitor(c); + JSONObject jsonCompetitor = serializer.serialize(new CompetitorWithBoatImpl(c, (DynamicBoat) boat)); result.add(jsonCompetitor); } String json = result.toJSONString(); diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RegattasResource.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RegattasResource.java index 0de42199cf2..0486e29a8f4 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RegattasResource.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RegattasResource.java @@ -32,7 +32,9 @@ import org.json.simple.JSONArray; import org.json.simple.JSONObject; import com.sap.sailing.datamining.SailingPredefinedQueries; +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.Course; import com.sap.sailing.domain.base.Mark; import com.sap.sailing.domain.base.RaceColumnInSeries; @@ -41,6 +43,7 @@ import com.sap.sailing.domain.base.Regatta; import com.sap.sailing.domain.base.Series; import com.sap.sailing.domain.base.Waypoint; import com.sap.sailing.domain.base.impl.BoatImpl; +import com.sap.sailing.domain.base.impl.DynamicBoat; import com.sap.sailing.domain.base.impl.PersonImpl; import com.sap.sailing.domain.base.impl.TeamImpl; import com.sap.sailing.domain.common.Distance; @@ -73,10 +76,9 @@ import com.sap.sailing.server.gateway.serialization.coursedata.impl.MarkJsonSeri import com.sap.sailing.server.gateway.serialization.coursedata.impl.WaypointJsonSerializer; import com.sap.sailing.server.gateway.serialization.impl.AbstractTrackedRaceDataJsonSerializer; import com.sap.sailing.server.gateway.serialization.impl.BoatClassJsonSerializer; -import com.sap.sailing.server.gateway.serialization.impl.BoatJsonSerializer; import com.sap.sailing.server.gateway.serialization.impl.ColorJsonSerializer; import com.sap.sailing.server.gateway.serialization.impl.CompetitorJsonSerializer; -import com.sap.sailing.server.gateway.serialization.impl.CompetitorWithChangingBoatJsonSerializer; +import com.sap.sailing.server.gateway.serialization.impl.CompetitorWithBoatJsonSerializer; import com.sap.sailing.server.gateway.serialization.impl.DefaultWindTrackJsonSerializer; import com.sap.sailing.server.gateway.serialization.impl.DistanceJsonSerializer; import com.sap.sailing.server.gateway.serialization.impl.FleetJsonSerializer; @@ -213,8 +215,7 @@ public class RegattasResource extends AbstractSailingServerResource { } else { NationalityJsonSerializer nationalityJsonSerializer = new NationalityJsonSerializer(); CompetitorJsonSerializer competitorJsonSerializer = new CompetitorJsonSerializer(new TeamJsonSerializer( - new PersonJsonSerializer(nationalityJsonSerializer)), new BoatJsonSerializer( - new BoatClassJsonSerializer())); + new PersonJsonSerializer(nationalityJsonSerializer))); JsonSerializer regattaSerializer = new RegattaJsonSerializer(null, competitorJsonSerializer); JSONObject serializedRegatta = regattaSerializer.serialize(regatta); @@ -244,10 +245,7 @@ public class RegattasResource extends AbstractSailingServerResource { if (race == null) { response = getBadRaceErrorResponse(regattaName, raceName); } else { - NationalityJsonSerializer nationalityJsonSerializer = new NationalityJsonSerializer(); - CompetitorJsonSerializer competitorJsonSerializer = new CompetitorWithChangingBoatJsonSerializer(race, new TeamJsonSerializer( - new PersonJsonSerializer(nationalityJsonSerializer)), new BoatJsonSerializer( - new BoatClassJsonSerializer())); + CompetitorWithBoatJsonSerializer competitorJsonSerializer = CompetitorWithBoatJsonSerializer.create(); JsonSerializer raceEntriesSerializer = new RaceEntriesJsonSerializer(competitorJsonSerializer); JSONObject serializedRaceEntries = raceEntriesSerializer.serialize(race); @@ -327,19 +325,20 @@ public class RegattasResource extends AbstractSailingServerResource { response = getBadBoatClassResponse(boatClassName); } else { final User user = getService(SecurityService.class).getCurrentUser(); - final Competitor competitor = getService().getCompetitorStore().getOrCreateCompetitor(UUID.randomUUID(), - user.getFullName() == null ? user.getName() : user.getFullName(), + final Boat boat = new BoatImpl(UUID.randomUUID(), user.getName(), + getService().getBaseDomainFactory().getOrCreateBoatClass(boatClassName, /* typicallyStartsUpwind */ true), + sailId); + final CompetitorWithBoat competitor = getService().getCompetitorStore().getOrCreateCompetitorWithBoat(UUID.randomUUID(), + user.getFullName() == null ? user.getName() : user.getFullName(), /* shortName */ null, /* displayColor */ null, user.getEmail(), /* flagImageURI */ null, new TeamImpl(user.getName(), Collections.singleton(new PersonImpl(user.getFullName() == null ? user.getName() : user.getFullName(), getService().getBaseDomainFactory().getOrCreateNationality(nationalityThreeLetterIOCCode), /* dateOfBirth */ null, /* description */ null)), - /* coach */ null), new BoatImpl(user.getName(), - getService().getBaseDomainFactory().getOrCreateBoatClass(boatClassName, /* typicallyStartsUpwind */ true), - sailId), timeOnTimeFactor, + /* coach */ null), timeOnTimeFactor, timeOnDistanceAllowancePerNauticalMileAsMillis == null ? null : new MillisecondsDurationImpl(timeOnDistanceAllowancePerNauticalMileAsMillis), - searchTag); + searchTag, (DynamicBoat) boat); regatta.registerCompetitor(competitor); - response = Response.ok(CompetitorJsonSerializer.create().serialize(competitor).toJSONString()). + response = Response.ok(CompetitorWithBoatJsonSerializer.create().serialize(competitor).toJSONString()). header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build(); } return response; @@ -434,7 +433,7 @@ public class RegattasResource extends AbstractSailingServerResource { JSONObject jsonCompetitor = new JSONObject(); jsonCompetitor.put("id", competitor.getId() != null ? competitor.getId().toString() : null); jsonCompetitor.put("name", competitor.getName()); - jsonCompetitor.put("sailNumber", competitor.getBoat().getSailID()); + jsonCompetitor.put("sailNumber", trackedRace.getBoatOfCompetitor(competitor).getSailID()); jsonCompetitor.put("color", competitor.getColor() != null ? competitor.getColor().getAsHtml() : null); if(competitor.getFlagImage() != null) { jsonCompetitor.put("flagImage", competitor.getFlagImage().toString()); @@ -1014,7 +1013,7 @@ public class RegattasResource extends AbstractSailingServerResource { .build(); } else { TrackedRace trackedRace = findTrackedRace(regattaName, raceName); - ManeuversJsonSerializer serializer = new ManeuversJsonSerializer(new CompetitorJsonSerializer(), + ManeuversJsonSerializer serializer = new ManeuversJsonSerializer(CompetitorWithBoatJsonSerializer.create(), new ManeuverJsonSerializer(new GPSFixJsonSerializer(), new DistanceJsonSerializer())); JSONObject jsonMarkPassings = serializer.serialize(trackedRace); String json = jsonMarkPassings.toJSONString(); @@ -1149,7 +1148,7 @@ public class RegattasResource extends AbstractSailingServerResource { jsonCompetitorInLeg.put("id", competitor.getId() != null ? competitor.getId() .toString() : null); jsonCompetitorInLeg.put("name", competitor.getName()); - jsonCompetitorInLeg.put("sailNumber", competitor.getBoat().getSailID()); + jsonCompetitorInLeg.put("sailNumber", trackedRace.getBoatOfCompetitor(competitor).getSailID()); jsonCompetitorInLeg.put("color", competitor.getColor() != null ? competitor.getColor() .getAsHtml() : null); @@ -1307,7 +1306,7 @@ public class RegattasResource extends AbstractSailingServerResource { } jsonCompetitorInLeg.put("id", competitor.getId() != null ? competitor.getId().toString() : null); jsonCompetitorInLeg.put("name", competitor.getName()); - jsonCompetitorInLeg.put("sailNumber", competitor.getBoat().getSailID()); + jsonCompetitorInLeg.put("sailNumber", trackedRace.getBoatOfCompetitor(competitor).getSailID()); jsonCompetitorInLeg.put("color", competitor.getColor() != null ? competitor.getColor().getAsHtml() : null); jsonCompetitorInLeg.put("rank", rank++); diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RestApiApplication.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RestApiApplication.java index 1484984183f..8f89926dc16 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RestApiApplication.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RestApiApplication.java @@ -16,6 +16,7 @@ public class RestApiApplication extends Application { classes.add(LeaderboardGroupsResource.class); classes.add(EventsResource.class); classes.add(RegattasResource.class); + classes.add(BoatsResource.class); classes.add(LeaderboardsResource.class); classes.add(LeaderboardsResourceV2.class); classes.add(PolarResource.class); diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/trackfiles/impl/ExpeditionAllInOneImporter.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/trackfiles/impl/ExpeditionAllInOneImporter.java index c6feaa5d4ca..eecf7bb6d92 100755 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/trackfiles/impl/ExpeditionAllInOneImporter.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/trackfiles/impl/ExpeditionAllInOneImporter.java @@ -214,7 +214,9 @@ public class ExpeditionAllInOneImporter { final RegattaCreationParametersDTO regattaCreationParameters = new RegattaCreationParametersDTO( seriesCreationParameters); - final Regatta regatta = service.apply(new AddSpecificRegatta(regattaNameAndleaderboardName, boatClassName, null, null, UUID.randomUUID(), + final Regatta regatta = service.apply(new AddSpecificRegatta(regattaNameAndleaderboardName, boatClassName, + /* can boats of competitors change */ false, + /* start date */ null, /* end date */ null, UUID.randomUUID(), regattaCreationParameters, true, scoringScheme, courseAreaId, buoyZoneRadiusInHullLengths, true, false, rankingMetric)); this.ensureBoatClassDetermination(regatta); diff --git a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/AbstractLogReplicationTest.java b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/AbstractLogReplicationTest.java index 039debfd8d4..4a84393f943 100644 --- a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/AbstractLogReplicationTest.java +++ b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/AbstractLogReplicationTest.java @@ -103,7 +103,8 @@ public abstract class AbstractLogReplicationTest series = Collections.emptyList(); + Regatta masterRegatta = master.createRegatta(RegattaImpl.getDefaultName(baseEventName, boatClassName), boatClassName, + /* canBoatsOfCompetitorsChangePerRace */ true, /*startDate*/ null, /*endDate*/ null, regattaId, series, + /* persistent */ true, DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), null, /*buoyZoneRadiusInHullLengths*/2.0, /* useStartTimeInference */ true, + /* controlTrackingFromStartAndFinishTimes */ false, OneDesignRankingMetric::new); + Iterable emptyWaypointList = Collections.emptyList(); + final String boatName = "Kielboat Harry"; + final BoatClass boatClass = DomainFactory.INSTANCE.getOrCreateBoatClass("505", /* typicallyStartsUpwind */true); + Competitor competitor = master.getBaseDomainFactory().getOrCreateCompetitor( + 123, "comp1", "c1", Color.RED, "someone@nowhere.de", null, + new TeamImpl("STG", Collections.singleton(new PersonImpl("comp1", new NationalityImpl("GER"), + /* dateOfBirth */null, "This is famous name")), new PersonImpl("Rigo van Maas", + new NationalityImpl("NED"), /* dateOfBirth */null, "This is Rigo, the coach")), + /* timeOnTimeFactor */ null, /* timeOnDistanceAllowanceInSecondsPerNauticalMile */ null, null); + Boat boat = master.getBaseDomainFactory().getOrCreateBoat(UUID.randomUUID(), "Kielboat", boatClass, "GER 123", Color.RED); + Map competitorsAndBoats = new HashMap<>(); + competitorsAndBoats.put(competitor, boat); + final String raceName = "Test Race"; + RaceDefinition raceDefinition = new RaceDefinitionImpl(raceName, new CourseImpl("Empty Course", emptyWaypointList), + masterRegatta.getBoatClass(), competitorsAndBoats); + master.apply(new AddRaceDefinition(masterRegatta.getRegattaIdentifier(), raceDefinition)); + Thread.sleep(1000); + Regatta replicatedRegatta = replica.getRegatta(new RegattaName(masterRegatta.getName())); + assertNotNull(replicatedRegatta); + assertTrue(replicatedRegatta.isPersistent()); + assertTrue(Util.isEmpty((replicatedRegatta.getSeries()))); + assertNull(replicatedRegatta.getDefaultCourseArea()); + assertTrue(regattaId.equals(replicatedRegatta.getId())); + RaceDefinition replicatedRace = replicatedRegatta.getRaceByName(raceName); + assertNotNull(replicatedRace); + Map.Entry replicatedCompetitorAndBoat = replicatedRace.getCompetitorsAndTheirBoats().entrySet().iterator().next(); + Competitor replicatedCompetitor = replicatedCompetitorAndBoat.getKey(); + Boat replicatedBoat = replicatedCompetitorAndBoat.getValue(); + assertNotSame(replicatedBoat, boat); + assertEquals(boat.getId(), replicatedBoat.getId()); + assertEquals(boat.getName(), replicatedBoat.getName()); + assertEquals(boat.getBoatClass().getName(), replicatedBoat.getBoatClass().getName()); + assertEquals(boat.getSailID(), replicatedBoat.getSailID()); + assertEquals(boat.getColor(), replicatedBoat.getColor()); + + // now update the boat on master using replicating operation + final String newBoatName = "Der Vogel, der mit dem Kiel zieht"; + master.apply(new UpdateBoat(boat.getId().toString(), newBoatName, boat.getColor(), boat.getSailID())); + Thread.sleep(1000); + assertEquals(newBoatName, replicatedBoat.getName()); // expect in-place update of existing boat in replica + + // now allow for resetting to default through some event, such as receiving a GPS position + master.apply(new AllowBoatResetToDefaults(Collections.singleton(boat.getId().toString()))); + // modify the boat on the master "from below" without an UpdateBoat operation, only locally: + master.getBaseDomainFactory().getCompetitorStore().updateBoat(boat.getId().toString(), boatName, boat.getColor(), boat.getSailID()); + + + final RegattaAndRaceIdentifier raceIdentifier = masterRegatta.getRaceIdentifier(raceDefinition); + DynamicTrackedRace trackedRace = (DynamicTrackedRace) master.apply(new CreateTrackedRace(raceIdentifier, + EmptyWindStore.INSTANCE, /* delayToLiveInMillis */ 3000, + /* millisecondsOverWhichToAverageWind */ 30000l, /* millisecondsOverWhichToAverageSpeed */ 30000l)); + trackedRace.getTrack(competitor).addGPSFix(new GPSFixMovingImpl(new DegreePosition(49.425, 8.293), MillisecondsTimePoint.now(), + new KnotSpeedWithBearingImpl(12.3, new DegreeBearingImpl(242.3)))); + Thread.sleep(1000); + TrackedRace replicatedTrackedRace = replica.getTrackedRace(raceIdentifier); + assertNotNull(replicatedTrackedRace); + Boat replicatedBoat2 = replicatedTrackedRace.getBoatOfCompetitor(replicatedCompetitor); + assertNotNull(replicatedBoat2); + + assertEquals(boatName, replicatedBoat2.getName()); + } + + @Test + public void testBoatCreationReplication() throws InterruptedException, URISyntaxException { + final String boatName = "Kielzugvogel 123"; + + BoatClass boatClass = new BoatClassImpl("Kielzugvogel", true); + Boat boat = master.getBaseDomainFactory().getOrCreateBoat(123, boatName, boatClass, "GER 123", null); + Thread.sleep(1000); + assertTrue(StreamSupport.stream(replica.getBaseDomainFactory().getCompetitorStore().getBoats().spliterator(), /* parallel */ false).anyMatch( + b-> b.getId().equals(boat.getId()))); + } +} \ No newline at end of file diff --git a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/CompetitorReplicationTest.java b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/CompetitorReplicationTest.java index 25f04b9b35a..5d6b168e9ff 100755 --- a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/CompetitorReplicationTest.java +++ b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/CompetitorReplicationTest.java @@ -9,10 +9,14 @@ import static org.junit.Assert.assertTrue; import java.net.URI; import java.net.URISyntaxException; import java.util.Collections; +import java.util.HashMap; +import java.util.Map; import java.util.stream.StreamSupport; import org.junit.Test; +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.CompetitorStore; import com.sap.sailing.domain.base.DomainFactory; @@ -76,22 +80,26 @@ public class CompetitorReplicationTest extends AbstractServerReplicationTest { URI flagImageURI = new URI("http://www.sapsailing.com"); Iterable series = Collections.emptyList(); Regatta masterRegatta = master.createRegatta(RegattaImpl.getDefaultName(baseEventName, boatClassName), boatClassName, - /*startDate*/ null, /*endDate*/ null, regattaId, series, + /* canBoatsOfCompetitorsChangePerRace */ true, /*startDate*/ null, /*endDate*/ null, regattaId, series, /* persistent */ true, DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), null, /*buoyZoneRadiusInHullLengths*/2.0, /* useStartTimeInference */ true, /* controlTrackingFromStartAndFinishTimes */ false, OneDesignRankingMetric::new); Iterable emptyWaypointList = Collections.emptyList(); final String competitorName = "Der mit dem Kiel zieht"; + final String competitorShortName = "DK"; + final BoatClass boatClass = new BoatClassImpl("505", /* typicallyStartsUpwind */true); Competitor competitor = master.getBaseDomainFactory().getOrCreateCompetitor( - 123, competitorName, Color.RED, "someone@nowhere.de", flagImageURI, + 123, competitorName, competitorShortName, Color.RED, "someone@nowhere.de", flagImageURI, new TeamImpl("STG", Collections.singleton(new PersonImpl(competitorName, new NationalityImpl("GER"), /* dateOfBirth */null, "This is famous " + competitorName)), new PersonImpl("Rigo van Maas", new NationalityImpl("NED"), /* dateOfBirth */null, "This is Rigo, the coach")), - new BoatImpl(competitorName + "'s boat", new BoatClassImpl("505", /* typicallyStartsUpwind */true), /* sailID */ null), /* timeOnTimeFactor */ null, /* timeOnDistanceAllowanceInSecondsPerNauticalMile */ null, null); - Iterable competitors = Collections.singleton(competitor); + /* timeOnTimeFactor */ null, /* timeOnDistanceAllowanceInSecondsPerNauticalMile */ null, null); + Boat boat = new BoatImpl(competitor.getId(), competitorName + "'s boat", boatClass, /* sailID */ null); + Map competitorsAndBoats = new HashMap<>(); + competitorsAndBoats.put(competitor, boat); final String raceName = "Test Race"; RaceDefinition raceDefinition = new RaceDefinitionImpl(raceName, new CourseImpl("Empty Course", emptyWaypointList), - masterRegatta.getBoatClass(), competitors); + masterRegatta.getBoatClass(), competitorsAndBoats); master.apply(new AddRaceDefinition(masterRegatta.getRegattaIdentifier(), raceDefinition)); Thread.sleep(1000); Regatta replicatedRegatta = replica.getRegatta(new RegattaName(masterRegatta.getName())); @@ -102,28 +110,33 @@ public class CompetitorReplicationTest extends AbstractServerReplicationTest { assertTrue(regattaId.equals(replicatedRegatta.getId())); RaceDefinition replicatedRace = replicatedRegatta.getRaceByName(raceName); assertNotNull(replicatedRace); - Competitor replicatedCompetitor = replicatedRace.getCompetitors().iterator().next(); + Map.Entry replicatedCompetitorAndBoat = replicatedRace.getCompetitorsAndTheirBoats().entrySet().iterator().next(); + Competitor replicatedCompetitor = replicatedCompetitorAndBoat.getKey(); + Boat replicatedBoat = replicatedCompetitorAndBoat.getValue(); assertNotSame(replicatedCompetitor, competitor); assertEquals(competitor.getId(), replicatedCompetitor.getId()); assertEquals(competitor.getName(), replicatedCompetitor.getName()); + assertEquals(competitor.getShortName(), replicatedCompetitor.getShortName()); assertEquals(competitor.getColor(), replicatedCompetitor.getColor()); assertEquals(competitor.getFlagImage(), replicatedCompetitor.getFlagImage()); - assertEquals(competitor.getBoat().getSailID(), replicatedCompetitor.getBoat().getSailID()); + assertEquals(boat.getSailID(), replicatedBoat.getSailID()); assertEquals(competitor.getTeam().getNationality(), replicatedCompetitor.getTeam().getNationality()); // now update competitor on master using replicating operation final String newCompetitorName = "Der Vogel, der mit dem Kiel zieht"; - master.apply(new UpdateCompetitor(competitor.getId().toString(), newCompetitorName, competitor.getColor(), competitor.getEmail(), competitor.getBoat().getSailID(), competitor.getTeam().getNationality(), + final String newCompetitorShortName = "VK"; + master.apply(new UpdateCompetitor(competitor.getId().toString(), newCompetitorName, newCompetitorShortName, competitor.getColor(), competitor.getEmail(), competitor.getTeam().getNationality(), competitor.getTeam().getImage(), competitor.getFlagImage(), /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null)); Thread.sleep(1000); assertEquals(newCompetitorName, replicatedCompetitor.getName()); // expect in-place update of existing competitor in replica + assertEquals(newCompetitorShortName, replicatedCompetitor.getShortName()); // now allow for resetting to default through some event, such as receiving a GPS position master.apply(new AllowCompetitorResetToDefaults(Collections.singleton(competitor.getId().toString()))); // modify the competitor on the master "from below" without an UpdateCompetitor operation, only locally: - master.getBaseDomainFactory().getCompetitorStore().updateCompetitor(competitor.getId().toString(), competitorName, Color.RED, competitor.getEmail(), - competitor.getBoat().getSailID(), competitor.getTeam().getNationality(), competitor.getTeam().getImage(), competitor.getFlagImage(), + master.getBaseDomainFactory().getCompetitorStore().updateCompetitor(competitor.getId().toString(), competitorName, competitorShortName, Color.RED, competitor.getEmail(), + competitor.getTeam().getNationality(), competitor.getTeam().getImage(), competitor.getFlagImage(), /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null); final RegattaAndRaceIdentifier raceIdentifier = masterRegatta.getRaceIdentifier(raceDefinition); DynamicTrackedRace trackedRace = (DynamicTrackedRace) master.apply(new CreateTrackedRace(raceIdentifier, @@ -136,24 +149,25 @@ public class CompetitorReplicationTest extends AbstractServerReplicationTest { assertNotNull(replicatedTrackedRace); assertNotNull(replicatedTrackedRace.getTrack(replicatedCompetitor).getFirstRawFix()); assertEquals(competitorName, replicatedCompetitor.getName()); - } + assertEquals(competitorShortName, replicatedCompetitor.getShortName()); + } @Test public void testCompetitorCreationReplication() throws InterruptedException, URISyntaxException { final String competitorName = "Der mit dem Kiel zieht"; + final String shortCcompetitorName = "Kiel"; URI flagImageURI = new URI("http://www.sapsailing.com"); - Competitor competitor = master.getBaseDomainFactory().getOrCreateCompetitor( - 123, competitorName, Color.RED, "someone@nowhere.de", flagImageURI, + + Competitor competitor = master.getBaseDomainFactory().getOrCreateCompetitor( + 123, competitorName, shortCcompetitorName, Color.RED, "someone@nowhere.de", flagImageURI, new TeamImpl("STG", Collections.singleton(new PersonImpl(competitorName, new NationalityImpl("GER"), /* dateOfBirth */null, "This is famous " + competitorName)), new PersonImpl("Rigo van Maas", new NationalityImpl("NED"), /* dateOfBirth */null, "This is Rigo, the coach")), - new BoatImpl(competitorName + "'s boat", new BoatClassImpl("505", /* typicallyStartsUpwind */true), /* sailID */ null), /* timeOnTimeFactor */ null, /* timeOnDistanceAllowanceInSecondsPerNauticalMile */ null, null); Thread.sleep(1000); - assertTrue(StreamSupport.stream(replica.getBaseDomainFactory().getCompetitorStore().getCompetitors().spliterator(), /* parallel */ false).anyMatch( + assertTrue(StreamSupport.stream(replica.getBaseDomainFactory().getCompetitorStore().getAllCompetitors().spliterator(), /* parallel */ false).anyMatch( c-> c.getId().equals(competitor.getId()))); } - } \ No newline at end of file diff --git a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/DelayedLeaderboardCorrectionsReplicationTest.java b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/DelayedLeaderboardCorrectionsReplicationTest.java index bac6ec9ccc1..c362f2efcde 100755 --- a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/DelayedLeaderboardCorrectionsReplicationTest.java +++ b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/DelayedLeaderboardCorrectionsReplicationTest.java @@ -14,11 +14,11 @@ import org.junit.Test; 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.RaceColumn; import com.sap.sailing.domain.base.RaceColumnListener; import com.sap.sailing.domain.base.impl.DomainFactoryImpl; -import com.sap.sailing.domain.base.impl.DynamicCompetitor; import com.sap.sailing.domain.common.MaxPointsReason; import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; import com.sap.sailing.domain.common.RegattaNameAndRaceName; @@ -79,8 +79,8 @@ public class DelayedLeaderboardCorrectionsReplicationTest extends AbstractServer // resolved properly on the replica. final DomainFactory domainFactory = new DomainFactoryImpl((srlid)->null); BoatClass boatClass = domainFactory.getOrCreateBoatClass("29erXX", /* typicallyStartsUpwind */ true); - DynamicCompetitor hasso = AbstractLeaderboardTest.createCompetitor("Dr. Hasso Plattner"); // don't create competitor using CompetitorStore - final DynamicTrackedRace q2YellowTrackedRace = new MockedTrackedRaceWithFixedRank(hasso, /* rank */ 1, /* started */ false, boatClass) { + CompetitorWithBoat hassoWithBoat = AbstractLeaderboardTest.createCompetitorWithBoat("Dr. Hasso Plattner"); // don't create competitor using CompetitorStore + final DynamicTrackedRace q2YellowTrackedRace = new MockedTrackedRaceWithFixedRank(hassoWithBoat, /* rank */ 1, /* started */ false, boatClass) { private static final long serialVersionUID = 1234L; @Override public RegattaAndRaceIdentifier getRaceIdentifier() { @@ -98,9 +98,9 @@ public class DelayedLeaderboardCorrectionsReplicationTest extends AbstractServer master.apply(new AddColumnToLeaderboard(Q2, leaderboardName, /* medalRace */ false)); // uses leaderboard's default fleet master.apply(new ConnectTrackedRaceToLeaderboardColumn(masterLeaderboard.getName(), Q2, /* default fleet */ masterLeaderboard.getFleet(null).getName(), q2YellowTrackedRace.getRaceIdentifier())); - master.apply(new UpdateLeaderboardMaxPointsReason(masterLeaderboard.getName(), Q2, hasso.getId().toString(), + master.apply(new UpdateLeaderboardMaxPointsReason(masterLeaderboard.getName(), Q2, hassoWithBoat.getId().toString(), MaxPointsReason.DNF, MillisecondsTimePoint.now())); - assertEquals(MaxPointsReason.DNF, masterLeaderboard.getMaxPointsReason(hasso, + assertEquals(MaxPointsReason.DNF, masterLeaderboard.getMaxPointsReason(hassoWithBoat, masterLeaderboard.getRaceColumnByName(Q2), MillisecondsTimePoint.now())); // re-load new master from persistence @@ -112,7 +112,7 @@ public class DelayedLeaderboardCorrectionsReplicationTest extends AbstractServer // expecting the correction to not be in the leaderboard because the competitor was not found in the // domain factory's competitor store while loading and therefore the score correction could not immediately // be applied to the leaderboard's real score correction - assertEquals(MaxPointsReason.NONE, masterLeaderboardReloaded.getMaxPointsReason(hasso, + assertEquals(MaxPointsReason.NONE, masterLeaderboardReloaded.getMaxPointsReason(hassoWithBoat, masterLeaderboardReloaded.getRaceColumnByName(Q2), MillisecondsTimePoint.now())); // replicate the re-loaded environment @@ -130,10 +130,10 @@ public class DelayedLeaderboardCorrectionsReplicationTest extends AbstractServer assertNotNull(replicaLeaderboard.getRaceColumnByName(Q2)); // so far, the replica should also only have the delayed corrections since during deserializing them the // replicatedHasso competitor was not yet known to the replica's competitor store: - assertEquals(MaxPointsReason.NONE, replicaLeaderboard.getMaxPointsReason(hasso, + assertEquals(MaxPointsReason.NONE, replicaLeaderboard.getMaxPointsReason(hassoWithBoat, replicaLeaderboard.getRaceColumnByName(Q2), MillisecondsTimePoint.now())); - logger.info("hasso object ID hash: "+System.identityHashCode(hasso)); + logger.info("hasso object ID hash: "+System.identityHashCode(hassoWithBoat)); Leaderboard newMasterLeaderboard = master.getLeaderboardByName(masterLeaderboard.getName()); SettableScoreCorrection newMasterScoreCorrections = newMasterLeaderboard.getScoreCorrection(); RaceColumn newMasterQ2 = newMasterLeaderboard.getRaceColumnByName(Q2); @@ -151,13 +151,13 @@ public class DelayedLeaderboardCorrectionsReplicationTest extends AbstractServer master.apply(new ConnectTrackedRaceToLeaderboardColumn(masterLeaderboard.getName(), Q2, /* default fleet */ masterLeaderboard.getFleet(null).getName(), q2YellowTrackedRace.getRaceIdentifier())); logger.info("got score correction for competitor "+System.identityHashCode(newMasterScoreCorrections.getCompetitorsThatHaveCorrectionsIn(newMasterQ2).iterator().next())); - assertTrue(Util.contains(newMasterScoreCorrections.getCompetitorsThatHaveCorrectionsIn(newMasterQ2), hasso)); // now score corrections must have been applied from Delayed... + assertTrue(Util.contains(newMasterScoreCorrections.getCompetitorsThatHaveCorrectionsIn(newMasterQ2), hassoWithBoat)); // now score corrections must have been applied from Delayed... // now the delayed corrections are expected to have been resolved: - assertEquals(MaxPointsReason.DNF, master.getLeaderboardByName(leaderboardName).getMaxPointsReason(hasso, + assertEquals(MaxPointsReason.DNF, master.getLeaderboardByName(leaderboardName).getMaxPointsReason(hassoWithBoat, master.getLeaderboardByName(leaderboardName).getRaceColumnByName(Q2), MillisecondsTimePoint.now())); Thread.sleep(1000); // wait for the tracked race to column connection to be replicated - assertNotNull(replicaLeaderboard.getRaceColumnByName(Q2).getTrackedRace(hasso)); - assertEquals(MaxPointsReason.DNF, replicaLeaderboard.getMaxPointsReason(hasso, + assertNotNull(replicaLeaderboard.getRaceColumnByName(Q2).getTrackedRace(hassoWithBoat)); + assertEquals(MaxPointsReason.DNF, replicaLeaderboard.getMaxPointsReason(hassoWithBoat, replicaLeaderboard.getRaceColumnByName(Q2), MillisecondsTimePoint.now())); } diff --git a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/InitialLoadReplicationObjectIdentityTest.java b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/InitialLoadReplicationObjectIdentityTest.java index 27a063437b1..d80cbf182fd 100755 --- a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/InitialLoadReplicationObjectIdentityTest.java +++ b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/InitialLoadReplicationObjectIdentityTest.java @@ -10,9 +10,11 @@ import static org.junit.Assert.assertTrue; import java.util.ArrayList; import java.util.Collections; import java.util.Date; +import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.UUID; @@ -24,8 +26,10 @@ import com.sap.sailing.domain.abstractlog.race.RaceLog; import com.sap.sailing.domain.abstractlog.race.RaceLogEvent; import com.sap.sailing.domain.abstractlog.race.RaceLogWindFixEvent; import com.sap.sailing.domain.abstractlog.race.impl.RaceLogWindFixEventImpl; +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.Event; import com.sap.sailing.domain.base.RaceColumn; @@ -122,7 +126,7 @@ public class InitialLoadReplicationObjectIdentityTest extends AbstractServerRepl final String boatClassName = "49er"; final Iterable series = Collections.emptyList(); Regatta masterRegatta = master.createRegatta(RegattaImpl.getDefaultName(baseEventName, boatClassName), boatClassName, - /*startDate*/ null, /*endDate*/ null, UUID.randomUUID(), series, + /* canBoatsOfCompetitorsChangePerRace */ true, /*startDate*/ null, /*endDate*/ null, UUID.randomUUID(), series, /* persistent */ true, DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), null, /*buoyZoneRadiusInHullLengths*/2.0, /* useStartTimeInference */ true, /* controlTrackingFromStartAndFinishTimes */ false, OneDesignRankingMetric.CONSTRUCTOR); assertNotNull(master.getRegatta(masterRegatta.getRegattaIdentifier())); @@ -131,7 +135,7 @@ public class InitialLoadReplicationObjectIdentityTest extends AbstractServerRepl /* RaceDefinition and DynamicTrackedRace */ RaceDefinition masterRace = new RaceDefinitionImpl("Test Race", new CourseImpl("Test Course", Collections.emptyList()), - masterRegatta.getBoatClass(), Collections.emptyList()); + masterRegatta.getBoatClass(), Collections.emptyMap()); masterRegatta.addRace(masterRace); DynamicTrackedRace masterTrackedRace = master.createTrackedRace(new RegattaNameAndRaceName(masterRegatta.getName(), masterRace.getName()), master.getWindStore(), /* delayToLiveInMillis */ 3000, @@ -252,10 +256,10 @@ public class InitialLoadReplicationObjectIdentityTest extends AbstractServerRepl Regatta regatta = master.apply(addEventOperation); final String raceName1 = "Test Race 1"; final String raceName2 = "Test Race 2"; - Competitor competitor = TrackBasedTest.createCompetitor("The Same Competitor"); + CompetitorWithBoat competitor = TrackBasedTest.createCompetitorWithBoat("The Same Competitor"); final CourseImpl masterCourse = new CourseImpl("Test Course", new ArrayList()); - final ArrayList competitors = new ArrayList(); - competitors.add(competitor); + final Map competitors = new HashMap<>(); + competitors.put(competitor, competitor.getBoat()); RaceDefinition race1 = new RaceDefinitionImpl(raceName1, masterCourse, boatClass, competitors); AddRaceDefinition addRaceOperation1 = new AddRaceDefinition(new RegattaName(regatta.getName()), race1); master.apply(addRaceOperation1); diff --git a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/RaceLogReplicationTest.java b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/RaceLogReplicationTest.java index 395f020fb01..c489a1a89bf 100755 --- a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/RaceLogReplicationTest.java +++ b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/RaceLogReplicationTest.java @@ -181,7 +181,7 @@ public class RaceLogReplicationTest extends AbstractLogReplicationTest series = Collections.emptyList(); final UUID regattaId = UUID.randomUUID(); Regatta masterRegatta = master.createRegatta(RegattaImpl.getDefaultName(baseEventName, boatClassName), boatClassName, - /*startDate*/ null, /*endDate*/ null, regattaId, series, - /* persistent */ true, DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), null, /*buoyZoneRadiusInHullLengths*/2.0, /* useStartTimeInference */ true, /* controlTrackingFromStartAndFinishTimes */ false, OneDesignRankingMetric::new); + /* canBoatsOfCompetitorsChangePerRace */ true, /*startDate*/ null, /*endDate*/ null, regattaId, series, + /* persistent */ true, DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), null, + /*buoyZoneRadiusInHullLengths*/2.0, /* useStartTimeInference */ true, /* controlTrackingFromStartAndFinishTimes */ false, OneDesignRankingMetric::new); Thread.sleep(1000); @@ -58,7 +59,36 @@ public class RegattaReplicationTest extends AbstractServerReplicationTest { assertTrue(regattaId.equals(replicatedRegatta.getId())); assertNull(replicatedRegatta.getRegattaConfiguration()); } - + + @Test + public void testSpecificRegattaReplicationWithDifferentBoatsOfCompetitorsCanChangePerRaceSettings() throws InterruptedException { + final String baseEventName = "Kiel Week 2012"; + final String boatClass1Name = "49er"; + final String boatClass2Name = "49erFX"; + final boolean canBoatsOfCompetitorsChangePerRaceRegatta1 = true; + final boolean canBoatsOfCompetitorsChangePerRaceRegatta2 = false; + final Iterable series = Collections.emptyList(); + final UUID regattaId1 = UUID.randomUUID(); + final UUID regattaId2 = UUID.randomUUID(); + Regatta masterRegatta1 = master.createRegatta(RegattaImpl.getDefaultName(baseEventName, boatClass1Name), boatClass1Name, + canBoatsOfCompetitorsChangePerRaceRegatta1, /*startDate*/ null, /*endDate*/ null, regattaId1, series, + /* persistent */ true, DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), null, + /*buoyZoneRadiusInHullLengths*/2.0, /* useStartTimeInference */ true, /* controlTrackingFromStartAndFinishTimes */ false, OneDesignRankingMetric::new); + Regatta masterRegatta2 = master.createRegatta(RegattaImpl.getDefaultName(baseEventName, boatClass2Name), boatClass2Name, + canBoatsOfCompetitorsChangePerRaceRegatta2, /*startDate*/ null, /*endDate*/ null, regattaId2, series, + /* persistent */ true, DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), null, + /*buoyZoneRadiusInHullLengths*/2.0, /* useStartTimeInference */ true, /* controlTrackingFromStartAndFinishTimes */ false, OneDesignRankingMetric::new); + + Thread.sleep(1000); + + Regatta replicatedRegatta1 = replica.getRegatta(new RegattaName(masterRegatta1.getName())); + assertNotNull(replicatedRegatta1); + assertEquals(replicatedRegatta1.canBoatsOfCompetitorsChangePerRace(), canBoatsOfCompetitorsChangePerRaceRegatta1); + Regatta replicatedRegatta2 = replica.getRegatta(new RegattaName(masterRegatta2.getName())); + assertNotNull(replicatedRegatta2); + assertEquals(replicatedRegatta2.canBoatsOfCompetitorsChangePerRace(), canBoatsOfCompetitorsChangePerRaceRegatta2); + } + @Test public void testUpdateSpecificRegattaReplicationForCourseArea() throws InterruptedException { Regatta replicatedRegatta; @@ -74,7 +104,8 @@ public class RegattaReplicationTest extends AbstractServerReplicationTest { UUID currentCourseAreaId = null; Regatta masterRegatta = master.createRegatta(RegattaImpl.getDefaultName("Kiel Week 2012", "49er"), "49er", - /*startDate*/ null, /*endDate*/ null, UUID.randomUUID(), Collections.emptyList(), + /* canBoatsOfCompetitorsChangePerRace */ true, /*startDate*/ null, /*endDate*/ null, + UUID.randomUUID(), Collections.emptyList(), /* persistent */ true, DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), currentCourseAreaId, /*buoyZoneRadiusInHullLengths*/2.0, /* useStartTimeInference */ true, /* controlTrackingFromStartAndFinishTimes */ false, OneDesignRankingMetric::new); @@ -114,7 +145,6 @@ public class RegattaReplicationTest extends AbstractServerReplicationTest { assertNull(replicatedRegatta.getDefaultCourseArea()); } - @Test public void testUpdateSpecificRegattaReplicationForProcedureAndCourseDesignerAndConfig() throws InterruptedException { Regatta replicatedRegatta; @@ -126,7 +156,9 @@ public class RegattaReplicationTest extends AbstractServerReplicationTest { master.addCourseAreas(event.getId(), new String[] {"Alpha"}, new UUID[] {alphaCourseAreaId}); UUID currentCourseAreaId = null; - Regatta masterRegatta = master.createRegatta(RegattaImpl.getDefaultName("RR", "49er"), "49er", /*startDate*/ null, /*endDate*/ null, UUID.randomUUID(), Collections.emptyList(), + Regatta masterRegatta = master.createRegatta(RegattaImpl.getDefaultName("RR", "49er"), "49er", + /* canBoatsOfCompetitorsChangePerRace */ true, /*startDate*/ null, /*endDate*/ null, + UUID.randomUUID(), Collections.emptyList(), true, DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), currentCourseAreaId, /*buoyZoneRadiusInHullLengths*/2.0, /* useStartTimeInference */ true, /* controlTrackingFromStartAndFinishTimes */ false, OneDesignRankingMetric::new); @@ -173,7 +205,8 @@ public class RegattaReplicationTest extends AbstractServerReplicationTest { Arrays.asList(new Fleet[] { new FleetImpl("Gold", 1), new FleetImpl("Silver", 2) }), emptyRaceColumnNamesList, /* trackedRegattaRegistry */ null); Series medal = new SeriesImpl("Medal", /* isMedal */ true, /* isFleetsCanRunInParallel */ true, Arrays.asList(new Fleet[] { new FleetImpl("Medal") }), emptyRaceColumnNamesList, /* trackedRegattaRegistry */ null); - Regatta masterRegatta = master.createRegatta(RegattaImpl.getDefaultName(baseEventName, boatClassName), boatClassName, /*startDate*/ null, /*endDate*/ null, + Regatta masterRegatta = master.createRegatta(RegattaImpl.getDefaultName(baseEventName, boatClassName), boatClassName, + /* canBoatsOfCompetitorsChangePerRace */ true, /*startDate*/ null, /*endDate*/ null, UUID.randomUUID(), Arrays.asList(new Series[] { qualification, finals, medal }), /* persistent */ true, DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), null, /*buoyZoneRadiusInHullLengths*/2.0, /* useStartTimeInference */ true, /* controlTrackingFromStartAndFinishTimes */ false, OneDesignRankingMetric::new); Thread.sleep(1000); @@ -209,7 +242,8 @@ public class RegattaReplicationTest extends AbstractServerReplicationTest { final List emptyRaceColumnNamesList = Collections.emptyList(); Series qualification = new SeriesImpl("Qualification", /* isMedal */ false, /* isFleetsCanRunInParallel */ true, Arrays.asList(new Fleet[] { new FleetImpl("Yellow"), new FleetImpl("Blue") }), emptyRaceColumnNamesList, /* trackedRegattaRegistry */ null); - Regatta masterRegatta = master.createRegatta(RegattaImpl.getDefaultName(baseEventName, boatClassName), boatClassName, /*startDate*/ null, /*endDate*/ null, + Regatta masterRegatta = master.createRegatta(RegattaImpl.getDefaultName(baseEventName, boatClassName), boatClassName, + /* canBoatsOfCompetitorsChangePerRace */ true, /*startDate*/ null, /*endDate*/ null, UUID.randomUUID(), Arrays.asList(new Series[] { qualification }), /* persistent */ true, DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), null, /*buoyZoneRadiusInHullLengths*/2.0, /* useStartTimeInference */ true, /* controlTrackingFromStartAndFinishTimes */ false, OneDesignRankingMetric::new); Thread.sleep(1000); @@ -253,7 +287,8 @@ public class RegattaReplicationTest extends AbstractServerReplicationTest { final List emptyRaceColumnNamesList = Collections.emptyList(); Series qualification = new SeriesImpl("Qualification", /* isMedal */ false, /* isFleetsCanRunInParallel */ true, Arrays.asList(new Fleet[] { new FleetImpl("Yellow"), new FleetImpl("Blue") }), emptyRaceColumnNamesList, /* trackedRegattaRegistry */ null); - Regatta masterRegatta = master.createRegatta(RegattaImpl.getDefaultName(baseEventName, boatClassName), boatClassName, /*startDate*/ null, /*endDate*/ null, + Regatta masterRegatta = master.createRegatta(RegattaImpl.getDefaultName(baseEventName, boatClassName), boatClassName, + /* canBoatsOfCompetitorsChangePerRace */ true, /*startDate*/ null, /*endDate*/ null, UUID.randomUUID(), Arrays.asList(new Series[] { qualification }), /* persistent */ true, DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), null, /*buoyZoneRadiusInHullLengths*/2.0, /* useStartTimeInference */ true, /* controlTrackingFromStartAndFinishTimes */ false, @@ -291,7 +326,8 @@ public class RegattaReplicationTest extends AbstractServerReplicationTest { Event masterEvent = master.addEvent(eventName, /* eventDescription */ null, eventStartDate, eventEndDate, venueName, isPublic, UUID.randomUUID()); CourseArea masterCourseArea = master.addCourseAreas(masterEvent.getId(), new String[] {courseArea}, new UUID[] {UUID.randomUUID()})[0]; - Regatta masterRegatta = master.createRegatta(RegattaImpl.getDefaultName(eventName, boatClassName), boatClassName, /*startDate*/ null, /*endDate*/ null, UUID.randomUUID(), series, + Regatta masterRegatta = master.createRegatta(RegattaImpl.getDefaultName(eventName, boatClassName), boatClassName, + /* canBoatsOfCompetitorsChangePerRace */ true, /*startDate*/ null, /*endDate*/ null, UUID.randomUUID(), series, /* persistent */ true, DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), masterCourseArea.getId(), /*buoyZoneRadiusInHullLengths*/2.0, /* useStartTimeInference */ true, /* controlTrackingFromStartAndFinishTimes */ false, OneDesignRankingMetric::new); Thread.sleep(1000); diff --git a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/ServerReplicationTest.java b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/ServerReplicationTest.java index a0d15045c6b..bc1301194ad 100755 --- a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/ServerReplicationTest.java +++ b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/ServerReplicationTest.java @@ -8,10 +8,12 @@ import static org.junit.Assert.assertTrue; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.UUID; import org.junit.Test; +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; @@ -92,8 +94,7 @@ public class ServerReplicationTest extends AbstractServerReplicationTest { Regatta regatta = master.apply(addRegattaOperation); final String raceName = "Test Race"; final CourseImpl masterCourse = new CourseImpl("Test Course", new ArrayList()); - RaceDefinition race = new RaceDefinitionImpl(raceName, masterCourse, boatClass, - new ArrayList()); + RaceDefinition race = new RaceDefinitionImpl(raceName, masterCourse, boatClass, Collections.emptyMap()); AddRaceDefinition addRaceOperation = new AddRaceDefinition(new RegattaName(regatta.getName()), race); master.apply(addRaceOperation); masterCourse.addWaypoint(0, masterDomainFactory.createWaypoint(masterDomainFactory.getOrCreateMark("Mark1"), /*passingInstruction*/ null)); diff --git a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/TrackRaceBoatCompetitorMetadataReplicationTest.java b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/TrackRaceBoatCompetitorMetadataReplicationTest.java index ac4aca81dc8..8d0e6f8ea80 100644 --- a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/TrackRaceBoatCompetitorMetadataReplicationTest.java +++ b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/TrackRaceBoatCompetitorMetadataReplicationTest.java @@ -6,8 +6,11 @@ import static org.junit.Assert.assertNotSame; import java.net.URI; import java.net.URL; +import java.util.Arrays; +import java.util.Collections; import java.util.GregorianCalendar; import java.util.TimeZone; +import java.util.UUID; import org.junit.After; import org.junit.Before; @@ -15,16 +18,24 @@ import org.junit.Test; import com.sap.sailing.domain.base.Boat; import com.sap.sailing.domain.base.Competitor; +import com.sap.sailing.domain.base.Regatta; import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; +import com.sap.sailing.domain.common.RegattaName; +import com.sap.sailing.domain.common.dto.FleetDTO; +import com.sap.sailing.domain.leaderboard.impl.LowPoint; import com.sap.sailing.domain.racelog.impl.EmptyRaceLogStore; +import com.sap.sailing.domain.ranking.OneDesignRankingMetric; import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore; import com.sap.sailing.domain.test.AbstractTracTracLiveTest; import com.sap.sailing.domain.tracking.RaceHandle; import com.sap.sailing.domain.tracking.RaceTracker; import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParameters; import com.sap.sailing.domain.tracking.TrackedRace; +import com.sap.sailing.domain.tractracadapter.impl.DomainFactoryImpl; import com.sap.sailing.server.RacingEventService; import com.sap.sailing.server.operationaltransformation.CreateTrackedRace; +import com.sap.sailing.server.operationaltransformation.UpdateSeries; +import com.sap.sse.common.Color; import com.sap.sse.common.Util; import com.sap.sse.common.impl.MillisecondsTimePoint; import com.sap.sse.replication.OperationExecutionListener; @@ -68,7 +79,7 @@ public class TrackRaceBoatCompetitorMetadataReplicationTest extends AbstractServ } } }); - trackingParams = com.sap.sailing.domain.tractracadapter.DomainFactory.INSTANCE + trackingParams = new DomainFactoryImpl(master.getBaseDomainFactory()) .createTrackingConnectivityParameters(paramURL, liveURI, storedURI, courseDesignUpdateURI, startOfTracking, endOfTracking, /* delayToLiveInMillis */ 0l, /* offsetToStartTimeOfSimulatedRace */null, /*ignoreTracTracMarkPassings*/ false, EmptyRaceLogStore.INSTANCE, @@ -84,7 +95,19 @@ public class TrackRaceBoatCompetitorMetadataReplicationTest extends AbstractServ } private void startTrackingOnMaster() throws Exception { - racesHandle = master.addRace(/* regattaToAddTo */ null, trackingParams, /* timeoutInMilliseconds */ 60000); + final Regatta regatta = master.createRegatta("Test regatta", "J/70", + /* canBoatsOfCompetitorsChangePerRace==true because it's a league race we're using for this test */ true, + /* startDate */ null, /* endDate */ null, UUID.randomUUID(), + /* start with no series */ Collections.emptySet(), + /* persistent */ true, new LowPoint(), /* defaultCourseAreaId */ UUID.randomUUID(), + /* buoyZoneRadiusInHullLengths */ 2., /* useStartTimeInference */ false, /* controlTrackingFromStartAndFinishTimes */ false, + /* rankingMetricConstructor */ OneDesignRankingMetric::new); + final RegattaName regattaIdentifier = new RegattaName(regatta.getName()); + master.apply(new UpdateSeries(regattaIdentifier, "Default", "Default", /* isMedal */ false, /* isFleetsCanRunInParallel */ false, + /* resultDiscardingThresholds */ null, /* startsWithZeroScore */ false, /* firstColumnIsNonDiscardableCarryForward */ false, + /* hasSplitFleetContiguousScoring */ false, /* maximumNumberOfDiscards */ null, + Arrays.asList(new FleetDTO("Red", 0, Color.RED), new FleetDTO("Green", 0, Color.GREEN), new FleetDTO("Blue", 0, Color.BLUE)))); + racesHandle = master.addRace(/* regattaToAddTo */ regattaIdentifier, trackingParams, /* timeoutInMilliseconds */ 60000); } private void waitForTrackRaceReplicationTrigger() throws InterruptedException, IllegalAccessException { @@ -125,7 +148,8 @@ public class TrackRaceBoatCompetitorMetadataReplicationTest extends AbstractServ for (Competitor competitor : masterCompetitors) { Competitor replicaCompetitor = findCompetitor(replicaCompetitors, competitor); - switch (competitor.getBoat().getSailID()) { + Boat competitorBoat = masterTrackedRace.getBoatOfCompetitor(competitor); + switch (competitorBoat.getSailID()) { case boat1CompetitorName: compareBoatOfCompetitors(masterTrackedRace.getRace().getBoatOfCompetitor(competitor), replicaTrackedRace.getRace().getBoatOfCompetitor(replicaCompetitor), boat1Name, boat1Color); diff --git a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/TrackedRaceContentsReplicationTest.java b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/TrackedRaceContentsReplicationTest.java index 3a082388195..8edf2353aff 100755 --- a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/TrackedRaceContentsReplicationTest.java +++ b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/TrackedRaceContentsReplicationTest.java @@ -8,14 +8,16 @@ import static org.junit.Assert.assertSame; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.UUID; import org.junit.Before; import org.junit.Test; import com.mongodb.MongoException; +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; @@ -23,7 +25,6 @@ import com.sap.sailing.domain.base.Mark; import com.sap.sailing.domain.base.RaceDefinition; import com.sap.sailing.domain.base.Regatta; import com.sap.sailing.domain.base.Waypoint; -import com.sap.sailing.domain.base.impl.BoatImpl; import com.sap.sailing.domain.base.impl.CourseImpl; import com.sap.sailing.domain.base.impl.PersonImpl; import com.sap.sailing.domain.base.impl.RaceDefinitionImpl; @@ -76,19 +77,22 @@ public class TrackedRaceContentsReplicationTest extends AbstractServerReplicatio // FIXME use master DomainFactory; see bug 592 final DomainFactory masterDomainFactory = testSetUp.getMaster().getBaseDomainFactory(); BoatClass boatClass = masterDomainFactory.getOrCreateBoatClass(boatClassName, /* typicallyStartsUpwind */true); - competitor = masterDomainFactory.getCompetitorStore().getOrCreateCompetitor("GER 61", "Tina Lutz", Color.RED, "someone@nowhere.de", null, new TeamImpl("Tina Lutz + Susann Beucke", + competitor = masterDomainFactory.getCompetitorStore().getOrCreateCompetitor("GER 61", "Tina Lutz", "TL", Color.RED, "someone@nowhere.de", null, new TeamImpl("Tina Lutz + Susann Beucke", (List) Arrays.asList(new PersonImpl[] { new PersonImpl("Tina Lutz", masterDomainFactory.getOrCreateNationality("GER"), null, null), new PersonImpl("Tina Lutz", masterDomainFactory.getOrCreateNationality("GER"), null, null) }), new PersonImpl("Rigo de Mas", masterDomainFactory.getOrCreateNationality("NED"), null, null)), - new BoatImpl("GER 61", masterDomainFactory.getOrCreateBoatClass("470", /* typicallyStartsUpwind */ true), "GER 61"), /* timeOnTimeFactor */ null, /* timeOnDistanceAllowanceInSecondsPerNauticalMile */ null, null); + Boat boat = masterDomainFactory.getCompetitorStore().getOrCreateBoat("boat123", "boat123", + masterDomainFactory.getOrCreateBoatClass("470", /* typicallyStartsUpwind */ true), "GER 61", null); + Map competitorAndBoats = new HashMap<>(); + competitorAndBoats.put(competitor, boat); final String baseEventName = "Test Event"; AddDefaultRegatta addEventOperation = new AddDefaultRegatta(RegattaImpl.getDefaultName(baseEventName, boatClassName), boatClassName, /*startDate*/ null, /*endDate*/ null, UUID.randomUUID()); Regatta regatta = master.apply(addEventOperation); final String raceName = "Test Race"; final CourseImpl masterCourse = new CourseImpl("Test Course", new ArrayList()); - RaceDefinition race = new RaceDefinitionImpl(raceName, masterCourse, boatClass, Collections.singletonList(competitor)); + RaceDefinition race = new RaceDefinitionImpl(raceName, masterCourse, boatClass, competitorAndBoats); AddRaceDefinition addRaceOperation = new AddRaceDefinition(new RegattaName(regatta.getName()), race); master.apply(addRaceOperation); masterCourse.addWaypoint(0, masterDomainFactory.createWaypoint(masterDomainFactory.getOrCreateMark("Mark1"), /*passingInstruction*/ null)); diff --git a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/TrackedRaceWithGPSFixStoreContentsReplicationTest.java b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/TrackedRaceWithGPSFixStoreContentsReplicationTest.java index b469f061be3..0c70fdfa6c0 100644 --- a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/TrackedRaceWithGPSFixStoreContentsReplicationTest.java +++ b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/TrackedRaceWithGPSFixStoreContentsReplicationTest.java @@ -7,20 +7,21 @@ import static org.junit.Assert.assertNotSame; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.UUID; import org.junit.Before; import org.junit.Test; +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; import com.sap.sailing.domain.base.RaceDefinition; import com.sap.sailing.domain.base.Regatta; import com.sap.sailing.domain.base.Waypoint; -import com.sap.sailing.domain.base.impl.BoatImpl; import com.sap.sailing.domain.base.impl.CourseImpl; import com.sap.sailing.domain.base.impl.MarkImpl; import com.sap.sailing.domain.base.impl.PersonImpl; @@ -77,19 +78,22 @@ public class TrackedRaceWithGPSFixStoreContentsReplicationTest extends AbstractS final String boatClassName = "49er"; final DomainFactory masterDomainFactory = testSetUp.getMaster().getBaseDomainFactory(); BoatClass boatClass = masterDomainFactory.getOrCreateBoatClass(boatClassName, /* typicallyStartsUpwind */true); - competitor = masterDomainFactory.getCompetitorStore().getOrCreateCompetitor("GER 61", "Tina Lutz", Color.RED, "someone@nowhere.de", null, new TeamImpl("Tina Lutz + Susann Beucke", + BoatClass boatClass470 = DomainFactory.INSTANCE.getOrCreateBoatClass("470", /* typicallyStartsUpwind */ true); + competitor = masterDomainFactory.getCompetitorStore().getOrCreateCompetitor("GER 61", "Tina Lutz", "TL", Color.RED, "someone@nowhere.de", null, new TeamImpl("Tina Lutz + Susann Beucke", (List) Arrays.asList(new PersonImpl[] { new PersonImpl("Tina Lutz", DomainFactory.INSTANCE.getOrCreateNationality("GER"), null, null), new PersonImpl("Tina Lutz", DomainFactory.INSTANCE.getOrCreateNationality("GER"), null, null) }), new PersonImpl("Rigo de Mas", DomainFactory.INSTANCE.getOrCreateNationality("NED"), null, null)), - new BoatImpl("GER 61", DomainFactory.INSTANCE.getOrCreateBoatClass("470", /* typicallyStartsUpwind */ true), "GER 61"), /* timeOnTimeFactor */ null, /* timeOnDistanceAllowanceInSecondsPerNauticalMile */ null, null); + Boat boat = masterDomainFactory.getCompetitorStore().getOrCreateBoat("boat", "GER 61", boatClass470, "GER 61", null); final String baseEventName = "Test Event"; AddDefaultRegatta addEventOperation = new AddDefaultRegatta(RegattaImpl.getDefaultName(baseEventName, boatClassName), boatClassName, /*startDate*/ null, /*endDate*/ null, UUID.randomUUID()); Regatta regatta = master.apply(addEventOperation); final String raceName = "Test Race"; final CourseImpl masterCourse = new CourseImpl("Test Course", new ArrayList()); - RaceDefinition race = new RaceDefinitionImpl(raceName, masterCourse, boatClass, Collections.singletonList(competitor)); + final Map competitorsAndBoats = new HashMap<>(); + competitorsAndBoats.put(competitor, boat); + RaceDefinition race = new RaceDefinitionImpl(raceName, masterCourse, boatClass, competitorsAndBoats); AddRaceDefinition addRaceOperation = new AddRaceDefinition(new RegattaName(regatta.getName()), race); master.apply(addRaceOperation); masterCourse.addWaypoint(0, masterDomainFactory.createWaypoint(masterDomainFactory.getOrCreateMark("Mark1"), /*passingInstruction*/ null)); diff --git a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/statistics/StatisticsTest.java b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/statistics/StatisticsTest.java old mode 100644 new mode 100755 index 17e91a94394..7dcc02f55b1 --- a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/statistics/StatisticsTest.java +++ b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/statistics/StatisticsTest.java @@ -7,13 +7,16 @@ import static org.mockito.Mockito.mock; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Optional; import org.junit.Before; import org.junit.Test; import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver; +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; @@ -52,9 +55,10 @@ public class StatisticsTest { private static final long START_OF_RACE = 200; private static final long END_OF_TRACKING = 400; private final BoatClass boatClass = DomainFactory.INSTANCE.getOrCreateBoatClass("49er"); - private final Competitor comp = DomainFactory.INSTANCE.getOrCreateCompetitor("comp", "comp", null, null, null, null, - new BoatImpl("boat", boatClass, "DE 12345"), /* timeOnTimeFactor */ null, + private final Competitor comp = DomainFactory.INSTANCE.getOrCreateCompetitor("comp", "comp", "c", null, null, null, null, + /* timeOnTimeFactor */ null, /* timeOnDistanceAllowanceInSecondsPerNauticalMile */ null, null); + private final Boat boat = new BoatImpl("boat", "b", boatClass, "DE 12345"); private final Mark mark1 = DomainFactory.INSTANCE.getOrCreateMark("mark1"); private final Mark mark2 = DomainFactory.INSTANCE.getOrCreateMark("mark2"); private final Mark mark3 = DomainFactory.INSTANCE.getOrCreateMark("mark3"); @@ -68,12 +72,14 @@ public class StatisticsTest { @Before public void setUp() { regatta = new DynamicTrackedRegattaImpl(new RegattaImpl(EmptyRaceLogStore.INSTANCE, - EmptyRegattaLogStore.INSTANCE, RegattaImpl.getDefaultName("regatta", boatClass.getName()), boatClass, + EmptyRegattaLogStore.INSTANCE, RegattaImpl.getDefaultName("regatta", boatClass.getName()), boatClass, false, /* startDate */ null, /* endDate */null, null, null, "a", null)); final Course course = new CourseImpl("course", Arrays.asList(new Waypoint[] { waypoint1, waypoint2, waypoint3 })); - RaceDefinition race = new RaceDefinitionImpl("race", course, boatClass, Arrays.asList(comp)); + Map competitors = new HashMap<>(); + competitors.put(comp, boat); + RaceDefinition race = new RaceDefinitionImpl("race", course, boatClass, competitors); trackedRace = new DynamicTrackedRaceImpl(regatta, race, Collections.emptyList(), EmptyWindStore.INSTANCE, 0, 0, 0, /* useMarkPassingCalculator */ false, OneDesignRankingMetric::new, diff --git a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/ApplyScoresFromRaceLogTest.java b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/ApplyScoresFromRaceLogTest.java index a1851511ceb..711ccab3e52 100644 --- a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/ApplyScoresFromRaceLogTest.java +++ b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/ApplyScoresFromRaceLogTest.java @@ -28,8 +28,6 @@ import com.sap.sailing.domain.base.DomainFactory; import com.sap.sailing.domain.base.RaceColumn; import com.sap.sailing.domain.base.Regatta; import com.sap.sailing.domain.base.configuration.impl.EmptyRegattaConfiguration; -import com.sap.sailing.domain.base.impl.BoatClassImpl; -import com.sap.sailing.domain.base.impl.BoatImpl; import com.sap.sailing.domain.base.impl.NationalityImpl; import com.sap.sailing.domain.base.impl.PersonImpl; import com.sap.sailing.domain.base.impl.TeamImpl; @@ -63,13 +61,12 @@ public class ApplyScoresFromRaceLogTest extends LeaderboardScoringAndRankingTest for (int i=0; inull); - DynamicCompetitor template = AbstractLeaderboardTest.createCompetitor("Test Competitor"); - Competitor competitor = persistentStore1.getOrCreateCompetitor(template.getId(), template.getName(), + DynamicCompetitor template = (DynamicCompetitor) AbstractLeaderboardTest.createCompetitor("Test Competitor"); + Competitor competitor = persistentStore1.getOrCreateCompetitor(template.getId(), template.getName(), template.getShortName(), template.getColor(), template.getEmail(), template.getFlagImage(), template.getTeam(), - template.getBoat(), /* timeOnTimeFactor */ 1.234, /* timeOnDistanceAllowanceInSecondsPerNauticalMile */ Duration.ONE_SECOND.times(730), null); + /* timeOnTimeFactor */ 1.234, /* timeOnDistanceAllowanceInSecondsPerNauticalMile */ Duration.ONE_SECOND.times(730), null); CompetitorStore persistentStore2 = new PersistentCompetitorStore( PersistenceFactory.INSTANCE.getDefaultMongoObjectFactory(), /* clearStore */false, null, /* raceLogResolver */ (srlid)->null); @@ -66,10 +74,11 @@ public class CompetitorStoreTest { assertEquals(template.getTeam().getNationality(), competitor2.getTeam().getNationality()); assertEquals(1.234, competitor2.getTimeOnTimeFactor(), 0.0000001); assertEquals(730, competitor2.getTimeOnDistanceAllowancePerNauticalMile().asSeconds(), 0.0000001); - DynamicTeam differentTeam = AbstractLeaderboardTest.createCompetitor("Test Competitor").getTeam(); + + DynamicTeam differentTeam = (DynamicTeam) AbstractLeaderboardTest.createCompetitor("Test Competitor").getTeam(); differentTeam.setNationality(DomainFactory.INSTANCE.getOrCreateNationality("GHA")); // Ghana - Competitor competitor3 = persistentStore2.getOrCreateCompetitor(template.getId(), template.getName(), - template.getColor(), template.getEmail(), template.getFlagImage(), differentTeam, template.getBoat(), /* timeOnTimeFactor */ + Competitor competitor3 = persistentStore2.getOrCreateCompetitor(template.getId(), template.getName(), template.getShortName(), + template.getColor(), template.getEmail(), template.getFlagImage(), differentTeam, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowanceInSecondsPerNauticalMile */null, null); assertSame(competitor2, competitor3); // use existing competitor despite the different team assertNotSame(differentTeam, competitor2.getTeam()); // team expected to remain unchanged @@ -77,10 +86,75 @@ public class CompetitorStoreTest { // expected to remain unchanged // now mark the competitor as to update from defaults persistentStore2.allowCompetitorResetToDefaults(competitor2); - Competitor competitor4 = persistentStore2.getOrCreateCompetitor(template.getId(), template.getName(), - template.getColor(), template.getEmail(), template.getFlagImage(), differentTeam, template.getBoat(), /* timeOnTimeFactor */ + Competitor competitor4 = persistentStore2.getOrCreateCompetitor(template.getId(), template.getName(), template.getShortName(), + template.getColor(), template.getEmail(), template.getFlagImage(), differentTeam, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowanceInSecondsPerNauticalMile */null, null); assertSame(competitor2, competitor4); // expecting an in-place update assertEquals(differentTeam.getNationality(), competitor4.getTeam().getNationality()); } + + @Test + public void testPersistentCompetitorWithBoatStore() { + CompetitorStore persistentStore1 = new PersistentCompetitorStore( + PersistenceFactory.INSTANCE.getDefaultMongoObjectFactory(), /* clearStore */true, null, /* raceLogResolver */ (srlid)->null); + DynamicCompetitorWithBoat template = (DynamicCompetitorWithBoat) AbstractLeaderboardTest.createCompetitorWithBoat("Test Competitor"); + CompetitorWithBoat competitor = persistentStore1.getOrCreateCompetitorWithBoat(template.getId(), template.getName(), template.getShortName(), + template.getColor(), template.getEmail(), template.getFlagImage(), template.getTeam(), + /* timeOnTimeFactor */ 1.234, /* timeOnDistanceAllowanceInSecondsPerNauticalMile */ Duration.ONE_SECOND.times(730), null, template.getBoat()); + + CompetitorStore persistentStore2 = new PersistentCompetitorStore( + PersistenceFactory.INSTANCE.getDefaultMongoObjectFactory(), /* clearStore */false, null, /* raceLogResolver */ (srlid)->null); + CompetitorWithBoat competitor2 = persistentStore2.getExistingCompetitorWithBoatById(template.getId()); + assertNotSame(competitor2, template); // the new store loads new instances from the database + assertEquals(template.getId(), competitor2.getId()); + assertEquals(template.getTeam().getNationality(), competitor2.getTeam().getNationality()); + assertEquals(1.234, competitor2.getTimeOnTimeFactor(), 0.0000001); + assertEquals(730, competitor2.getTimeOnDistanceAllowancePerNauticalMile().asSeconds(), 0.0000001); + + DynamicTeam differentTeam = (DynamicTeam) AbstractLeaderboardTest.createCompetitorWithBoat("Test Competitor").getTeam(); + differentTeam.setNationality(DomainFactory.INSTANCE.getOrCreateNationality("GHA")); // Ghana + CompetitorWithBoat competitor3 = persistentStore2.getOrCreateCompetitorWithBoat(template.getId(), template.getName(), template.getShortName(), + template.getColor(), template.getEmail(), template.getFlagImage(), differentTeam, /* timeOnTimeFactor */ + null, /* timeOnDistanceAllowanceInSecondsPerNauticalMile */null, null, template.getBoat()); + assertSame(competitor2, competitor3); // use existing competitor despite the different team + assertNotSame(differentTeam, competitor2.getTeam()); // team expected to remain unchanged + assertEquals(competitor.getTeam().getNationality(), competitor3.getTeam().getNationality()); // no updatability requested; nationality + // expected to remain unchanged + // now mark the competitor as to update from defaults + persistentStore2.allowCompetitorResetToDefaults(competitor2); + CompetitorWithBoat competitor4 = persistentStore2.getOrCreateCompetitorWithBoat(template.getId(), template.getName(), template.getShortName(), + template.getColor(), template.getEmail(), template.getFlagImage(), differentTeam, /* timeOnTimeFactor */ + null, /* timeOnDistanceAllowanceInSecondsPerNauticalMile */null, null, template.getBoat()); + assertSame(competitor2, competitor4); // expecting an in-place update + assertEquals(differentTeam.getNationality(), competitor4.getTeam().getNationality()); + } + + @Test + public void testPersistentBoatStore() { + CompetitorStore persistentStore1 = new PersistentCompetitorStore( + PersistenceFactory.INSTANCE.getDefaultMongoObjectFactory(), /* clearStore */true, null, /* raceLogResolver */ (srlid)->null); + DynamicBoat template = new BoatImpl("id-12345", "Morning Glory", new BoatClassImpl("505", /* typicallyStartsUpwind */ true), + "GER 1234", new RGBColor(255, 0, 0)); + persistentStore1.getOrCreateBoat(template.getId(), template.getName(), template.getBoatClass(), + template.getSailID(), template.getColor()); + + CompetitorStore persistentStore2 = new PersistentCompetitorStore( + PersistenceFactory.INSTANCE.getDefaultMongoObjectFactory(), /* clearStore */false, null, /* raceLogResolver */ (srlid)->null); + Boat boat2 = persistentStore2.getExistingBoatById(template.getId()); + assertNotSame(boat2, template); // the new store loads new instances from the database + assertEquals(template.getId(), boat2.getId()); + + String differentSailId = "USA 1234"; + Boat boat3 = persistentStore2.getOrCreateBoat(template.getId(), template.getName(), template.getBoatClass(), + differentSailId, template.getColor()); + assertSame(boat2, boat3); // use existing boat despite the different sailID + assertNotSame(differentSailId, boat2.getSailID()); // sailID expected to remain unchanged + + // now mark the boat as to update from defaults + persistentStore2.allowBoatResetToDefaults(boat2); + Boat boat4 = persistentStore2.getOrCreateBoat(template.getId(), template.getName(), template.getBoatClass(), + differentSailId, template.getColor()); + assertSame(boat2, boat4); // expecting an in-place update + assertEquals(differentSailId, boat4.getSailID()); + } } diff --git a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/LeaderboardDiscardingRulesTest.java b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/LeaderboardDiscardingRulesTest.java index c54f8debcad..31d63af6d53 100644 --- a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/LeaderboardDiscardingRulesTest.java +++ b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/LeaderboardDiscardingRulesTest.java @@ -8,7 +8,7 @@ import org.junit.Before; import org.junit.Test; 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.common.MaxPointsReason; import com.sap.sailing.domain.common.NoWindException; @@ -66,13 +66,13 @@ public class LeaderboardDiscardingRulesTest { assertNotNull(leaderboard); BoatClass boatClass = DomainFactory.INSTANCE.getOrCreateBoatClass("29erXX", /* typicallyStartsUpwind */ true); - Competitor hasso = AbstractLeaderboardTest.createCompetitor("Dr. Hasso Plattner"); + CompetitorWithBoat hasso = AbstractLeaderboardTest.createCompetitorWithBoat("Dr. Hasso Plattner"); final TrackedRace race1 = new MockedTrackedRaceWithFixedRank(hasso, /* rank */ 123, /* started */ true, boatClass); leaderboard.addRace(race1, "R1", /* medalRace */false); final TrackedRace race2 = new MockedTrackedRaceWithFixedRank(hasso, /* rank */ 123, /* started */ true, boatClass); leaderboard.addRace(race2, "R2", /* medalRace */false); - Competitor wolfgang = AbstractLeaderboardTest.createCompetitor("Wolfgang Hunger"); + CompetitorWithBoat wolfgang = AbstractLeaderboardTest.createCompetitorWithBoat("Wolfgang Hunger"); final TrackedRace race3 = new MockedTrackedRaceWithFixedRank(wolfgang, /* rank */ 123, /* started */ true, boatClass); leaderboard.addRace(race3, "R3", /* medalRace */false); final TrackedRace race4 = new MockedTrackedRaceWithFixedRank(wolfgang, /* rank */ 124, /* started */ true, boatClass); @@ -105,19 +105,19 @@ public class LeaderboardDiscardingRulesTest { assertNotNull(leaderboard); BoatClass boatClass = DomainFactory.INSTANCE.getOrCreateBoatClass("ESS40", /* typicallyStartsUpwind */ true); - Competitor rasmus = AbstractLeaderboardTest.createCompetitor("Rasmus"); + CompetitorWithBoat rasmus = AbstractLeaderboardTest.createCompetitorWithBoat("Rasmus"); final TrackedRace race1 = new MockedTrackedRaceWithFixedRank(rasmus, /* rank */ 2, /* started */ true, boatClass); // score 2.0 leaderboard.addRace(race1, "R1", /* medalRace */false); final TrackedRace race2 = new MockedTrackedRaceWithFixedRank(rasmus, /* rank */ 2, /* started */ true, boatClass); // score 2.0 leaderboard.addRace(race2, "R2", /* medalRace */false); - Competitor jes = AbstractLeaderboardTest.createCompetitor("Jes"); + CompetitorWithBoat jes = AbstractLeaderboardTest.createCompetitorWithBoat("Jes"); final TrackedRace race3 = new MockedTrackedRaceWithFixedRank(jes, /* rank */ 2, /* started */ true, boatClass); // score 2.0 leaderboard.addRace(race3, "R3", /* medalRace */false); final TrackedRace race4 = new MockedTrackedRaceWithFixedRank(jes, /* rank */ 3, /* started */ true, boatClass); // score 1.0 leaderboard.addRace(race4, "R4", /* medalRace */false); - Competitor nobody = AbstractLeaderboardTest.createCompetitor("Nobody"); + CompetitorWithBoat nobody = AbstractLeaderboardTest.createCompetitorWithBoat("Nobody"); final TrackedRace race5 = new MockedTrackedRaceWithFixedRank(nobody, /* rank */ 3, /* started */ true, boatClass); // score 1.0 leaderboard.addRace(race5, "R5", /* medalRace */false); @@ -149,13 +149,13 @@ public class LeaderboardDiscardingRulesTest { assertNotNull(leaderboard); BoatClass boatClass = DomainFactory.INSTANCE.getOrCreateBoatClass("29erXX", /* typicallyStartsUpwind */ true); - Competitor hasso = AbstractLeaderboardTest.createCompetitor("Dr. Hasso Plattner"); + CompetitorWithBoat hasso = AbstractLeaderboardTest.createCompetitorWithBoat("Dr. Hasso Plattner"); final TrackedRace race1 = new MockedTrackedRaceWithFixedRank(hasso, /* rank */ 123, /* started */ true, boatClass); leaderboard.addRace(race1, "R1", /* medalRace */false); final TrackedRace race2 = new MockedTrackedRaceWithFixedRank(hasso, /* rank */ 123, /* started */ true, boatClass); leaderboard.addRace(race2, "R2", /* medalRace */false); - Competitor wolfgang = AbstractLeaderboardTest.createCompetitor("Wolfgang Hunger"); + CompetitorWithBoat wolfgang = AbstractLeaderboardTest.createCompetitorWithBoat("Wolfgang Hunger"); final TrackedRace race3 = new MockedTrackedRaceWithFixedRank(wolfgang, /* rank */ 123, /* started */ true, boatClass); leaderboard.addRace(race3, "R3", /* medalRace */false); final TrackedRace race4 = new MockedTrackedRaceWithFixedRank(wolfgang, /* rank */ 124, /* started */ true, boatClass); diff --git a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/LeaderboardStorageTest.java b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/LeaderboardStorageTest.java index 3a9b768f124..04ed2ad3a6a 100644 --- a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/LeaderboardStorageTest.java +++ b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/LeaderboardStorageTest.java @@ -4,8 +4,6 @@ import java.io.Serializable; import java.util.ArrayList; import java.util.List; -import junit.framework.TestCase; - import org.junit.Test; import com.mongodb.BasicDBList; @@ -14,8 +12,6 @@ import com.mongodb.DBCollection; import com.mongodb.DBCursor; import com.mongodb.DBObject; import com.sap.sailing.domain.base.Competitor; -import com.sap.sailing.domain.base.impl.BoatImpl; -import com.sap.sailing.domain.base.impl.DynamicBoat; import com.sap.sailing.domain.base.impl.DynamicPerson; import com.sap.sailing.domain.base.impl.DynamicTeam; import com.sap.sailing.domain.base.impl.NationalityImpl; @@ -27,6 +23,8 @@ import com.sap.sailing.server.RacingEventService; import com.sap.sailing.server.impl.RacingEventServiceImpl; import com.sap.sse.mongodb.MongoDBService; +import junit.framework.TestCase; + public class LeaderboardStorageTest extends TestCase { private static final String LEADERBOARD_NAME = "test"; @@ -58,10 +56,9 @@ public class LeaderboardStorageTest extends TestCase { List sailorList = new ArrayList(); sailorList.add(new PersonImpl("sailor", new NationalityImpl("GER"), null, "")); DynamicTeam team = new TeamImpl("team", sailorList, null); - DynamicBoat boat = new BoatImpl("woot", service.getBaseDomainFactory().getOrCreateBoatClass("H16"), "70155"); String competitorId = "testC"; - Competitor competitor = service.getBaseDomainFactory().getOrCreateCompetitor(competitorId, "Test C", null, - null, null, team, boat, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowanceInSecondsPerNauticalMile */ null, null); + Competitor competitor = service.getBaseDomainFactory().getOrCreateCompetitor(competitorId, "Test C", null, null, + null, null, team, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowanceInSecondsPerNauticalMile */ null, null); double carriedPoints = 2.0; leaderboard.setCarriedPoints(competitor, carriedPoints); diff --git a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/MasterDataImportTest.java b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/MasterDataImportTest.java index bbf9d3b7c25..6776e0477ef 100644 --- a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/MasterDataImportTest.java +++ b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/MasterDataImportTest.java @@ -17,9 +17,12 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Date; +import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; @@ -53,6 +56,7 @@ import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogRegisterCompe import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogDeviceCompetitorBravoMappingEventImpl; 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.CourseArea; @@ -189,6 +193,16 @@ public class MasterDataImportTest { service.getDB().dropDatabase(); } + private Map createCompetitorsAndBoatsMap(BoatClass boatClass, Iterable competitors) { + Map result = new LinkedHashMap<>(); + int i = 1; + for(Competitor c: competitors) { + Boat b = new BoatImpl(c.getId(), "Boat of " + c.getName(), boatClass, "GER " + i++, null); + result.put(c, b); + } + return result; + } + @Test public void testMasterDataImportWithoutHttpStack() throws MalformedURLException, IOException, InterruptedException, ClassNotFoundException { @@ -217,7 +231,7 @@ public class MasterDataImportTest { UUID regattaUUID = UUID.randomUUID(); Regatta regatta = sourceService.createRegatta( RegattaImpl.getDefaultName(TEST_REGATTA_NAME, TEST_BOAT_CLASS_NAME), TEST_BOAT_CLASS_NAME, - /* startDate */null, /* endDate */null, regattaUUID, series, true, new LowPoint(), courseAreaUUID, /*buoyZoneRadiusInHullLengths*/2.0, + /* canBoatsOfCompetitorsChangePerRace */ true, /* startDate */null, /* endDate */null, regattaUUID, series, true, new LowPoint(), courseAreaUUID, /*buoyZoneRadiusInHullLengths*/2.0, /* useStartTimeInference */ true, /* controlTrackingFromStartAndFinishTimes */ false, OneDesignRankingMetric::new); for (String name : raceColumnNames) { @@ -245,8 +259,7 @@ public class MasterDataImportTest { "Der Lennart halt"); DynamicTeam team = new TeamImpl("Pros", sailors, coach); BoatClass boatClass = new BoatClassImpl("H16", true); - DynamicBoat boat = new BoatImpl("Wingy", boatClass, "GER70133"); - CompetitorImpl competitor = new CompetitorImpl(competitorUUID, "Froderik", Color.RED, null, null, team, boat, /* timeOnTimeFactor */ + CompetitorImpl competitor = new CompetitorImpl(competitorUUID, "Froderik", "KYC", Color.RED, null, null, team, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */null, null); competitors.add(competitor); UUID competitorToSuppressUUID = UUID.randomUUID(); @@ -256,12 +269,11 @@ public class MasterDataImportTest { DynamicPerson coach2 = new PersonImpl("Peer Steinbrueck", new NationalityImpl("GER"), new Date(645487200000L), "Bester Coach"); DynamicTeam team2 = new TeamImpl("Noobs", sailors2, coach2); - DynamicBoat boat2 = new BoatImpl("LahmeEnte", boatClass, "GER1337"); - CompetitorImpl competitorToSuppress = new CompetitorImpl(competitorToSuppressUUID, "Merkel", Color.RED, null, - null, team2, boat2, /* timeOnTimeFactor */null, /* timeOnDistanceAllowancePerNauticalMile */null, null); + CompetitorImpl competitorToSuppress = new CompetitorImpl(competitorToSuppressUUID, "Merkel", "KYC", Color.RED, null, + null, team2, /* timeOnTimeFactor */null, /* timeOnDistanceAllowancePerNauticalMile */null, null); competitors.add(competitorToSuppress); RaceColumn raceColumn = leaderboard.getRaceColumnByName(raceColumnName); - TrackedRace trackedRace = new DummyTrackedRace(raceId, competitors, regatta, null, sourceService.getWindStore()); + TrackedRace trackedRace = new DummyTrackedRace(raceId, createCompetitorsAndBoatsMap(boatClass, competitors), regatta, null, sourceService.getWindStore()); raceColumn.setTrackedRace(testFleet1, trackedRace); @@ -386,7 +398,7 @@ public class MasterDataImportTest { Assert.assertNull(raceColumnOnTarget.getTrackedRace(raceColumnOnTarget.getFleetByName(testFleet1Name))); raceColumnOnTarget.setTrackedRace(raceColumnOnTarget.getFleets().iterator().next(), new DummyTrackedRace( - raceId, competitors, regattaOnTarget, null, sourceService.getWindStore())); + raceId, createCompetitorsAndBoatsMap(boatClass, competitors), regattaOnTarget, null, sourceService.getWindStore())); Assert.assertTrue(leaderboardOnTarget.getScoreCorrection().hasCorrectionFor(raceColumnOnTarget)); Competitor competitorOnTarget = domainFactory.getExistingCompetitorById(competitorUUID); @@ -394,7 +406,7 @@ public class MasterDataImportTest { competitorsCreatedOnTarget.add(competitorOnTarget); Fleet fleet1OnTarget = raceColumnOnTarget.getFleetByName(testFleet1.getName()); - TrackedRace trackedRaceForTarget = new DummyTrackedRace(raceId, competitorsCreatedOnTarget, regattaOnTarget, + TrackedRace trackedRaceForTarget = new DummyTrackedRace(raceId, createCompetitorsAndBoatsMap(boatClass, competitorsCreatedOnTarget), regattaOnTarget, null, sourceService.getWindStore()); raceColumnOnTarget.setTrackedRace(fleet1OnTarget, trackedRaceForTarget); @@ -544,7 +556,8 @@ public class MasterDataImportTest { UUID regattaUUID = UUID.randomUUID(); Regatta regatta = sourceService.createRegatta( RegattaImpl.getDefaultName(TEST_REGATTA_NAME, TEST_BOAT_CLASS_NAME), TEST_BOAT_CLASS_NAME, - /* startDate */null, /* endDate */null, regattaUUID, series, true, new LowPoint(), courseAreaUUID, /*buoyZoneRadiusInHullLengths*/2.0, + /* canBoatsOfCompetitorsChangePerRace */ true, /* startDate */null, /* endDate */null, + regattaUUID, series, true, new LowPoint(), courseAreaUUID, /*buoyZoneRadiusInHullLengths*/2.0, /* useStartTimeInference */ true, /* controlTrackingFromStartAndFinishTimes */ false, OneDesignRankingMetric::new); for (String name : raceColumnNames) { series.get(0).addRaceColumn(name, sourceService); @@ -568,8 +581,7 @@ public class MasterDataImportTest { "Der Lennart halt"); DynamicTeam team = new TeamImpl("Pros", sailors, coach); BoatClass boatClass = new BoatClassImpl("H16", true); - DynamicBoat boat = new BoatImpl("Wingy", boatClass, "GER70133"); - CompetitorImpl competitor = new CompetitorImpl(competitorUUID, "Froderik", Color.RED, null, null, team, boat, /* timeOnTimeFactor */ + CompetitorImpl competitor = new CompetitorImpl(competitorUUID, "Froderik", "KYC", Color.RED, null, null, team, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */null, null); competitors.add(competitor); UUID competitor2UUID = UUID.randomUUID(); @@ -577,12 +589,11 @@ public class MasterDataImportTest { sailors2.add(new PersonImpl("Test Mustermann", new NationalityImpl("GER"), new Date(645487200000L), "desc")); DynamicPerson coach2 = new PersonImpl("Max Test", new NationalityImpl("GER"), new Date(645487200000L), "desc"); DynamicTeam team2 = new TeamImpl("Pros2", sailors2, coach2); - DynamicBoat boat2 = new BoatImpl("FastBoat", boatClass, "GER70133"); - CompetitorImpl competitor2 = new CompetitorImpl(competitor2UUID, "Froderik", Color.RED, null, null, team2, - boat2, /* timeOnTimeFactor */null, /* timeOnDistanceAllowancePerNauticalMile */null, null); + CompetitorImpl competitor2 = new CompetitorImpl(competitor2UUID, "Froderik", "KYC", Color.RED, null, null, team2, + /* timeOnTimeFactor */null, /* timeOnDistanceAllowancePerNauticalMile */null, null); competitors.add(competitor2); RaceColumn raceColumn = leaderboard.getRaceColumnByName(raceColumnName); - TrackedRace trackedRace = new DummyTrackedRace(raceId, competitors, regatta, null, sourceService.getWindStore()); + TrackedRace trackedRace = new DummyTrackedRace(raceId, createCompetitorsAndBoatsMap(boatClass, competitors), regatta, null, sourceService.getWindStore()); raceColumn.setTrackedRace(testFleet1, trackedRace); @@ -653,7 +664,7 @@ public class MasterDataImportTest { competitorsCreatedOnTarget.add(competitorOnTarget); Fleet fleet1OnTarget = raceColumnOnTarget.getFleetByName(testFleet1.getName()); - TrackedRace trackedRaceForTarget = new DummyTrackedRace(raceId, competitorsCreatedOnTarget, regattaOnTarget, + TrackedRace trackedRaceForTarget = new DummyTrackedRace(raceId, createCompetitorsAndBoatsMap(boatClass, competitorsCreatedOnTarget), regattaOnTarget, null, sourceService.getWindStore()); raceColumnOnTarget.setTrackedRace(fleet1OnTarget, trackedRaceForTarget); @@ -701,8 +712,9 @@ public class MasterDataImportTest { series.add(new SeriesImpl("testSeries", false, /* isFleetsCanRunInParallel */ true, fleets, emptyRaceColumnNamesList, sourceService)); UUID regattaUUID = UUID.randomUUID(); Regatta regatta = sourceService.createRegatta(RegattaImpl.getDefaultName("testRegatta", "29er"), "29er", - /* startDate */null, /* endDate */null, regattaUUID, series, true, new LowPoint(), courseAreaUUID, /*buoyZoneRadiusInHullLengths*/2.0, - /* useStartTimeInference */ true, /* controlTrackingFromStartAndFinishTimes */ false, OneDesignRankingMetric::new); + /* canBoatsOfCompetitorsChangePerRace */ true, /* startDate */null, /* endDate */null, + regattaUUID, series, true, new LowPoint(), courseAreaUUID, /*buoyZoneRadiusInHullLengths*/2.0, + /* useStartTimeInference */ true, /* controlTrackingFromStartAndFinishTimes */ false, OneDesignRankingMetric::new); for (String name : raceColumnNames) { series.get(0).addRaceColumn(name, sourceService); } @@ -716,7 +728,6 @@ public class MasterDataImportTest { /* displayName */null, false, leaderboardNames, null, null); // Set tracked Race with competitors - Set competitors = new HashSet(); UUID competitorUUID = UUID.randomUUID(); Set sailors = new HashSet(); sailors.add(new PersonImpl("Froderik Poterson", new NationalityImpl("GER"), new Date(645487200000L), @@ -725,14 +736,15 @@ public class MasterDataImportTest { "Der Lennart halt"); DynamicTeam team = new TeamImpl("Pros", sailors, coach); BoatClass boatClass = new BoatClassImpl("H16", true); - DynamicBoat boat = new BoatImpl("Wingy", boatClass, "GER70133"); - CompetitorImpl competitor = new CompetitorImpl(competitorUUID, "Froderik", Color.RED, null, null, team, boat, /* timeOnTimeFactor */ + DynamicBoat boat = new BoatImpl("123", "Wingy", boatClass, "GER70133"); + CompetitorImpl competitor = new CompetitorImpl(competitorUUID, "Froderik", "KYC", Color.RED, null, null, team, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */null, null); - competitors.add(competitor); + Map competitorsAndBoats = new HashMap<>(); + competitorsAndBoats.put(competitor, boat); RaceColumn raceColumn = leaderboard.getRaceColumnByName(raceColumnName); DynamicTrackedRegatta trackedRegatta = sourceService.getOrCreateTrackedRegatta(regatta); - TrackedRace trackedRace = new DummyTrackedRace(raceId, competitors, regatta, trackedRegatta, + TrackedRace trackedRace = new DummyTrackedRace(raceId, competitorsAndBoats, regatta, trackedRegatta, sourceService.getWindStore()); regatta.addRace(trackedRace.getRace()); // make sure the competitor is serialized together with the regatta raceColumn.setTrackedRace(testFleet1, trackedRace); @@ -793,12 +805,12 @@ public class MasterDataImportTest { Assert.assertNotNull(raceColumnOnTarget); Competitor competitorOnTarget = domainFactory.getExistingCompetitorById(competitorUUID); - Set competitorsCreatedOnTarget = new HashSet(); - competitorsCreatedOnTarget.add(competitorOnTarget); - + Boat boatOnTarget = domainFactory.getExistingBoatById(competitorUUID); + Map competitorsAndBoatsOnTarget = new HashMap<>(); + competitorsAndBoatsOnTarget.put(competitorOnTarget, boatOnTarget); Fleet fleet1OnTarget = raceColumnOnTarget.getFleetByName(testFleet1.getName()); DynamicTrackedRegatta trackedRegattaForTarget = destService.getOrCreateTrackedRegatta(regattaOnTarget); - TrackedRace trackedRaceForTarget = new DummyTrackedRace(raceId, competitorsCreatedOnTarget, regattaOnTarget, + TrackedRace trackedRaceForTarget = new DummyTrackedRace(raceId, competitorsAndBoatsOnTarget, regattaOnTarget, trackedRegattaForTarget, sourceService.getWindStore()); raceColumnOnTarget.setTrackedRace(fleet1OnTarget, trackedRaceForTarget); @@ -864,7 +876,8 @@ public class MasterDataImportTest { UUID regattaUUID = UUID.randomUUID(); Regatta regatta = sourceService.createRegatta( RegattaImpl.getDefaultName(TEST_REGATTA_NAME, TEST_BOAT_CLASS_NAME), TEST_BOAT_CLASS_NAME, - /* startDate */null, /* endDate */null, regattaUUID, series, true, new LowPoint(), courseAreaUUID, /*buoyZoneRadiusInHullLengths*/2.0, + /* canBoatsOfCompetitorsChangePerRace */ true, /* startDate */null, /* endDate */null, + regattaUUID, series, true, new LowPoint(), courseAreaUUID, /*buoyZoneRadiusInHullLengths*/2.0, /* useStartTimeInference */ true, /* controlTrackingFromStartAndFinishTimes */ false, OneDesignRankingMetric::new); for (String name : raceColumnNames) { @@ -889,9 +902,8 @@ public class MasterDataImportTest { "Der Lennart halt"); DynamicTeam team = new TeamImpl("Pros", sailors, coach); BoatClass boatClass = new BoatClassImpl("H16", true); - DynamicBoat boat = new BoatImpl("Wingy", boatClass, "GER70133"); - Competitor competitor = sourceDomainFactory.getOrCreateCompetitor(competitorUUID, "Froderik", Color.RED, - "noone@nowhere.de", null, team, boat, /* timeOnTimeFactor */null, /* timeOnDistanceAllowanceInSecondsPerNauticalMile */ + Competitor competitor = sourceDomainFactory.getOrCreateCompetitor(competitorUUID, "Froderik", "F", Color.RED, + "noone@nowhere.de", null, team, /* timeOnTimeFactor */null, /* timeOnDistanceAllowanceInSecondsPerNauticalMile */ null, null); competitors.add(competitor); UUID competitor2UUID = UUID.randomUUID(); @@ -899,13 +911,12 @@ public class MasterDataImportTest { sailors2.add(new PersonImpl("Test Mustermann", new NationalityImpl("GER"), new Date(645487200000L), "desc")); DynamicPerson coach2 = new PersonImpl("Max Test", new NationalityImpl("GER"), new Date(645487200000L), "desc"); DynamicTeam team2 = new TeamImpl("Pros2", sailors2, coach2); - DynamicBoat boat2 = new BoatImpl("FastBoat", boatClass, "GER70133"); Competitor competitor2 = sourceDomainFactory.getCompetitorStore().getOrCreateCompetitor(competitor2UUID, - "Froderik", Color.RED, "noone@nowhere.de", null, team2, boat2, /* timeOnTimeFactor */null, /* timeOnDistanceAllowanceInSecondsPerNauticalMile */ + "Froderik", "F", Color.RED, "noone@nowhere.de", null, team2, /* timeOnTimeFactor */null, /* timeOnDistanceAllowanceInSecondsPerNauticalMile */ null, null); competitors.add(competitor2); RaceColumn raceColumn = leaderboard.getRaceColumnByName(raceColumnName); - TrackedRace trackedRace = new DummyTrackedRace(raceId, competitors, regatta, null, sourceService.getWindStore()); + TrackedRace trackedRace = new DummyTrackedRace(raceId, createCompetitorsAndBoatsMap(boatClass, competitors), regatta, null, sourceService.getWindStore()); raceColumn.setTrackedRace(testFleet1, trackedRace); @@ -981,7 +992,7 @@ public class MasterDataImportTest { competitorsCreatedOnTarget.add(competitorOnTarget); Fleet fleet1OnTarget = raceColumnOnTarget.getFleetByName(testFleet1.getName()); - TrackedRace trackedRaceForTarget = new DummyTrackedRace(raceId, competitorsCreatedOnTarget, regattaOnTarget, + TrackedRace trackedRaceForTarget = new DummyTrackedRace(raceId, createCompetitorsAndBoatsMap(boatClass, competitorsCreatedOnTarget), regattaOnTarget, null, sourceService.getWindStore()); raceColumnOnTarget.setTrackedRace(fleet1OnTarget, trackedRaceForTarget); @@ -1024,7 +1035,8 @@ public class MasterDataImportTest { UUID regattaUUID = UUID.randomUUID(); Regatta regatta = sourceService.createRegatta( RegattaImpl.getDefaultName(TEST_REGATTA_NAME, TEST_BOAT_CLASS_NAME), TEST_BOAT_CLASS_NAME, - /* startDate */null, /* endDate */null, regattaUUID, series, true, new LowPoint(), courseAreaUUID, /*buoyZoneRadiusInHullLengths*/2.0, + /* canBoatsOfCompetitorsChangePerRace */ true, /* startDate */null, /* endDate */null, + regattaUUID, series, true, new LowPoint(), courseAreaUUID, /*buoyZoneRadiusInHullLengths*/2.0, /* useStartTimeInference */ true, /* controlTrackingFromStartAndFinishTimes */ false, OneDesignRankingMetric::new); for (String name : raceColumnNames) { series.get(0).addRaceColumn(name, sourceService); @@ -1048,8 +1060,7 @@ public class MasterDataImportTest { "Der Lennart halt"); DynamicTeam team = new TeamImpl("Pros", sailors, coach); BoatClass boatClass = new BoatClassImpl("H16", true); - DynamicBoat boat = new BoatImpl("Wingy", boatClass, "GER70133"); - CompetitorImpl competitor = new CompetitorImpl(competitorUUID, "Froderik", Color.RED, null, null, team, boat, /* timeOnTimeFactor */ + CompetitorImpl competitor = new CompetitorImpl(competitorUUID, "Froderik", "KYC", Color.RED, null, null, team, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */null, null); competitors.add(competitor); UUID competitorToSuppressUUID = UUID.randomUUID(); @@ -1059,12 +1070,11 @@ public class MasterDataImportTest { DynamicPerson coach2 = new PersonImpl("Peer Steinbrueck", new NationalityImpl("GER"), new Date(645487200000L), "Bester Coach"); DynamicTeam team2 = new TeamImpl("Noobs", sailors2, coach2); - DynamicBoat boat2 = new BoatImpl("LahmeEnte", boatClass, "GER1337"); - CompetitorImpl competitorToSuppress = new CompetitorImpl(competitorToSuppressUUID, "Merkel", Color.RED, null, - null, team2, boat2, /* timeOnTimeFactor */null, /* timeOnDistanceAllowancePerNauticalMile */null, null); + CompetitorImpl competitorToSuppress = new CompetitorImpl(competitorToSuppressUUID, "Merkel", "KYC", Color.RED, null, + null, team2, /* timeOnTimeFactor */null, /* timeOnDistanceAllowancePerNauticalMile */null, null); competitors.add(competitorToSuppress); RaceColumn raceColumn = leaderboard.getRaceColumnByName(raceColumnName); - TrackedRace trackedRace = new DummyTrackedRace(raceId, competitors, regatta, null, sourceService.getWindStore()); + TrackedRace trackedRace = new DummyTrackedRace(raceId, createCompetitorsAndBoatsMap(boatClass, competitors), regatta, null, sourceService.getWindStore()); raceColumn.setTrackedRace(testFleet1, trackedRace); @@ -1136,8 +1146,9 @@ public class MasterDataImportTest { seriesNotToOverride.add(new SeriesImpl("testSeries", false, /* isFleetsCanRunInParallel */ true, fleetsNotToOverride, emptyRaceColumnNamesList, destService)); Regatta regattaNotToOverride = destService.createRegatta( - RegattaImpl.getDefaultName(TEST_REGATTA_NAME, TEST_BOAT_CLASS_NAME), TEST_BOAT_CLASS_NAME, /* startDate */ - null, /* endDate */null, regattaUUID, seriesNotToOverride, true, new LowPoint(), courseAreaUUID, /*buoyZoneRadiusInHullLengths*/2.0, + RegattaImpl.getDefaultName(TEST_REGATTA_NAME, TEST_BOAT_CLASS_NAME), TEST_BOAT_CLASS_NAME, + /* canBoatsOfCompetitorsChangePerRace */ true, /* startDate */ null, /* endDate */null, + regattaUUID, seriesNotToOverride, true, new LowPoint(), courseAreaUUID, /*buoyZoneRadiusInHullLengths*/2.0, /* useStartTimeInference */ true, /* controlTrackingFromStartAndFinishTimes */ false, OneDesignRankingMetric::new); for (String name : raceColumnNamesNotToOverride) { @@ -1225,7 +1236,8 @@ public class MasterDataImportTest { UUID regattaUUID = UUID.randomUUID(); Regatta regatta = sourceService.createRegatta( RegattaImpl.getDefaultName(TEST_REGATTA_NAME, TEST_BOAT_CLASS_NAME), TEST_BOAT_CLASS_NAME, - /* startDate */null, /* endDate */null, regattaUUID, series, true, new LowPoint(), courseAreaUUID, /*buoyZoneRadiusInHullLengths*/2.0, + /* canBoatsOfCompetitorsChangePerRace */ true, /* startDate */null, /* endDate */null, + regattaUUID, series, true, new LowPoint(), courseAreaUUID, /*buoyZoneRadiusInHullLengths*/2.0, /* useStartTimeInference */ true, /* controlTrackingFromStartAndFinishTimes */ false, OneDesignRankingMetric::new); for (String name : raceColumnNames) { @@ -1250,8 +1262,7 @@ public class MasterDataImportTest { "Der Lennart halt"); DynamicTeam team = new TeamImpl("Pros", sailors, coach); BoatClass boatClass = new BoatClassImpl("H16", true); - DynamicBoat boat = new BoatImpl("Wingy", boatClass, "GER70133"); - CompetitorImpl competitor = new CompetitorImpl(competitorUUID, "Froderik", Color.RED, null, null, team, boat, /* timeOnTimeFactor */ + CompetitorImpl competitor = new CompetitorImpl(competitorUUID, "Froderik", "KYC", Color.RED, null, null, team, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */null, null); competitors.add(competitor); UUID competitorToSuppressUUID = UUID.randomUUID(); @@ -1261,12 +1272,11 @@ public class MasterDataImportTest { DynamicPerson coach2 = new PersonImpl("Peer Steinbrueck", new NationalityImpl("GER"), new Date(645487200000L), "Bester Coach"); DynamicTeam team2 = new TeamImpl("Noobs", sailors2, coach2); - DynamicBoat boat2 = new BoatImpl("LahmeEnte", boatClass, "GER1337"); - CompetitorImpl competitorToSuppress = new CompetitorImpl(competitorToSuppressUUID, "Merkel", Color.RED, null, - null, team2, boat2, /* timeOnTimeFactor */null, /* timeOnDistanceAllowancePerNauticalMile */null, null); + CompetitorImpl competitorToSuppress = new CompetitorImpl(competitorToSuppressUUID, "Merkel", "KYC", Color.RED, null, + null, team2, /* timeOnTimeFactor */null, /* timeOnDistanceAllowancePerNauticalMile */null, null); competitors.add(competitorToSuppress); RaceColumn raceColumn = leaderboard.getRaceColumnByName(raceColumnName); - TrackedRace trackedRace = new DummyTrackedRace(raceId, competitors, regatta, null, sourceService.getWindStore()); + TrackedRace trackedRace = new DummyTrackedRace(raceId, createCompetitorsAndBoatsMap(boatClass, competitors), regatta, null, sourceService.getWindStore()); raceColumn.setTrackedRace(testFleet1, trackedRace); @@ -1334,8 +1344,9 @@ public class MasterDataImportTest { seriesToOverride.add(new SeriesImpl("testSeries", false, /* isFleetsCanRunInParallel */ true, fleetsToOverride, emptyRaceColumnNamesList, destService)); Regatta regattaToOverride = destService.createRegatta( - RegattaImpl.getDefaultName(TEST_REGATTA_NAME, TEST_BOAT_CLASS_NAME), TEST_BOAT_CLASS_NAME, /* startDate */ - null, /* endDate */null, regattaUUID, seriesToOverride, true, new LowPoint(), courseAreaUUID, /*buoyZoneRadiusInHullLengths*/2.0, + RegattaImpl.getDefaultName(TEST_REGATTA_NAME, TEST_BOAT_CLASS_NAME), TEST_BOAT_CLASS_NAME, + /* canBoatsOfCompetitorsChangePerRace */ true, /* startDate */ null, /* endDate */null, + regattaUUID, seriesToOverride, true, new LowPoint(), courseAreaUUID, /*buoyZoneRadiusInHullLengths*/2.0, /* useStartTimeInference */ true, /* controlTrackingFromStartAndFinishTimes */ false, OneDesignRankingMetric::new); for (String name : raceColumnNamesToOverride) { @@ -1351,17 +1362,16 @@ public class MasterDataImportTest { 645487200000L), "Der Lennart halt"); DynamicTeam teamToOverride = new TeamImpl("Pros", sailorsToOverride, coachToOverride); BoatClass boatClassToOverride = new BoatClassImpl("H16", true); - DynamicBoat boatToOverride = new BoatImpl("Wingy", boatClassToOverride, "GER70133"); String competitorOldName = "oldName"; - Competitor competitorToOverride = domainFactory.getOrCreateCompetitor(competitorUUID, competitorOldName, - Color.BLUE, "noone@nowhere.de", null, teamToOverride, boatToOverride, /* timeOnTimeFactor */null, /* timeOnDistanceAllowanceInSecondsPerNauticalMile */ + Competitor competitorToOverride = domainFactory.getOrCreateCompetitor(competitorUUID, competitorOldName, "c", + Color.BLUE, "noone@nowhere.de", null, teamToOverride, /* timeOnTimeFactor */null, /* timeOnDistanceAllowanceInSecondsPerNauticalMile */ null, null); competitorsToOverride.add(competitorToOverride); Leaderboard leaderboardToOverride = destService.addRegattaLeaderboard( regattaToOverride.getRegattaIdentifier(), "testDisplayNameNotToOverride", discardRule); - TrackedRace trackedRace2 = new DummyTrackedRace(raceId, competitorsToOverride, regattaToOverride, null, - sourceService.getWindStore()); + TrackedRace trackedRace2 = new DummyTrackedRace(raceId, createCompetitorsAndBoatsMap(boatClassToOverride, competitorsToOverride), + regattaToOverride, null, sourceService.getWindStore()); RaceColumn columnToOverride = leaderboardToOverride.getRaceColumns().iterator().next(); columnToOverride.setTrackedRace(testFleet1ToOverride, trackedRace2); identifierOfRegattaTrackedRace = regattaToOverride.getRaceIdentifier(columnToOverride @@ -1444,7 +1454,8 @@ public class MasterDataImportTest { new UUID[] { UUID.randomUUID() })[0]; Regatta regatta = sourceService.createRegatta( - RegattaImpl.getDefaultName(TEST_REGATTA_NAME, TEST_BOAT_CLASS_NAME), TEST_BOAT_CLASS_NAME, /* startDate */ + RegattaImpl.getDefaultName(TEST_REGATTA_NAME, TEST_BOAT_CLASS_NAME), TEST_BOAT_CLASS_NAME, + /* canBoatsOfCompetitorsChangePerRace */ true, /* startDate */ null, /* endDate */null, UUID.randomUUID(), new ArrayList(), true, new LowPoint(), defaultCourseArea.getId(), /*buoyZoneRadiusInHullLengths*/2.0, /* useStartTimeInference */true, /* controlTrackingFromStartAndFinishTimes */ false, OneDesignRankingMetric::new); // Let's use the setters directly because we are not testing replication @@ -1527,8 +1538,10 @@ public class MasterDataImportTest { Regatta regatta = sourceService.createRegatta( RegattaImpl.getDefaultName(TEST_REGATTA_NAME, TEST_BOAT_CLASS_NAME), TEST_BOAT_CLASS_NAME, - /* startDate */null, /* endDate */null, regattaUUID, series, true, new LowPoint(), - defaultCourseArea.getId(), /*buoyZoneRadiusInHullLengths*/2.0, /* useStartTimeInference */true, /* controlTrackingFromStartAndFinishTimes */ false, OneDesignRankingMetric::new); + /* canBoatsOfCompetitorsChangePerRace */ true, /* startDate */null, /* endDate */null, + regattaUUID, series, true, new LowPoint(), + defaultCourseArea.getId(), /*buoyZoneRadiusInHullLengths*/2.0, /* useStartTimeInference */true, + /* controlTrackingFromStartAndFinishTimes */ false, OneDesignRankingMetric::new); for (String name : raceColumnNames) { series.get(0).addRaceColumn(name, sourceService); } @@ -1550,8 +1563,7 @@ public class MasterDataImportTest { "Der Lennart halt"); DynamicTeam team = new TeamImpl("Pros", sailors, coach); BoatClass boatClass = new BoatClassImpl("H16", true); - DynamicBoat boat = new BoatImpl("Wingy", boatClass, "GER70133"); - CompetitorImpl competitor = new CompetitorImpl(competitorUUID, "Froderik", Color.RED, null, null, team, boat, /* timeOnTimeFactor */ + CompetitorImpl competitor = new CompetitorImpl(competitorUUID, "Froderik", "KYC", Color.RED, null, null, team, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */null, null); competitors.add(competitor); UUID competitorToSuppressUUID = UUID.randomUUID(); @@ -1561,12 +1573,11 @@ public class MasterDataImportTest { DynamicPerson coach2 = new PersonImpl("Peer Steinbrueck", new NationalityImpl("GER"), new Date(645487200000L), "Bester Coach"); DynamicTeam team2 = new TeamImpl("Noobs", sailors2, coach2); - DynamicBoat boat2 = new BoatImpl("LahmeEnte", boatClass, "GER1337"); - CompetitorImpl competitorToSuppress = new CompetitorImpl(competitorToSuppressUUID, "Merkel", Color.RED, null, - null, team2, boat2, /* timeOnTimeFactor */null, /* timeOnDistanceAllowancePerNauticalMile */null, null); + CompetitorImpl competitorToSuppress = new CompetitorImpl(competitorToSuppressUUID, "Merkel", "KYC", Color.RED, null, + null, team2, /* timeOnTimeFactor */null, /* timeOnDistanceAllowancePerNauticalMile */null, null); competitors.add(competitorToSuppress); RaceColumn raceColumn = leaderboard.getRaceColumnByName(raceColumnName); - TrackedRace trackedRace = new DummyTrackedRace(raceId, competitors, regatta, null, sourceService.getWindStore()); + TrackedRace trackedRace = new DummyTrackedRace(raceId, createCompetitorsAndBoatsMap(boatClass, competitors), regatta, null, sourceService.getWindStore()); raceColumn.setTrackedRace(testFleet1, trackedRace); @@ -1661,7 +1672,8 @@ public class MasterDataImportTest { UUID regattaUUID = UUID.randomUUID(); Regatta regatta = sourceService.createRegatta( RegattaImpl.getDefaultName(TEST_REGATTA_NAME, TEST_BOAT_CLASS_NAME), TEST_BOAT_CLASS_NAME, - /* startDate */null, /* endDate */null, regattaUUID, series, true, new LowPoint(), courseAreaUUID, /*buoyZoneRadiusInHullLengths*/2.0, + /* canBoatsOfCompetitorsChangePerRace */ true, /* startDate */null, /* endDate */null, + regattaUUID, series, true, new LowPoint(), courseAreaUUID, /*buoyZoneRadiusInHullLengths*/2.0, /* useStartTimeInference */true, /* controlTrackingFromStartAndFinishTimes */ false, OneDesignRankingMetric::new); for (String name : raceColumnNames) { @@ -1686,8 +1698,7 @@ public class MasterDataImportTest { "Der Lennart halt"); DynamicTeam team = new TeamImpl("Pros", sailors, coach); BoatClass boatClass = new BoatClassImpl("H16", true); - DynamicBoat boat = new BoatImpl("Wingy", boatClass, "GER70133"); - CompetitorImpl competitor = new CompetitorImpl(competitorUUID, "Froderik", Color.RED, null, null, team, boat, /* timeOnTimeFactor */ + CompetitorImpl competitor = new CompetitorImpl(competitorUUID, "Froderik", "KYC", Color.RED, null, null, team, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */null, null); competitors.add(competitor); UUID competitorToSuppressUUID = UUID.randomUUID(); @@ -1697,12 +1708,11 @@ public class MasterDataImportTest { DynamicPerson coach2 = new PersonImpl("Peer Steinbrueck", new NationalityImpl("GER"), new Date(645487200000L), "Bester Coach"); DynamicTeam team2 = new TeamImpl("Noobs", sailors2, coach2); - DynamicBoat boat2 = new BoatImpl("LahmeEnte", boatClass, "GER1337"); - CompetitorImpl competitorToSuppress = new CompetitorImpl(competitorToSuppressUUID, "Merkel", Color.RED, null, - null, team2, boat2, /* timeOnTimeFactor */null, /* timeOnDistanceAllowancePerNauticalMile */null, null); + CompetitorImpl competitorToSuppress = new CompetitorImpl(competitorToSuppressUUID, "Merkel", "KYC", Color.RED, null, + null, team2, /* timeOnTimeFactor */null, /* timeOnDistanceAllowancePerNauticalMile */null, null); competitors.add(competitorToSuppress); RaceColumn raceColumn = leaderboard.getRaceColumnByName(raceColumnName); - TrackedRace trackedRace = new DummyTrackedRace(raceId, competitors, regatta, null, sourceService.getWindStore()); + TrackedRace trackedRace = new DummyTrackedRace(raceId, createCompetitorsAndBoatsMap(boatClass, competitors), regatta, null, sourceService.getWindStore()); raceColumn.setTrackedRace(testFleet1, trackedRace); sourceService.setRegattaForRace(regatta, "dummy"); @@ -1905,7 +1915,7 @@ public class MasterDataImportTest { UUID regattaUUID = UUID.randomUUID(); Regatta regatta = sourceService.createRegatta( RegattaImpl.getDefaultName(TEST_REGATTA_NAME, TEST_BOAT_CLASS_NAME), TEST_BOAT_CLASS_NAME, - /* startDate */null, /* endDate */null, regattaUUID, series, true, new LowPoint(), courseAreaUUID, /*buoyZoneRadiusInHullLengths*/2.0, + /* canBoatsOfCompetitorsChangePerRace */ true, /* startDate */null, /* endDate */null, regattaUUID, series, true, new LowPoint(), courseAreaUUID, /*buoyZoneRadiusInHullLengths*/2.0, /* useStartTimeInference */true, /* controlTrackingFromStartAndFinishTimes */ false, OneDesignRankingMetric::new); for (String name : raceColumnNames) { @@ -1934,8 +1944,7 @@ public class MasterDataImportTest { "Der Lennart halt"); DynamicTeam team = new TeamImpl("Pros", sailors, coach); BoatClass boatClass = new BoatClassImpl("H16", true); - DynamicBoat boat = new BoatImpl("Wingy", boatClass, "GER70133"); - CompetitorImpl competitor = new CompetitorImpl(competitorUUID, "Froderik", Color.RED, null, null, team, boat, /* timeOnTimeFactor */ + CompetitorImpl competitor = new CompetitorImpl(competitorUUID, "Froderik", "KYC", Color.RED, null, null, team, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */null, null); competitors.add(competitor); UUID competitorToSuppressUUID = UUID.randomUUID(); @@ -1945,13 +1954,12 @@ public class MasterDataImportTest { DynamicPerson coach2 = new PersonImpl("Peer Steinbrueck", new NationalityImpl("GER"), new Date(645487200000L), "Bester Coach"); DynamicTeam team2 = new TeamImpl("Noobs", sailors2, coach2); - DynamicBoat boat2 = new BoatImpl("LahmeEnte", boatClass, "GER1337"); - CompetitorImpl competitorToSuppress = new CompetitorImpl(competitorToSuppressUUID, "Merkel", Color.RED, null, - null, team2, boat2, /* timeOnTimeFactor */null, /* timeOnDistanceAllowancePerNauticalMile */null, null); + CompetitorImpl competitorToSuppress = new CompetitorImpl(competitorToSuppressUUID, "Merkel", "KYC", Color.RED, null, + null, team2, /* timeOnTimeFactor */null, /* timeOnDistanceAllowancePerNauticalMile */null, null); competitors.add(competitorToSuppress); RaceColumn raceColumn = leaderboard.getRaceColumnByName(raceColumnName); - TrackedRace trackedRace = new DummyTrackedRace(raceId, competitors, regatta, null, sourceService.getWindStore()); + TrackedRace trackedRace = new DummyTrackedRace(raceId, createCompetitorsAndBoatsMap(boatClass, competitors), regatta, null, sourceService.getWindStore()); raceColumn.setTrackedRace(testFleet1, trackedRace); @@ -2135,8 +2143,9 @@ public class MasterDataImportTest { seriesOnTarget.add(new SeriesImpl("testSeries", false, /* isFleetsCanRunInParallel */ true, fleets, emptyRaceColumnNamesList, sourceService)); UUID regattaUUID = UUID.randomUUID(); Regatta regatta = sourceService.createRegatta( - RegattaImpl.getDefaultName(TEST_REGATTA_NAME, TEST_BOAT_CLASS_NAME), TEST_BOAT_CLASS_NAME, /* startDate */ - null, /* endDate */null, regattaUUID, seriesOnSource, true, new LowPoint(), courseAreaUUID, /*buoyZoneRadiusInHullLengths*/2.0, + RegattaImpl.getDefaultName(TEST_REGATTA_NAME, TEST_BOAT_CLASS_NAME), TEST_BOAT_CLASS_NAME, + /* canBoatsOfCompetitorsChangePerRace */ true, /* startDate */ null, /* endDate */null, + regattaUUID, seriesOnSource, true, new LowPoint(), courseAreaUUID, /*buoyZoneRadiusInHullLengths*/2.0, /* useStartTimeInference */true, /* controlTrackingFromStartAndFinishTimes */ false, OneDesignRankingMetric::new); for (String name : raceColumnNames) { seriesOnSource.get(0).addRaceColumn(name, sourceService); @@ -2164,8 +2173,7 @@ public class MasterDataImportTest { "Der Lennart halt"); DynamicTeam team = new TeamImpl("Pros", sailors, coach); BoatClass boatClass = new BoatClassImpl("H16", true); - DynamicBoat boat = new BoatImpl("Wingy", boatClass, "GER70133"); - CompetitorImpl competitor = new CompetitorImpl(competitorUUID, "Froderik", Color.RED, null, null, team, boat, /* timeOnTimeFactor */ + CompetitorImpl competitor = new CompetitorImpl(competitorUUID, "Froderik", "KYC", Color.RED, null, null, team, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */null, null); competitors.add(competitor); UUID competitorToSuppressUUID = UUID.randomUUID(); @@ -2175,11 +2183,10 @@ public class MasterDataImportTest { DynamicPerson coach2 = new PersonImpl("Peer Steinbrueck", new NationalityImpl("GER"), new Date(645487200000L), "Bester Coach"); DynamicTeam team2 = new TeamImpl("Noobs", sailors2, coach2); - DynamicBoat boat2 = new BoatImpl("LahmeEnte", boatClass, "GER1337"); - CompetitorImpl competitorToSuppress = new CompetitorImpl(competitorToSuppressUUID, "Merkel", Color.RED, null, - null, team2, boat2, /* timeOnTimeFactor */null, /* timeOnDistanceAllowancePerNauticalMile */null, null); + CompetitorImpl competitorToSuppress = new CompetitorImpl(competitorToSuppressUUID, "Merkel", "KYC", Color.RED, null, + null, team2, /* timeOnTimeFactor */null, /* timeOnDistanceAllowancePerNauticalMile */null, null); competitors.add(competitorToSuppress); - TrackedRace trackedRace = new DummyTrackedRace(raceId, competitors, regatta, null, sourceService.getWindStore()); + TrackedRace trackedRace = new DummyTrackedRace(raceId, createCompetitorsAndBoatsMap(boatClass, competitors), regatta, null, sourceService.getWindStore()); leaderboard.addRace(trackedRace, raceColumnName, false); RaceColumn raceColumnOnLeaderboard = leaderboard.getRaceColumnByName(raceColumnName); @@ -2264,10 +2271,11 @@ public class MasterDataImportTest { Regatta regattaOnTarget = destService.createRegatta( RegattaImpl.getDefaultName(TEST_REGATTA_NAME, TEST_BOAT_CLASS_NAME), TEST_BOAT_CLASS_NAME, - /* startDate */null, /* endDate */null, regattaUUID, seriesOnTarget, true, new LowPoint(), + /* canBoatsOfCompetitorsChangePerRace */ true, /* startDate */null, /* endDate */null, + regattaUUID, seriesOnTarget, true, new LowPoint(), courseAreaUUID, /*buoyZoneRadiusInHullLengths*/2.0, /* useStartTimeInference */true, /* controlTrackingFromStartAndFinishTimes */ false, OneDesignRankingMetric::new); - raceColumnOnTarget.setTrackedRace(defaultFleetOnTarget, new DummyTrackedRace(raceId, competitors, + raceColumnOnTarget.setTrackedRace(defaultFleetOnTarget, new DummyTrackedRace(raceId, createCompetitorsAndBoatsMap(boatClass, competitors), regattaOnTarget, null, sourceService.getWindStore())); Assert.assertTrue(leaderboardOnTarget.getScoreCorrection().hasCorrectionFor(raceColumnOnTarget)); @@ -2276,7 +2284,7 @@ public class MasterDataImportTest { competitorsCreatedOnTarget.add(competitorOnTarget); Assert.assertEquals(defaultFleetOnTarget, leaderboardOnTarget.getFleet(null)); - TrackedRace trackedRaceForTarget = new DummyTrackedRace(raceId, competitorsCreatedOnTarget, regattaOnTarget, + TrackedRace trackedRaceForTarget = new DummyTrackedRace(raceId, createCompetitorsAndBoatsMap(boatClass, competitorsCreatedOnTarget), regattaOnTarget, null, sourceService.getWindStore()); raceColumnOnTarget.setTrackedRace(defaultFleetOnTarget, trackedRaceForTarget); diff --git a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RaceColumnReloadTest.java b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RaceColumnReloadTest.java index 847769443af..0977b4fe398 100644 --- a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RaceColumnReloadTest.java +++ b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RaceColumnReloadTest.java @@ -2,6 +2,8 @@ package com.sap.sailing.server.test; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; import java.util.Set; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; @@ -15,8 +17,10 @@ import com.sap.sailing.domain.abstractlog.race.RaceLog; import com.sap.sailing.domain.abstractlog.race.RaceLogWindFixEvent; import com.sap.sailing.domain.abstractlog.race.impl.BaseRaceLogEventVisitor; import com.sap.sailing.domain.abstractlog.race.impl.RaceLogWindFixEventImpl; +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; @@ -25,6 +29,7 @@ import com.sap.sailing.domain.base.Regatta; import com.sap.sailing.domain.base.Waypoint; import com.sap.sailing.domain.base.impl.BoatImpl; import com.sap.sailing.domain.base.impl.CourseImpl; +import com.sap.sailing.domain.base.impl.DynamicBoat; import com.sap.sailing.domain.base.impl.PersonImpl; import com.sap.sailing.domain.base.impl.RaceDefinitionImpl; import com.sap.sailing.domain.base.impl.RegattaImpl; @@ -85,16 +90,19 @@ public class RaceColumnReloadTest { BoatClass boatClass = masterDomainFactory.getOrCreateBoatClass(boatClassName, /* typicallyStartsUpwind */true); PersonImpl sailor = new PersonImpl("Sailor", DomainFactory.INSTANCE.getOrCreateNationality("GER"), null, null); PersonImpl coach = new PersonImpl("Coach", DomainFactory.INSTANCE.getOrCreateNationality("NED"), null, null); - Competitor comp = masterDomainFactory.getOrCreateCompetitor("GER 61", "Team", Color.RED, "noone@nowhere.de", - null, new TeamImpl("Team", Arrays.asList(sailor), coach), new BoatImpl("GER 61", boatClass, "GER 61"), - /* timeOnTimeFactor */ null, /* timeOnDistanceAllowanceInSecondsPerNauticalMile */ null, null); + Boat boat = new BoatImpl("61", "GER 61", boatClass, "GER 61"); + CompetitorWithBoat comp = masterDomainFactory.getOrCreateCompetitorWithBoat("GER 61", "Team", "T", Color.RED, "noone@nowhere.de", + null, new TeamImpl("Team", Arrays.asList(sailor), coach), + /* timeOnTimeFactor */ null, /* timeOnDistanceAllowanceInSecondsPerNauticalMile */ null, null, (DynamicBoat) boat); service.apply(new CreateFlexibleLeaderboard(leaderboardName, "Test", new int[] { 1, 2 }, new LowPoint(), null)); raceColumn = service.apply(new AddColumnToLeaderboard("R1", leaderboardName, false)); Regatta regatta = service.apply(new AddDefaultRegatta(RegattaImpl.getDefaultName("Test Event", boatClassName), boatClassName, /* startDate */ null, /* endDate */ null, UUID.randomUUID())); final CourseImpl masterCourse = new CourseImpl("Test Course", new ArrayList()); - final RaceDefinition race = new RaceDefinitionImpl(raceName, masterCourse, boatClass, Arrays.asList(comp)); + final Map compAndBoats = new HashMap<>(); + compAndBoats.put(comp, comp.getBoat()); + final RaceDefinition race = new RaceDefinitionImpl(raceName, masterCourse, boatClass, compAndBoats); service.apply(new AddRaceDefinition(new RegattaName(regatta.getName()), race)); masterCourse.addWaypoint(0, masterDomainFactory.createWaypoint(masterDomainFactory.getOrCreateMark("Mark1"), /* passingInstruction */ null)); diff --git a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RaceTrackerStartStopTest.java b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RaceTrackerStartStopTest.java index 8685d9ca7a5..8d244c15438 100644 --- a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RaceTrackerStartStopTest.java +++ b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RaceTrackerStartStopTest.java @@ -23,6 +23,7 @@ import org.junit.Before; import org.junit.Test; import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver; +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; @@ -71,15 +72,16 @@ public class RaceTrackerStartStopTest { racingEventService = new RacingEventServiceImplMock(){}; boatClass = new BoatClassImpl(BOATCLASSNAME, /* typicallyStartsUpwind */ true); regatta = new RegattaImpl(EmptyRaceLogStore.INSTANCE, EmptyRegattaLogStore.INSTANCE, - RegattaImpl.getDefaultName(EVENTNAME, boatClass.getName()), boatClass, /*startDate*/ null, /*endDate*/ null, /* trackedRegattaRegistry */ + RegattaImpl.getDefaultName(EVENTNAME, boatClass.getName()), boatClass, + /* canBoatsOfCompetitorsChangePerRace */ true, /*startDate*/ null, /*endDate*/ null, /* trackedRegattaRegistry */ null, DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), UUID.randomUUID(), null); racingEventService.getRegattasByName().put(EVENTNAME, regatta); TrackedRegatta trackedRegatta1 = racingEventService.getOrCreateTrackedRegatta(regatta); racingEventService.getRegattasByName().put(EVENTNAME, regatta); raceTrackerSet = Collections.newSetFromMap(new ConcurrentHashMap()); - raceDef1 = new RaceDefinitionImpl(RACENAME1, new CourseImpl("Course1", new ArrayList()), boatClass, new ArrayList()); - raceDef2 = new RaceDefinitionImpl(RACENAME2, new CourseImpl("Course2", new ArrayList()), boatClass, new ArrayList()); - raceDef3 = new RaceDefinitionImpl(RACENAME3, new CourseImpl("Course3", new ArrayList()), boatClass, new ArrayList()); + raceDef1 = new RaceDefinitionImpl(RACENAME1, new CourseImpl("Course1", new ArrayList()), boatClass, Collections.emptyMap()); + raceDef2 = new RaceDefinitionImpl(RACENAME2, new CourseImpl("Course2", new ArrayList()), boatClass, Collections.emptyMap()); + raceDef3 = new RaceDefinitionImpl(RACENAME3, new CourseImpl("Course3", new ArrayList()), boatClass, Collections.emptyMap()); regatta.addRace(raceDef1); trackedRegatta1.createTrackedRace(raceDef1, Collections. emptyList(), /* windStore */ EmptyWindStore.INSTANCE, /* delayToLiveInMillis */ 0l, diff --git a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RemoveLeaderboardTest.java b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RemoveLeaderboardTest.java index 39a522db57f..328f99c8218 100755 --- a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RemoveLeaderboardTest.java +++ b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RemoveLeaderboardTest.java @@ -19,7 +19,6 @@ import org.junit.Test; import com.sap.sailing.domain.abstractlog.race.RaceLog; import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver; import com.sap.sailing.domain.base.BoatClass; -import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.DomainFactory; import com.sap.sailing.domain.base.Event; import com.sap.sailing.domain.base.Fleet; @@ -90,14 +89,15 @@ public class RemoveLeaderboardTest { boatClass = new BoatClassImpl(BOATCLASSNAME, /* typicallyStartsUpwind */ true); regatta = new RegattaImpl(EmptyRaceLogStore.INSTANCE, EmptyRegattaLogStore.INSTANCE, - RegattaImpl.getDefaultName(EVENTNAME, boatClass.getName()), boatClass, /*startDate*/ null, /*endDate*/ null, /* trackedRegattaRegistry */ + RegattaImpl.getDefaultName(EVENTNAME, boatClass.getName()), boatClass, /* canBoatsOfCompetitorsChangePerRace*/ true, + /*startDate*/ null, /*endDate*/ null, /* trackedRegattaRegistry */ null, DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), UUID.randomUUID(), null); TrackedRegatta trackedRegatta1 = server.getOrCreateTrackedRegatta(regatta); defaultFleet = new FleetImpl("Default"); regatta.addSeries(new SeriesImpl(SERIES_NAME, /* is medal */ false, /* can fleets run in parallel */ true, Collections.singleton(defaultFleet), Collections.singleton(R1), /* regatta registry */ server)); - raceDef1 = new RaceDefinitionImpl(RACENAME1, new CourseImpl("Course1", new ArrayList()), boatClass, new ArrayList()); + raceDef1 = new RaceDefinitionImpl(RACENAME1, new CourseImpl("Course1", new ArrayList()), boatClass, new HashMap<>()); regatta.addRace(raceDef1); trackedRace = trackedRegatta1.createTrackedRace(raceDef1, Collections. emptyList(), /* windStore */ EmptyWindStore.INSTANCE, /* delayToLiveInMillis */ 0l, diff --git a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/SearchServiceTest.java b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/SearchServiceTest.java index fd8b3e6223a..e0dad0c2846 100755 --- a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/SearchServiceTest.java +++ b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/SearchServiceTest.java @@ -20,7 +20,7 @@ import java.util.UUID; import org.junit.Before; import org.junit.Test; -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.LeaderboardGroupBase; @@ -85,12 +85,12 @@ public class SearchServiceTest { private Venue flensburg; private Event pfingstbusch; private Regatta pfingstbusch29er; - private Competitor hassoPlattner; - private Competitor alexanderRies; - private Competitor antonKoch; - private Competitor tobiasSchadewaldt; - private Competitor philippBuhl; - private Competitor dennisGehrlein; + private CompetitorWithBoat hassoPlattner; + private CompetitorWithBoat alexanderRies; + private CompetitorWithBoat antonKoch; + private CompetitorWithBoat tobiasSchadewaldt; + private CompetitorWithBoat philippBuhl; + private CompetitorWithBoat dennisGehrlein; private Regatta pfingstbusch470; private Regatta aalRegatta; private DynamicTrackedRace pfingstbusch29erTrackedR1; @@ -139,7 +139,7 @@ public class SearchServiceTest { /* medal */false, /* fleetsCanRunInParallel */ true, /* startsWithZero */false, /* firstColumnIsNonDiscardableCarryForward */false, /* discardingThresholds */null, /* hasSplitFleetContiguousScoring */false, /* maximumNumberOfDiscards */ null)); pfingstbusch29er = server.apply(new AddSpecificRegatta(RegattaImpl.getDefaultName("Pfingstbusch", "29er"), "29er", - /*startDate*/ null, /*endDate*/ null, UUID.randomUUID(), + /* canBoatsOfCompetitorsChangePerRace */ true, /*startDate*/ null, /*endDate*/ null, UUID.randomUUID(), new RegattaCreationParametersDTO(seriesCreationParams), /* persistent */ true, new LowPoint(), kielAlpha.getId(), /*buoyZoneRadiusInHullLengths*/2.0, /* useStartTimeInference */ true, /* controlTrackingFromStartAndFinishTimes */ false, RankingMetrics.ONE_DESIGN)); server.apply(new AddColumnToSeries(pfingstbusch29er.getRegattaIdentifier(), "Default", "R1")); @@ -148,7 +148,8 @@ public class SearchServiceTest { RegattaLeaderboard pfingstbusch29erLeaderboard = server.apply(new CreateRegattaLeaderboard(pfingstbusch29er.getRegattaIdentifier(), /* leaderboardDisplayName */ null, /* discardThresholds */ new int[0])); pfingstbusch470 = server.apply(new AddSpecificRegatta(RegattaImpl.getDefaultName("Pfingstbusch", "470"), "470", - /*startDate*/ null, /*endDate*/ null, UUID.randomUUID(), new RegattaCreationParametersDTO(seriesCreationParams), /* persistent */ + /* canBoatsOfCompetitorsChangePerRace */ true, /*startDate*/ null, /*endDate*/ null, + UUID.randomUUID(), new RegattaCreationParametersDTO(seriesCreationParams), /* persistent */ true, new LowPoint(), kielBravo.getId(), /*buoyZoneRadiusInHullLengths*/2.0, /* useStartTimeInference */ true, /* controlTrackingFromStartAndFinishTimes */ false, RankingMetrics.ONE_DESIGN)); server.apply(new AddColumnToSeries(pfingstbusch470.getRegattaIdentifier(), "Default", "R1")); server.apply(new AddColumnToSeries(pfingstbusch470.getRegattaIdentifier(), "Default", "R2")); @@ -170,7 +171,7 @@ public class SearchServiceTest { final CourseAreaImpl flensburgStandard = new CourseAreaImpl("Standard", UUID.randomUUID()); flensburg.addCourseArea(flensburgStandard); aalRegatta = server.apply(new AddSpecificRegatta(RegattaImpl.getDefaultName("Aalregatta", "ORC"), "ORC", - /*startDate*/ null, /*endDate*/ null, UUID.randomUUID(), + /* canBoatsOfCompetitorsChangePerRace */ true, /*startDate*/ null, /*endDate*/ null, UUID.randomUUID(), new RegattaCreationParametersDTO(seriesCreationParams), /* persistent */ true, new LowPoint(), flensburgStandard.getId(), /*buoyZoneRadiusInHullLengths*/2.0, /* useStartTimeInference */ true, /* controlTrackingFromStartAndFinishTimes */ false, RankingMetrics.ONE_DESIGN)); server.apply(new AddColumnToSeries(aalRegatta.getRegattaIdentifier(), "Default", "R1")); @@ -181,18 +182,18 @@ public class SearchServiceTest { /* displayGroupsInReverseOrder */ false, Collections.singletonList(RegattaImpl.getDefaultName("Aalregatta", "ORC")), new int[0], /* overallLeaderboardScoringSchemeType */ null)); server.apply(new AddLeaderboardGroupToEvent(aalEvent.getId(), aalLeaderboardGroup.getId())); - hassoPlattner = AbstractTracTracLiveTest.createCompetitor("Hasso Plattner"); - alexanderRies = AbstractTracTracLiveTest.createCompetitor("Alexander Ries"); - antonKoch = AbstractTracTracLiveTest.createCompetitor("Anton Koch"); - tobiasSchadewaldt = AbstractTracTracLiveTest.createCompetitor("Tobias Schadewaldt"); - philippBuhl = AbstractTracTracLiveTest.createCompetitor("Philipp Buhl"); - dennisGehrlein = AbstractTracTracLiveTest.createCompetitor("Dennis Gehrlein"); + hassoPlattner = AbstractTracTracLiveTest.createCompetitorWithBoat("Hasso Plattner"); + alexanderRies = AbstractTracTracLiveTest.createCompetitorWithBoat("Alexander Ries"); + antonKoch = AbstractTracTracLiveTest.createCompetitorWithBoat("Anton Koch"); + tobiasSchadewaldt = AbstractTracTracLiveTest.createCompetitorWithBoat("Tobias Schadewaldt"); + philippBuhl = AbstractTracTracLiveTest.createCompetitorWithBoat("Philipp Buhl"); + dennisGehrlein = AbstractTracTracLiveTest.createCompetitorWithBoat("Dennis Gehrlein"); final RaceDefinitionImpl pfingstbusch29erR1 = new RaceDefinitionImpl("R1", new CourseImpl("up/down", Collections.emptyList()), pfingstbusch29er.getBoatClass(), - Arrays.asList(new Competitor[] { alexanderRies, tobiasSchadewaldt })); + AbstractTracTracLiveTest.createCompetitorAndBoatsMap(alexanderRies, tobiasSchadewaldt)); final RaceDefinitionImpl pfingstbusch29erR2 = new RaceDefinitionImpl("R2", new CourseImpl("up/down", Collections.emptyList()), pfingstbusch29er.getBoatClass(), - Arrays.asList(new Competitor[] { alexanderRies, tobiasSchadewaldt })); + AbstractTracTracLiveTest.createCompetitorAndBoatsMap(alexanderRies, tobiasSchadewaldt)); final RaceDefinitionImpl pfingstbusch29erR3 = new RaceDefinitionImpl("R3", new CourseImpl("up/down", Collections.emptyList()), pfingstbusch29er.getBoatClass(), - Arrays.asList(new Competitor[] { alexanderRies, tobiasSchadewaldt })); + AbstractTracTracLiveTest.createCompetitorAndBoatsMap(alexanderRies, tobiasSchadewaldt)); server.apply(new AddRaceDefinition(pfingstbusch29er.getRegattaIdentifier(), pfingstbusch29erR1)); server.apply(new AddRaceDefinition(pfingstbusch29er.getRegattaIdentifier(), pfingstbusch29erR2)); server.apply(new AddRaceDefinition(pfingstbusch29er.getRegattaIdentifier(), pfingstbusch29erR3)); @@ -206,9 +207,9 @@ public class SearchServiceTest { pfingstbusch29erLeaderboard.getRaceColumnByName("R3").setTrackedRace(pfingstbusch29erLeaderboard.getRaceColumnByName("R3").getFleetByName("Default"), pfingstbusch29erTrackedR3); final RaceDefinitionImpl pfingstbush470R1 = new RaceDefinitionImpl("R1", new CourseImpl("up/down", Collections.emptyList()), pfingstbusch470.getBoatClass(), - Arrays.asList(new Competitor[] { philippBuhl, antonKoch })); + AbstractTracTracLiveTest.createCompetitorAndBoatsMap(philippBuhl, antonKoch)); final RaceDefinitionImpl pfingstbush470R2 = new RaceDefinitionImpl("R2", new CourseImpl("up/down", Collections.emptyList()), pfingstbusch470.getBoatClass(), - Arrays.asList(new Competitor[] { philippBuhl, antonKoch })); + AbstractTracTracLiveTest.createCompetitorAndBoatsMap(philippBuhl, antonKoch)); server.apply(new AddRaceDefinition(pfingstbusch470.getRegattaIdentifier(), pfingstbush470R1)); server.apply(new AddRaceDefinition(pfingstbusch470.getRegattaIdentifier(), pfingstbush470R2)); // track only R1 and R2 @@ -221,9 +222,9 @@ public class SearchServiceTest { pfingstbusch470Leaderboard.getRaceColumnByName("R2").setTrackedRace(pfingstbusch470Leaderboard.getRaceColumnByName("R2").getFleetByName("Default"), pfingstbusch470TrackedR2); final RaceDefinitionImpl aalOrcR1 = new RaceDefinitionImpl("R1", new CourseImpl("up/down", Collections.emptyList()), aalRegatta.getBoatClass(), - Arrays.asList(new Competitor[] { hassoPlattner, dennisGehrlein, philippBuhl })); + AbstractTracTracLiveTest.createCompetitorAndBoatsMap(hassoPlattner, dennisGehrlein, philippBuhl)); final RaceDefinitionImpl aalOrcR2 = new RaceDefinitionImpl("R2", new CourseImpl("up/down", Collections.emptyList()), aalRegatta.getBoatClass(), - Arrays.asList(new Competitor[] { hassoPlattner, dennisGehrlein, philippBuhl })); + AbstractTracTracLiveTest.createCompetitorAndBoatsMap(hassoPlattner, dennisGehrlein, philippBuhl)); server.apply(new AddRaceDefinition(aalRegatta.getRegattaIdentifier(), aalOrcR1)); server.apply(new AddRaceDefinition(aalRegatta.getRegattaIdentifier(), aalOrcR2)); // track only R1 and R2 diff --git a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/WindByRaceLogTest.java b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/WindByRaceLogTest.java index f2d9c852d4f..e054974e774 100644 --- a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/WindByRaceLogTest.java +++ b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/WindByRaceLogTest.java @@ -5,8 +5,9 @@ import static org.junit.Assert.assertTrue; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.UUID; import org.junit.Before; @@ -17,16 +18,19 @@ import com.sap.sailing.domain.abstractlog.impl.LogEventAuthorImpl; import com.sap.sailing.domain.abstractlog.race.RaceLog; import com.sap.sailing.domain.abstractlog.race.RaceLogWindFixEvent; import com.sap.sailing.domain.abstractlog.race.impl.RaceLogWindFixEventImpl; +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.RaceDefinition; import com.sap.sailing.domain.base.Regatta; import com.sap.sailing.domain.base.Waypoint; -import com.sap.sailing.domain.base.impl.BoatImpl; +import com.sap.sailing.domain.base.impl.CompetitorWithBoatImpl; import com.sap.sailing.domain.base.impl.CourseImpl; +import com.sap.sailing.domain.base.impl.DynamicBoat; import com.sap.sailing.domain.base.impl.PersonImpl; import com.sap.sailing.domain.base.impl.RaceDefinitionImpl; import com.sap.sailing.domain.base.impl.RegattaImpl; @@ -80,7 +84,7 @@ public class WindByRaceLogTest { // FIXME use master DomainFactory; see bug 592 final DomainFactory masterDomainFactory = service.getBaseDomainFactory(); BoatClass boatClass = masterDomainFactory.getOrCreateBoatClass(boatClassName, /* typicallyStartsUpwind */true); - Competitor competitor = createCompetitor(masterDomainFactory); + CompetitorWithBoat competitor = createCompetitorWithBoat(masterDomainFactory, boatClass); int[] discardThreshold = {1, 2}; CreateFlexibleLeaderboard createLeaderboardOperation = new CreateFlexibleLeaderboard("Test Leaderboard", "Test", discardThreshold, new LowPoint(), null); service.apply(createLeaderboardOperation); @@ -93,7 +97,9 @@ public class WindByRaceLogTest { Regatta regatta = service.apply(addRegattaOperation); final String raceName = "Test Race"; final CourseImpl masterCourse = new CourseImpl("Test Course", new ArrayList()); - RaceDefinition race = new RaceDefinitionImpl(raceName, masterCourse, boatClass, Collections.singletonList(competitor)); + final Map competitorsAndBoats = new HashMap<>(); + competitorsAndBoats.put(competitor, competitor.getBoat()); + RaceDefinition race = new RaceDefinitionImpl(raceName, masterCourse, boatClass, competitorsAndBoats); AddRaceDefinition addRaceOperation = new AddRaceDefinition(new RegattaName(regatta.getName()), race); service.apply(addRaceOperation); masterCourse.addWaypoint(0, masterDomainFactory.createWaypoint(masterDomainFactory.getOrCreateMark("Mark1"), /*passingInstruction*/ null)); @@ -105,11 +111,13 @@ public class WindByRaceLogTest { defaultFleet = Util.get(raceColumn.getFleets(), 0); } - private Competitor createCompetitor(final DomainFactory masterDomainFactory) { - return masterDomainFactory.getOrCreateCompetitor("GER 61", "Sailor", Color.RED, "noone@nowhere.de", null, new TeamImpl("Sailor", + private CompetitorWithBoat createCompetitorWithBoat(final DomainFactory masterDomainFactory, final BoatClass boatClass) { + Competitor competitor = masterDomainFactory.getOrCreateCompetitor("GER 61", "Sailor", "S", Color.RED, "noone@nowhere.de", null, new TeamImpl("Sailor", (List) Arrays.asList(new PersonImpl[] { new PersonImpl("Sailor 1", DomainFactory.INSTANCE.getOrCreateNationality("GER"), null, null)}), new PersonImpl("Sailor 2", DomainFactory.INSTANCE.getOrCreateNationality("NED"), null, null)), - new BoatImpl("GER 61", DomainFactory.INSTANCE.getOrCreateBoatClass("470", /* typicallyStartsUpwind */ true), "GER 61"), /* timeOnTimeFactor */ null, /* timeOnDistanceAllowanceInSecondsPerNauticalMile */ null, null); + /* timeOnTimeFactor */ null, /* timeOnDistanceAllowanceInSecondsPerNauticalMile */ null, null); + DynamicBoat boat = (DynamicBoat) masterDomainFactory.getOrCreateBoat("boat", "GER 61", boatClass, "GER 61", null); + return new CompetitorWithBoatImpl(competitor, boat); } private void attachTrackedRaceToRaceColumn() { diff --git a/java/com.sap.sailing.server.trackfiles/src/com/sap/sailing/server/trackfiles/impl/BravoDataImporterImpl.java b/java/com.sap.sailing.server.trackfiles/src/com/sap/sailing/server/trackfiles/impl/BravoDataImporterImpl.java index ccac5747d9e..d292b98975b 100644 --- a/java/com.sap.sailing.server.trackfiles/src/com/sap/sailing/server/trackfiles/impl/BravoDataImporterImpl.java +++ b/java/com.sap.sailing.server.trackfiles/src/com/sap/sailing/server/trackfiles/impl/BravoDataImporterImpl.java @@ -3,8 +3,11 @@ package com.sap.sailing.server.trackfiles.impl; import java.io.Serializable; import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor; +import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceBoatSensorDataMappingEvent; import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceCompetitorSensorDataMappingEvent; +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.Boat; import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.common.DeviceIdentifier; import com.sap.sailing.domain.common.sensordata.BravoSensorDataMetadata; @@ -27,4 +30,12 @@ public class BravoDataImporterImpl extends AbstractBravoDataImporterImpl { return new RegattaLogDeviceCompetitorBravoMappingEventImpl(createdAt, logicalTimePoint, author, id, mappedTo, device, from, to); } + + @Override + public RegattaLogDeviceBoatSensorDataMappingEvent createEvent(TimePoint createdAt, TimePoint logicalTimePoint, + AbstractLogEventAuthor author, Serializable id, Boat mappedTo, DeviceIdentifier device, + TimePoint from, TimePoint to) { + return new RegattaLogDeviceBoatBravoMappingEventImpl(createdAt, logicalTimePoint, author, id, mappedTo, + device, from, to); + } } diff --git a/java/com.sap.sailing.server.trackfiles/src/com/sap/sailing/server/trackfiles/impl/BravoExtendedDataImporterImpl.java b/java/com.sap.sailing.server.trackfiles/src/com/sap/sailing/server/trackfiles/impl/BravoExtendedDataImporterImpl.java index 2265131c3d0..edaf21c91b6 100644 --- a/java/com.sap.sailing.server.trackfiles/src/com/sap/sailing/server/trackfiles/impl/BravoExtendedDataImporterImpl.java +++ b/java/com.sap.sailing.server.trackfiles/src/com/sap/sailing/server/trackfiles/impl/BravoExtendedDataImporterImpl.java @@ -3,8 +3,11 @@ package com.sap.sailing.server.trackfiles.impl; import java.io.Serializable; import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor; +import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceBoatSensorDataMappingEvent; import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceCompetitorSensorDataMappingEvent; +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.Boat; import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.common.DeviceIdentifier; import com.sap.sailing.domain.common.sensordata.BravoExtendedSensorDataMetadata; @@ -27,4 +30,12 @@ public class BravoExtendedDataImporterImpl extends AbstractBravoDataImporterImpl return new RegattaLogDeviceCompetitorBravoExtendedMappingEventImpl(createdAt, logicalTimePoint, author, id, mappedTo, device, from, to); } + + @Override + public RegattaLogDeviceBoatSensorDataMappingEvent createEvent(TimePoint createdAt, TimePoint logicalTimePoint, + AbstractLogEventAuthor author, Serializable id, Boat mappedTo, DeviceIdentifier device, + TimePoint from, TimePoint to) { + return new RegattaLogDeviceBoatBravoExtendedMappingEventImpl(createdAt, logicalTimePoint, author, id, mappedTo, + device, from, to); + } } diff --git a/java/com.sap.sailing.server.trackfiles/src/com/sap/sailing/server/trackfiles/impl/CompetitorDataRetriever.java b/java/com.sap.sailing.server.trackfiles/src/com/sap/sailing/server/trackfiles/impl/CompetitorDataRetriever.java index 8b473264152..de287f1882c 100644 --- a/java/com.sap.sailing.server.trackfiles/src/com/sap/sailing/server/trackfiles/impl/CompetitorDataRetriever.java +++ b/java/com.sap.sailing.server.trackfiles/src/com/sap/sailing/server/trackfiles/impl/CompetitorDataRetriever.java @@ -2,33 +2,37 @@ package com.sap.sailing.server.trackfiles.impl; import java.io.IOException; import java.util.Collection; +import java.util.Map; -import slash.navigation.gpx.GpxRoute; - +import com.sap.sailing.domain.base.Boat; import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.common.trackfiles.TrackFilesFormat; import com.sap.sailing.domain.common.tracking.GPSFixMoving; import com.sap.sailing.domain.trackimport.FormatNotSupportedException; import com.sap.sailing.domain.tracking.TrackedRace; +import slash.navigation.gpx.GpxRoute; + public class CompetitorDataRetriever extends AbstractDataRetriever { @Override public Collection getRoutes(TrackFilesFormat format, final TrackedRace race, boolean dataBeforeAfter, boolean rawFixes) throws FormatNotSupportedException, IOException { - TrackReaderRetriever retriever = new TrackReaderRetriever() { + TrackReaderRetriever, GPSFixMoving> retriever = new TrackReaderRetriever, GPSFixMoving>() { @Override - public TrackReader retrieveTrackReader(Competitor e) { - return new TrackReaderImpl(race.getTrack(e)); + public TrackReader, GPSFixMoving> retrieveTrackReader(Map.Entry e) { + return new TrackReaderImpl, GPSFixMoving>(race.getTrack(e.getKey())); } }; - + return getRoutes(race, dataBeforeAfter, rawFixes, GPSFixMovingToGpxPosition.INSTANCE, race.getRace() - .getCompetitors(), new NameReader() { + .getCompetitorsAndTheirBoats().entrySet(), new NameReader>() { @Override - public String getName(Competitor c) { - return c.getName() + " - " + c.getBoat().getSailID() + " - " + public String getName(Map.Entry competitorAndBoat) { + final Competitor c = competitorAndBoat.getKey(); + final Boat b = competitorAndBoat.getValue(); + return c.getName() + " - " + b.getSailID() + " - " + c.getTeam().getNationality().getThreeLetterIOCAcronym(); } }, retriever); diff --git a/java/com.sap.sailing.server.trackfiles/src/com/sap/sailing/server/trackfiles/impl/ExpeditionExtendedDataImporterImpl.java b/java/com.sap.sailing.server.trackfiles/src/com/sap/sailing/server/trackfiles/impl/ExpeditionExtendedDataImporterImpl.java index 346d90cb20d..cd9e6bf3ca3 100755 --- a/java/com.sap.sailing.server.trackfiles/src/com/sap/sailing/server/trackfiles/impl/ExpeditionExtendedDataImporterImpl.java +++ b/java/com.sap.sailing.server.trackfiles/src/com/sap/sailing/server/trackfiles/impl/ExpeditionExtendedDataImporterImpl.java @@ -23,8 +23,11 @@ import java.util.logging.Logger; import java.util.regex.Pattern; import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor; +import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceBoatSensorDataMappingEvent; import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceCompetitorSensorDataMappingEvent; +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.Boat; import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.common.DeviceIdentifier; import com.sap.sailing.domain.common.sensordata.ExpeditionExtendedSensorDataMetadata; @@ -80,6 +83,14 @@ public class ExpeditionExtendedDataImporterImpl extends AbstractDoubleVectorFixI return new RegattaLogDeviceCompetitorExpeditionExtendedMappingEventImpl(createdAt, logicalTimePoint, author, id, mappedTo, device, from, to); } + + @Override + public RegattaLogDeviceBoatSensorDataMappingEvent createEvent(TimePoint createdAt, TimePoint logicalTimePoint, + AbstractLogEventAuthor author, Serializable id, Boat mappedTo, DeviceIdentifier device, + TimePoint from, TimePoint to) { + return new RegattaLogDeviceBoatExpeditionExtendedMappingEventImpl(createdAt, logicalTimePoint, author, id, + mappedTo, device, from, to); + } @Override public boolean importFixes(InputStream inputStream, final Callback callback, String filename, String sourceName, diff --git a/java/com.sap.sailing.server/SailingServer (Proxy).launch b/java/com.sap.sailing.server/SailingServer (Proxy).launch index 5ef2a34f8ae..68b0206c5da 100644 --- a/java/com.sap.sailing.server/SailingServer (Proxy).launch +++ b/java/com.sap.sailing.server/SailingServer (Proxy).launch @@ -36,6 +36,6 @@ - + diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/RacingEventService.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/RacingEventService.java index ebfa0e4b327..e5eabd7a4d4 100644 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/RacingEventService.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/RacingEventService.java @@ -315,15 +315,17 @@ public interface RacingEventService extends TrackedRegattaRegistry, RegattaFetch Regatta getOrCreateDefaultRegatta(String name, String boatClassName, Serializable id); /** + * Creates a regatta and replicates this to all replicas currently attached. + * * @param series * the series must not have any {@link RaceColumn}s yet * @param controlTrackingFromStartAndFinishTimes * cannot be {@code true} if {@link useStartTimeInference} is also {@code true} */ - Regatta createRegatta(String regattaName, String boatClassName, TimePoint startDate, TimePoint endDate, Serializable id, Iterable series, + Regatta createRegatta(String regattaName, String boatClassName, boolean canBoatsOfCompetitorsChangePerRace, TimePoint startDate, TimePoint endDate, Serializable id, Iterable series, boolean persistent, ScoringScheme scoringScheme, Serializable defaultCourseAreaId, Double buoyZoneRadiusInHullLengths, boolean useStartTimeInference, boolean controlTrackingFromStartAndFinishTimes, RankingMetricConstructor rankingMetricConstructor); - + /** * @param controlTrackingFromStartAndFinishTimes * cannot be {@code true} if {@link useStartTimeInference} is also {@code true} @@ -471,6 +473,8 @@ public interface RacingEventService extends TrackedRegattaRegistry, RegattaFetch RaceLog getRaceLog(String leaderboardName, String raceColumnName, String fleetName); + Map getCompetitorToBoatMappingsForRace(String leaderboardName, String raceColumnName, String fleetName); + /** * @param controlTrackingFromStartAndFinishTimes TODO * @param rankingMetricConstructor TODO @@ -478,7 +482,7 @@ public interface RacingEventService extends TrackedRegattaRegistry, RegattaFetch * the call */ Util.Pair getOrCreateRegattaWithoutReplication(String fullRegattaName, String boatClassName, - TimePoint startDate, TimePoint endDate, Serializable id, + boolean canBoatsOfCompetitorsChangePerRace, TimePoint startDate, TimePoint endDate, Serializable id, Iterable series, boolean persistent, ScoringScheme scoringScheme, Serializable defaultCourseAreaId, Double buoyZoneRadiusInHullLengths, boolean useStartTimeInference, boolean controlTrackingFromStartAndFinishTimes, RankingMetricConstructor rankingMetricConstructor); diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/PersistentCompetitorStore.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/PersistentCompetitorStore.java index 746b83569f8..994d711cfd6 100755 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/PersistentCompetitorStore.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/PersistentCompetitorStore.java @@ -3,13 +3,24 @@ package com.sap.sailing.server.impl; import java.io.IOException; import java.io.ObjectInputStream; import java.net.URI; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.UUID; +import java.util.logging.Level; +import java.util.logging.Logger; import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver; +import com.sap.sailing.domain.base.Boat; import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.CompetitorStore; +import com.sap.sailing.domain.base.CompetitorWithBoat; import com.sap.sailing.domain.base.DomainFactory; import com.sap.sailing.domain.base.Nationality; +import com.sap.sailing.domain.base.impl.BoatImpl; +import com.sap.sailing.domain.base.impl.CompetitorWithBoatImpl; import com.sap.sailing.domain.base.impl.DomainFactoryImpl; +import com.sap.sailing.domain.base.impl.DynamicBoat; import com.sap.sailing.domain.base.impl.TransientCompetitorStoreImpl; import com.sap.sailing.domain.persistence.DomainObjectFactory; import com.sap.sailing.domain.persistence.MongoObjectFactory; @@ -20,12 +31,13 @@ import com.sap.sse.common.TypeBasedServiceFinderFactory; import com.sap.sse.mongodb.MongoDBService; /** - * Manages a persistent set of {@link Competitor}s using a {@link MongoObjectFactory} to update the persistent store, + * Manages a persistent set of {@link Competitor}s, {@link CompetitorWithBoat}s and {@link Boat}s using a {@link MongoObjectFactory} to update the persistent store, * and a {@link DomainObjectFactory} for initially filling this store's in-memory representation from the persistent * store.

* - * Note that the results of calling {@link CompetitorStore#allowCompetitorResetToDefaults(Competitor)} are not stored - * persistently and therefore will be reset when re-initializing a competitor store from the database. + * Note that the results of calling {@link CompetitorStore#allowCompetitorResetToDefaults(Competitor)} and + * {@link CompetitorStore#allowBoatResetToDefaults(Boat)} are not stored + * persistently and therefore will be reset when re-initializing a competitor/boat store from the database. * * @author Axel Uhl (d043530) * @@ -34,26 +46,57 @@ public class PersistentCompetitorStore extends TransientCompetitorStoreImpl impl private static final long serialVersionUID = 9205956018421790908L; private transient MongoObjectFactory storeTo; private transient DomainObjectFactory loadFrom; + private static final Logger logger = Logger.getLogger(PersistentCompetitorStore.class.getName()); /** - * @param clear - * if true, the persistent competitor store is initially cleared, with all persistent - * competitor data removed; use with caution! + * @param clearCompetitorsAndBaots + * if true, the persistent competitor and boats store is initially cleared, with all persistent + * competitor and boat data removed; use with caution! */ - public PersistentCompetitorStore(MongoObjectFactory storeTo, boolean clear, + public PersistentCompetitorStore(MongoObjectFactory storeTo, boolean clearCompetitorsAndBaots, TypeBasedServiceFinderFactory serviceFinderFactory, RaceLogResolver raceLogResolver) { DomainFactoryImpl baseDomainFactory = new DomainFactoryImpl(this, raceLogResolver); this.loadFrom = PersistenceFactory.INSTANCE.getDomainObjectFactory(MongoDBService.INSTANCE, baseDomainFactory, serviceFinderFactory); this.storeTo = storeTo; - if (clear) { + migrateCompetitorsIfRequired(); + if (clearCompetitorsAndBaots) { + storeTo.removeAllBoats(); storeTo.removeAllCompetitors(); } else { - for (Competitor competitor : loadFrom.loadAllCompetitors()) { + Collection allBoats = loadFrom.loadAllBoats(); + for (Boat boat: allBoats) { + super.addNewBoat(boat); + } + Collection allCompetitors = loadFrom.loadAllCompetitors(); + for (Competitor competitor : allCompetitors) { super.addNewCompetitor(competitor); } } } + /** + * Migrate competitors with contained boats (before bug2822) to competitors with separate boats if required + */ + private void migrateCompetitorsIfRequired() { + Iterable allLegacyCompetitorsWithBoat = loadFrom.migrateLegacyCompetitorsIfRequired(); + if (allLegacyCompetitorsWithBoat != null) { + List newCompetitors = new ArrayList<>(); + List newBoats = new ArrayList<>(); + for (CompetitorWithBoat competitorWithBoat : allLegacyCompetitorsWithBoat) { + Boat containedBoat = competitorWithBoat.getBoat(); + // Create a new random uuid for the boats to make sure the competitor uuid is not used + UUID boatUUID = UUID.randomUUID(); + DynamicBoat newBoat = new BoatImpl(boatUUID, null /* the old boat names were nonsense names */, containedBoat.getBoatClass(), containedBoat.getSailID()); + newBoats.add(newBoat); + CompetitorWithBoat newCompetitorWithBoat = new CompetitorWithBoatImpl((Competitor) competitorWithBoat, newBoat); + newCompetitors.add(newCompetitorWithBoat); + } + logger.log(Level.INFO, "Bug2822 DB-Migration: Store migrated competitors and boats."); + storeTo.storeCompetitors(newCompetitors); + storeTo.storeBoats(newBoats); + } + } + DomainObjectFactory getDomainObjectFactory() { return loadFrom; } @@ -74,7 +117,17 @@ public class PersistentCompetitorStore extends TransientCompetitorStoreImpl impl ois.defaultReadObject(); storeTo = PersistenceFactory.INSTANCE.getDefaultMongoObjectFactory(); } - + + @Override + public synchronized Competitor migrateToCompetitorWithoutBoat(CompetitorWithBoat competitorWithBoat) { + // It should not be possible to call this for a competitor which has already been migrated. + assert competitorWithBoat.hasBoat(); + logger.log(Level.INFO, "Bug2822 DB-Migration: Migrate competitor " + competitorWithBoat.getName() + " with ID " + competitorWithBoat.getId().toString() + " to have a separate boat"); + Competitor migratedCompetitor = super.migrateToCompetitorWithoutBoat(competitorWithBoat); + storeTo.storeCompetitor(migratedCompetitor); + return migratedCompetitor; + } + @Override protected void addNewCompetitor(Competitor competitor) { storeTo.storeCompetitor(competitor); @@ -82,30 +135,69 @@ public class PersistentCompetitorStore extends TransientCompetitorStoreImpl impl } @Override - public void clear() { + public void clearCompetitors() { storeTo.removeAllCompetitors(); - super.clear(); + super.clearCompetitors(); } - @Override - public void removeCompetitor(Competitor competitor) { + protected void removeCompetitor(Competitor competitor) { storeTo.removeCompetitor(competitor); super.removeCompetitor(competitor); } @Override - public Competitor updateCompetitor(String idAsString, String newName, Color newRgbDisplayColor, String newEmail, String newSailId, + public Competitor updateCompetitor(String idAsString, String newName, String newShortName, Color newRgbDisplayColor, String newEmail, Nationality newNationality, URI newTeamImageUri, URI newFlagImageUri, Double timeOnTimeFactor, Duration timeOnDistanceAllowancePerNauticalMile, String searchTag) { - Competitor result = super.updateCompetitor(idAsString, newName, newRgbDisplayColor, newEmail, newSailId, newNationality, + Competitor result = super.updateCompetitor(idAsString, newName, newShortName, newRgbDisplayColor, newEmail, newNationality, newTeamImageUri, newFlagImageUri, timeOnTimeFactor, timeOnDistanceAllowancePerNauticalMile, searchTag); storeTo.storeCompetitor(result); return result; } - + @Override public void addNewCompetitors(Iterable competitors) { storeTo.storeCompetitors(competitors); super.addNewCompetitors(competitors); } + + @Override + public CompetitorWithBoat updateCompetitorWithBoat(String idAsString, String newName, String newShortName, Color newRgbDisplayColor, String newEmail, + Nationality newNationality, URI newTeamImageUri, URI newFlagImageUri, Double timeOnTimeFactor, Duration timeOnDistanceAllowancePerNauticalMile, + String searchTag, DynamicBoat boat) { + CompetitorWithBoat result = super.updateCompetitorWithBoat(idAsString, newName, newShortName, newRgbDisplayColor, newEmail, newNationality, + newTeamImageUri, newFlagImageUri, timeOnTimeFactor, timeOnDistanceAllowancePerNauticalMile, searchTag, boat); + storeTo.storeCompetitor(result); + return result; + } + + @Override + protected void addNewBoat(Boat boat) { + storeTo.storeBoat(boat); + super.addNewBoat(boat); + } + + @Override + public void clearBoats() { + storeTo.removeAllBoats(); + super.clearBoats(); + } + + protected void removeBoat(Boat boat) { + storeTo.removeBoat(boat); + super.removeBoat(boat); + } + + @Override + public Boat updateBoat(String idAsString, String newName, Color newColor, String newSailId) { + Boat result = super.updateBoat(idAsString, newName, newColor, newSailId); + storeTo.storeBoat(result); + return result; + } + + @Override + public void addNewBoats(Iterable boats) { + storeTo.storeBoats(boats); + super.addNewBoats(boats); + } } diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java index 02f7aedf5a4..2943c3a8d5a 100644 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java @@ -64,11 +64,13 @@ import com.sap.sailing.domain.abstractlog.race.state.ReadonlyRaceState; import com.sap.sailing.domain.abstractlog.race.state.impl.RaceStateImpl; import com.sap.sailing.domain.abstractlog.race.state.impl.ReadonlyRaceStateImpl; import com.sap.sailing.domain.abstractlog.regatta.RegattaLog; +import com.sap.sailing.domain.abstractlog.shared.analyzing.CompetitorsAndBoatsInLogAnalyzer; import com.sap.sailing.domain.anniversary.DetailedRaceInfo; import com.sap.sailing.domain.anniversary.SimpleRaceInfo; import com.sap.sailing.domain.base.Boat; import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.CompetitorStore; +import com.sap.sailing.domain.base.CompetitorStore.BoatUpdateListener; import com.sap.sailing.domain.base.CompetitorStore.CompetitorUpdateListener; import com.sap.sailing.domain.base.ControlPoint; import com.sap.sailing.domain.base.CourseArea; @@ -94,9 +96,8 @@ import com.sap.sailing.domain.base.configuration.DeviceConfigurationIdentifier; import com.sap.sailing.domain.base.configuration.DeviceConfigurationMatcher; import com.sap.sailing.domain.base.configuration.RegattaConfiguration; import com.sap.sailing.domain.base.configuration.impl.DeviceConfigurationMapImpl; -import com.sap.sailing.domain.base.impl.BoatClassImpl; -import com.sap.sailing.domain.base.impl.BoatImpl; import com.sap.sailing.domain.base.impl.DynamicCompetitor; +import com.sap.sailing.domain.base.impl.DynamicCompetitorWithBoat; import com.sap.sailing.domain.base.impl.EventImpl; import com.sap.sailing.domain.base.impl.RegattaImpl; import com.sap.sailing.domain.base.impl.RemoteSailingServerReferenceImpl; @@ -206,6 +207,7 @@ import com.sap.sailing.server.operationaltransformation.AddMediaTrackOperation; import com.sap.sailing.server.operationaltransformation.AddRaceDefinition; import com.sap.sailing.server.operationaltransformation.AddSpecificRegatta; import com.sap.sailing.server.operationaltransformation.ConnectTrackedRaceToLeaderboardColumn; +import com.sap.sailing.server.operationaltransformation.CreateBoat; import com.sap.sailing.server.operationaltransformation.CreateCompetitor; import com.sap.sailing.server.operationaltransformation.CreateEvent; import com.sap.sailing.server.operationaltransformation.CreateOrUpdateDataImportProgress; @@ -227,6 +229,7 @@ import com.sap.sailing.server.operationaltransformation.RemoveWindFix; import com.sap.sailing.server.operationaltransformation.RenameEvent; import com.sap.sailing.server.operationaltransformation.SetDataImportDeleteProgressFromMapTimer; import com.sap.sailing.server.operationaltransformation.TrackRegatta; +import com.sap.sailing.server.operationaltransformation.UpdateBoat; import com.sap.sailing.server.operationaltransformation.UpdateCompetitor; import com.sap.sailing.server.operationaltransformation.UpdateEndOfTracking; import com.sap.sailing.server.operationaltransformation.UpdateMarkPassings; @@ -511,18 +514,18 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes /** * Constructs a {@link DomainFactory base domain factory} that uses this object's {@link #competitorStore competitor - * store} for competitor management. This base domain factory is then also used for the construction of the - * {@link DomainObjectFactory}. This constructor variant initially clears the persistent competitor collection, - * hence removes all previously persistent competitors. This is the default for testing and for backward - * compatibility with prior releases that did not support a persistent competitor collection. + * store} for competitor and boat management. This base domain factory is then also used for the construction of the + * {@link DomainObjectFactory}. This constructor variant initially clears the persistent competitor and boat collections, + * hence removes all previously persistent competitors and boats. This is the default for testing and for backward + * compatibility with prior releases that did not support a persistent competitor and boat collection. */ public RacingEventServiceImpl() { - this(/* clearPersistentCompetitorStore */ true, /* serviceFinderFactory */ null, /* restoreTrackedRaces */ false); + this(/* clearPersistentCompetitorAndBoatStore */ true, /* serviceFinderFactory */ null, /* restoreTrackedRaces */ false); } public RacingEventServiceImpl(WindStore windStore, SensorFixStore sensorFixStore, TypeBasedServiceFinderFactory serviceFinderFactory) { - this(/* clearPersistentCompetitorStore */ true, windStore, sensorFixStore, serviceFinderFactory, + this(/* clearPersistentCompetitorAndBoatStore */ true, windStore, sensorFixStore, serviceFinderFactory, /* sailingNotificationService */ null, /* restoreTrackedRaces */ false); } @@ -530,19 +533,17 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes this.bundleContext = bundleContext; } - public RacingEventServiceImpl(boolean clearPersistentCompetitorStore, - final TypeBasedServiceFinderFactory serviceFinderFactory, boolean restoreTrackedRaces) { - this(clearPersistentCompetitorStore, serviceFinderFactory, null, /* sailingNotificationService */ null, - /* trackedRaceStatisticsCache */ null, restoreTrackedRaces); + public RacingEventServiceImpl(boolean clearPersistentCompetitorAndBoatStore, final TypeBasedServiceFinderFactory serviceFinderFactory, boolean restoreTrackedRaces) { + this(clearPersistentCompetitorAndBoatStore, serviceFinderFactory, null, /* sailingNotificationService */ null, /* trackedRaceStatisticsCache */ null, restoreTrackedRaces); } /** * Like {@link #RacingEventServiceImpl()}, but allows callers to specify that the persistent competitor collection * be cleared before the service starts. * - * @param clearPersistentCompetitorStore + * @param clearPersistentCompetitorAndBoatStore * if true, the {@link PersistentCompetitorStore} is created empty, with the corresponding - * database collection cleared as well. Use with caution! When used with false, competitors + * database collection cleared as well. Use with caution! When used with false, competitors and boats * created and stored during previous service executions will initially be loaded. * @param sailingNotificationService * a notification service to call upon events worth notifying users about, or {@code null} if no @@ -551,7 +552,7 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes * a cache that gives access to detailed statistics about TrackedRaces. If null, no detailed * statistics about TrackedRaces will be calculated. */ - public RacingEventServiceImpl(boolean clearPersistentCompetitorStore, + public RacingEventServiceImpl(boolean clearPersistentCompetitorAndBoatStore, final TypeBasedServiceFinderFactory serviceFinderFactory, TrackedRegattaListenerManager trackedRegattaListener, SailingNotificationService sailingNotificationService, TrackedRaceStatisticsCache trackedRaceStatisticsCache, boolean restoreTrackedRaces) { @@ -561,7 +562,7 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes .getDefaultMongoObjectFactory(serviceFinderFactory); private final PersistentCompetitorStore competitorStore = new PersistentCompetitorStore( PersistenceFactory.INSTANCE.getDefaultMongoObjectFactory(serviceFinderFactory), - clearPersistentCompetitorStore, serviceFinderFactory, raceLogResolver); + clearPersistentCompetitorAndBoatStore, serviceFinderFactory, raceLogResolver); @Override public DomainObjectFactory getDomainObjectFactory() { @@ -701,23 +702,33 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes this.competitorStore.addCompetitorUpdateListener(new CompetitorUpdateListener() { @Override public void competitorUpdated(Competitor competitor) { - replicate(new UpdateCompetitor(competitor.getId().toString(), competitor.getName(), - competitor.getColor(), competitor.getEmail(), competitor.getBoat().getSailID(), - competitor.getTeam().getNationality(), competitor.getTeam().getImage(), - competitor.getFlagImage(), competitor.getTimeOnTimeFactor(), - competitor.getTimeOnDistanceAllowancePerNauticalMile(), competitor.getSearchTag())); + replicate(new UpdateCompetitor(competitor.getId().toString(), competitor.getName(), competitor.getShortName(), competitor + .getColor(), competitor.getEmail(), competitor.getTeam().getNationality(), + competitor.getTeam().getImage(), competitor.getFlagImage(), + competitor.getTimeOnTimeFactor(), competitor.getTimeOnDistanceAllowancePerNauticalMile(), + competitor.getSearchTag())); } @Override public void competitorCreated(Competitor competitor) { - replicate(new CreateCompetitor(competitor.getId(), competitor.getName(), - competitor.getBoat()==null?null:competitor.getBoat().getBoatClass()==null?null:competitor.getBoat().getBoatClass().getName(), - competitor.getColor(), competitor.getEmail(), - competitor.getFlagImage(), competitor.getBoat()==null?null:competitor.getBoat().getSailID(), + replicate(new CreateCompetitor(competitor.getId(), competitor.getName(), competitor.getShortName(), + competitor.getColor(), competitor.getEmail(), competitor.getFlagImage(), competitor.getTeam()==null?null:competitor.getTeam().getNationality(), competitor.getTimeOnTimeFactor(), competitor.getTimeOnDistanceAllowancePerNauticalMile(), competitor.getSearchTag())); } }); + this.competitorStore.addBoatUpdateListener(new BoatUpdateListener() { + @Override + public void boatUpdated(Boat boat) { + replicate(new UpdateBoat(boat.getId().toString(), boat.getName(), boat.getColor(), boat.getSailID())); + } + @Override + public void boatCreated(Boat boat) { + replicate(new CreateBoat(boat.getId(), boat.getName(), + boat.getBoatClass()==null?null:boat.getBoatClass().getName(), + boat.getSailID(), boat.getColor())); + } + }); this.dataImportLock = new DataImportLockWithProgress(); remoteSailingServerSet = new RemoteSailingServerSet(scheduler, baseDomainFactory); @@ -1404,7 +1415,8 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes Regatta result = regattasByName.get(name); if (result == null) { result = new RegattaImpl(getRaceLogStore(), getRegattaLogStore(), name, getBaseDomainFactory() - .getOrCreateBoatClass(boatClassName), /* startDate */null, /* endDate */null, this, + .getOrCreateBoatClass(boatClassName), /* canBoatsOfCompetitorsChangePerRace*/ false, + /* startDate */null, /* endDate */null, this, getBaseDomainFactory().createScoringScheme(ScoringSchemeType.LOW_POINT), id, null); logger.info("Created default regatta " + result.getName() + " (" + hashCode() + ") on " + this); onRegattaLikeAdded(result); @@ -1423,7 +1435,7 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes } @Override - public Regatta createRegatta(String fullRegattaName, String boatClassName, TimePoint startDate, TimePoint endDate, + public Regatta createRegatta(String fullRegattaName, String boatClassName, boolean canBoatsOfCompetitorsChangePerRace, TimePoint startDate, TimePoint endDate, Serializable id, Iterable series, boolean persistent, ScoringScheme scoringScheme, Serializable defaultCourseAreaId, Double buoyZoneRadiusInHullLengths, boolean useStartTimeInference, boolean controlTrackingFromStartAndFinishTimes, RankingMetricConstructor rankingMetricConstructor) { @@ -1431,7 +1443,7 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes throw new IllegalArgumentException("Cannot set both of useStartTimeInference and controlTrackingFromStartAndFinishTimes to true"); } com.sap.sse.common.Util.Pair regattaWithCreatedFlag = getOrCreateRegattaWithoutReplication( - fullRegattaName, boatClassName, startDate, endDate, id, series, persistent, scoringScheme, + fullRegattaName, boatClassName, canBoatsOfCompetitorsChangePerRace, startDate, endDate, id, series, persistent, scoringScheme, defaultCourseAreaId, buoyZoneRadiusInHullLengths, useStartTimeInference, controlTrackingFromStartAndFinishTimes, rankingMetricConstructor); Regatta regatta = regattaWithCreatedFlag.getA(); if (regattaWithCreatedFlag.getB()) { @@ -1463,13 +1475,13 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes @Override public com.sap.sse.common.Util.Pair getOrCreateRegattaWithoutReplication(String fullRegattaName, - String boatClassName, TimePoint startDate, TimePoint endDate, Serializable id, + String boatClassName, boolean canBoatsOfCompetitorsChangePerRace, TimePoint startDate, TimePoint endDate, Serializable id, Iterable series, boolean persistent, ScoringScheme scoringScheme, Serializable defaultCourseAreaId, Double buoyZoneRadiusInHullLengths, boolean useStartTimeInference, boolean controlTrackingFromStartAndFinishTimes, RankingMetricConstructor rankingMetricConstructor) { CourseArea courseArea = getCourseArea(defaultCourseAreaId); Regatta regatta = new RegattaImpl(getRaceLogStore(), getRegattaLogStore(), fullRegattaName, - getBaseDomainFactory().getOrCreateBoatClass(boatClassName), startDate, endDate, series, persistent, + getBaseDomainFactory().getOrCreateBoatClass(boatClassName), canBoatsOfCompetitorsChangePerRace, startDate, endDate, series, persistent, scoringScheme, id, courseArea, buoyZoneRadiusInHullLengths, useStartTimeInference, controlTrackingFromStartAndFinishTimes, rankingMetricConstructor); boolean wasCreated = addAndConnectRegatta(persistent, defaultCourseAreaId, regatta); if (wasCreated) { @@ -1569,10 +1581,10 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes Regatta regatta = regattaToAddTo == null ? null : getRegatta(regattaToAddTo); if (regatta == null) { // create tracker and use an existing or create a default regatta - tracker = params.createRaceTracker(this, windStore, /* raceLogResolver */ this, timeoutInMilliseconds); + tracker = params.createRaceTracker(this, windStore, /* raceLogResolver */ this, /* leaderboardGroupResolver */ this, timeoutInMilliseconds); } else { // use the regatta selected by the RaceIdentifier regattaToAddTo - tracker = params.createRaceTracker(regatta, this, windStore, /* raceLogResolver */ this, timeoutInMilliseconds); + tracker = params.createRaceTracker(regatta, this, windStore, /* raceLogResolver */ this, /* leaderboardGroupResolver */ this, timeoutInMilliseconds); assert tracker.getRegatta() == regatta; } LockUtil.lockForWrite(raceTrackersByRegattaLock); @@ -1672,7 +1684,8 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes courseAreaId = regatta.getDefaultCourseArea().getId(); } replicate(new AddSpecificRegatta(regatta.getName(), regatta.getBoatClass() == null ? null : regatta - .getBoatClass().getName(), regatta.getStartDate(), regatta.getEndDate(), regatta.getId(), + .getBoatClass().getName(), regatta.canBoatsOfCompetitorsChangePerRace(), + regatta.getStartDate(), regatta.getEndDate(), regatta.getId(), getSeriesWithoutRaceColumnsConstructionParametersAsMap(regatta), regatta.isPersistent(), regatta.getScoringScheme(), courseAreaId, regatta.getBuoyZoneRadiusInHullLengths(), regatta.useStartTimeInference(), regatta.isControlTrackingFromStartAndFinishTimes(), regatta.getRankingMetricType())); @@ -2672,6 +2685,18 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes return leaderboardGroupsByID.get(leaderboardGroupID); } + @Override + public LeaderboardGroup resolveLeaderboardGroupByRegattaName(String regattaName) { + for (LeaderboardGroup leaderboardGroup: getLeaderboardGroups().values()) { + for (Leaderboard leaderboard: leaderboardGroup.getLeaderboards()) { + if (leaderboard.getName().equals(regattaName)) { + return leaderboardGroup; + } + } + } + return null; + } + @Override public LeaderboardGroup addLeaderboardGroup(UUID leaderboardGroupId, String groupName, String description, String displayName, boolean displayGroupsInReverseOrder, List leaderboardNames, @@ -2904,7 +2929,7 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes } logger.info("Serializing persisted competitors..."); oos.writeObject(competitorStore); - logoutput.append("Serialized " + competitorStore.size() + " persisted competitors\n"); + logoutput.append("Serialized " + competitorStore.getCompetitorsCount() + " persisted competitors\n"); logger.info("Serializing configuration map..."); oos.writeObject(configurationMap); @@ -3012,17 +3037,25 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes // only copy the competitors from the deserialized competitor store; don't use it because it will have set // a default Mongo object factory logger.info("Reading competitors..."); - for (Competitor competitor : ((CompetitorStore) ois.readObject()).getCompetitors()) { + for (Competitor competitor : ((CompetitorStore) ois.readObject()).getAllCompetitors()) { DynamicCompetitor dynamicCompetitor = (DynamicCompetitor) competitor; // the following should actually be redundant because during de-serialization the Competitor objects, // whose classes implement IsManagedByCache, should already have been got/created from/in the // competitor store - competitorStore.getOrCreateCompetitor(dynamicCompetitor.getId(), dynamicCompetitor.getName(), - dynamicCompetitor.getColor(), dynamicCompetitor.getEmail(), dynamicCompetitor.getFlagImage(), - dynamicCompetitor.getTeam(), dynamicCompetitor.getBoat(), dynamicCompetitor.getTimeOnTimeFactor(), - dynamicCompetitor.getTimeOnDistanceAllowancePerNauticalMile(), dynamicCompetitor.getSearchTag()); + if (dynamicCompetitor.hasBoat()) { + competitorStore.getOrCreateCompetitorWithBoat(dynamicCompetitor.getId(), dynamicCompetitor.getName(), dynamicCompetitor.getShortName(), + dynamicCompetitor.getColor(), dynamicCompetitor.getEmail(), dynamicCompetitor.getFlagImage(), + dynamicCompetitor.getTeam(), dynamicCompetitor.getTimeOnTimeFactor(), + dynamicCompetitor.getTimeOnDistanceAllowancePerNauticalMile(), dynamicCompetitor.getSearchTag(), + ((DynamicCompetitorWithBoat) dynamicCompetitor).getBoat()); + } else { + competitorStore.getOrCreateCompetitor(dynamicCompetitor.getId(), dynamicCompetitor.getName(), dynamicCompetitor.getShortName(), + dynamicCompetitor.getColor(), dynamicCompetitor.getEmail(), dynamicCompetitor.getFlagImage(), + dynamicCompetitor.getTeam(), dynamicCompetitor.getTimeOnTimeFactor(), + dynamicCompetitor.getTimeOnDistanceAllowancePerNauticalMile(), dynamicCompetitor.getSearchTag()); + } } - logoutput.append("Received " + competitorStore.size() + " NEW competitors\n"); + logoutput.append("Received " + competitorStore.getCompetitorsCount() + " NEW competitors\n"); logger.info("Reading device configurations..."); configurationMap.putAll((DeviceConfigurationMapImpl) ois.readObject()); @@ -3124,7 +3157,7 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes connectivityParametersByRace.clear(); eventsById.clear(); mediaLibrary.clear(); - competitorStore.clear(); + competitorStore.clearCompetitors(); remoteSailingServerSet.clear(); if (notificationService != null) { notificationService.stop(); @@ -3436,7 +3469,7 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes public String toString() { return "RacingEventService: " + this.hashCode() + " Build: " + ServerInfo.getBuildVersion(); } - + @Override public void reloadRaceLog(String leaderboardName, String raceColumnName, String fleetName) { Leaderboard leaderboard = getLeaderboardByName(leaderboardName); @@ -3450,7 +3483,7 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes logger.info("Reloaded race log for fleet " + fleetImpl + " for race column " + raceColumn.getName() + " for leaderboard " + leaderboard.getName()); } - } + } } } @@ -3553,6 +3586,36 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes return null; } + public Map getCompetitorToBoatMappingsForRace(String leaderboardName, String raceColumnName, String fleetName) { + Map result = new HashMap<>(); + Leaderboard leaderboard = getLeaderboardByName(leaderboardName); + if (leaderboard != null) { + RaceColumn raceColumn = leaderboard.getRaceColumnByName(raceColumnName); + Fleet fleet = leaderboard.getFleet(fleetName); + if (raceColumn != null && fleet != null) { + RaceLog raceLog = raceColumn.getRaceLog(fleet); + // take the boats first from the racelog + if (raceLog != null) { + Map competitorAndBoatsInRacelog = new CompetitorsAndBoatsInLogAnalyzer<>(raceLog).analyze(); + result.putAll(competitorAndBoatsInRacelog); + } + // now look into the tracked race for mappings of competitors without a boat + TrackedRace trackedRace = raceColumn.getTrackedRace(fleet); + if (trackedRace != null) { + Map competitorsAndBoatsFromRaceDef = trackedRace.getRace().getCompetitorsAndTheirBoats(); + for (Competitor competitor: competitorsAndBoatsFromRaceDef.keySet()) { + if (!result.containsKey(competitor)) { + result.put(competitor, competitorsAndBoatsFromRaceDef.get(competitor)); + } + } + } + } + } + return result; + } + + + @Override public com.sap.sse.common.Util.Triple getStartTimeAndProcedure( String leaderboardName, String raceColumnName, String fleetName) { @@ -4198,11 +4261,6 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes Leaderboard leaderboard = getLeaderboardByName(leaderboardName); List competitors = Util.createList(leaderboard.getAllCompetitors()); Collections.shuffle(competitors); - //TODO (bug4403) get Boats of Regatta/Leaderboard - List boats = new ArrayList<>(); - for (int slot = 0; slot < pairingListTemplate.getPairingListTemplate()[0].length; slot++) { - boats.add(new BoatImpl("Boat " + (slot + 1), new BoatClassImpl("49er", true), "DE" + slot)); - } PairingList pairingList = pairingListTemplate.createPairingList( new CompetitionFormat() { @Override @@ -4223,7 +4281,7 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes } @Override public Iterable getCompetitorAllocation() { - return boats; + return leaderboard.getAllBoats(); } }); return pairingList; diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RegattaByKeywordSearchService.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RegattaByKeywordSearchService.java index 2b9b9d93488..0c501a4abcd 100755 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RegattaByKeywordSearchService.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RegattaByKeywordSearchService.java @@ -72,7 +72,7 @@ public class RegattaByKeywordSearchService { leaderboardStrings.add(leaderboard.getDisplayName()); for (Competitor competitor : leaderboard.getCompetitors()) { leaderboardStrings.add(competitor.getName()); - leaderboardStrings.add(competitor.getBoat().getSailID()); + leaderboardStrings.add(competitor.getShortName()); String competitorDisplayName = leaderboard.getDisplayName(competitor); if (competitorDisplayName != null) { leaderboardStrings.add(competitorDisplayName); diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/masterdata/DummyTrackedRace.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/masterdata/DummyTrackedRace.java index 67ad65e2698..ed4b5d27dd0 100755 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/masterdata/DummyTrackedRace.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/masterdata/DummyTrackedRace.java @@ -2,13 +2,14 @@ package com.sap.sailing.server.masterdata; import java.io.Serializable; import java.util.Collections; -import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.NavigableSet; import java.util.Set; 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.base.Competitor; import com.sap.sailing.domain.base.Leg; import com.sap.sailing.domain.base.Mark; @@ -69,29 +70,26 @@ public class DummyTrackedRace extends TrackedRaceWithWindEssentials { private static final long serialVersionUID = -11522605089325440L; private Regatta regatta; - public DummyTrackedRace(final Serializable raceId, final Iterable competitors, - final Regatta regatta, final TrackedRegatta trackedRegatta, final WindStore windStore) { - this(competitors, regatta, trackedRegatta, - new RaceDefinitionImpl("DummyRace", new CourseImpl("Dummy Course", Collections. emptyList()), - regatta.getBoatClass(), competitors, raceId), - windStore); + public DummyTrackedRace(final Serializable raceId, final Map competitorsAndBoats, final Regatta regatta, final TrackedRegatta trackedRegatta, final WindStore windStore) { + this(competitorsAndBoats, regatta, trackedRegatta, new RaceDefinitionImpl("DummyRace", new CourseImpl("Dummy Course", Collections.emptyList()), + regatta.getBoatClass(), competitorsAndBoats, raceId), windStore); } - - public DummyTrackedRace(final Iterable competitors, final Regatta regatta, - final TrackedRegatta trackedRegatta, RaceDefinition race, WindStore windStore) { + + public DummyTrackedRace(final Map competitors, final Regatta regatta, final TrackedRegatta trackedRegatta, + RaceDefinition race, WindStore windStore) { super(race, trackedRegatta, windStore, -1); this.regatta = regatta; } - public DummyTrackedRace(final Iterable competitors, final Regatta regatta, - final TrackedRegatta trackedRegatta, RaceDefinition race) { - this(competitors, regatta, trackedRegatta, race, EmptyWindStore.INSTANCE); - + public DummyTrackedRace(final Map competitors, final Regatta regatta, final TrackedRegatta trackedRegatta, + RaceDefinition race) { + this(competitors, regatta, trackedRegatta, race, EmptyWindStore.INSTANCE); + } public DummyTrackedRace(final String raceName, final Serializable raceId) { super(new RaceDefinitionImpl(raceName, new CourseImpl("Dummy Course", Collections. emptyList()), - /* boatClass */ null, new HashSet(), raceId), null, EmptyWindStore.INSTANCE, -1); + /* boatClass */ null, Collections.emptyMap(), raceId), null, EmptyWindStore.INSTANCE, -1); } @Override @@ -642,6 +640,16 @@ public class DummyTrackedRace extends TrackedRaceWithWindEssentials { return null; } + @Override + public Boat getBoatOfCompetitor(Competitor competitor) { + return null; + } + + @Override + public Competitor getCompetitorOfBoat(Boat boat) { + return null; + } + @Override public RankingMetric getRankingMetric() { return null; diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/AddSpecificRegatta.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/AddSpecificRegatta.java index 763d662e751..ebdbfaeaecb 100755 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/AddSpecificRegatta.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/AddSpecificRegatta.java @@ -32,13 +32,15 @@ public class AddSpecificRegatta extends AbstractAddRegattaOperation { private final Double buoyZoneRadiusInHullLengths; private final boolean useStartTimeInference; private final boolean controlTrackingFromStartAndFinishTimes; + private final boolean canBoatsOfCompetitorsChangePerRace; private final RankingMetrics rankingMetricType; - public AddSpecificRegatta(String regattaName, String boatClassName, TimePoint startDate, TimePoint endDate, Serializable id, + public AddSpecificRegatta(String regattaName, String boatClassName, boolean canBoatsOfCompetitorsChangePerRace, TimePoint startDate, TimePoint endDate, Serializable id, RegattaCreationParametersDTO seriesNamesWithFleetNamesAndFleetOrderingAndMedalAndDiscardingThresholds, boolean persistent, ScoringScheme scoringScheme, Serializable defaultCourseAreaId, Double buoyZoneRadiusInHullLengths, boolean useStartTimeInference, boolean controlTrackingFromStartAndFinishTimes, RankingMetrics rankingMetricType) { super(regattaName, boatClassName, startDate, endDate, id); + this.canBoatsOfCompetitorsChangePerRace = canBoatsOfCompetitorsChangePerRace; this.seriesNamesWithFleetNamesAndFleetOrderingAndMedalAndStartsWithZeroScoreAndDiscardingThresholds = seriesNamesWithFleetNamesAndFleetOrderingAndMedalAndDiscardingThresholds; this.persistent = persistent; this.scoringScheme = scoringScheme; @@ -51,7 +53,7 @@ public class AddSpecificRegatta extends AbstractAddRegattaOperation { @Override public Regatta internalApplyTo(RacingEventService toState) throws Exception { - Regatta regatta = toState.createRegatta(getRegattaName(), getBoatClassName(), getStartDate(), getEndDate(), getId(), createSeries(toState), + Regatta regatta = toState.createRegatta(getRegattaName(), getBoatClassName(), canBoatsOfCompetitorsChangePerRace, getStartDate(), getEndDate(), getId(), createSeries(toState), persistent, scoringScheme, defaultCourseAreaId, buoyZoneRadiusInHullLengths, useStartTimeInference, controlTrackingFromStartAndFinishTimes, RankingMetricsFactory.getRankingMetricConstructor(rankingMetricType)); return regatta; } diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/AllowBoatResetToDefaults.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/AllowBoatResetToDefaults.java new file mode 100644 index 00000000000..7fdc49bc60e --- /dev/null +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/AllowBoatResetToDefaults.java @@ -0,0 +1,46 @@ +package com.sap.sailing.server.operationaltransformation; + +import java.util.ArrayList; + +import com.sap.sailing.domain.base.Boat; +import com.sap.sailing.domain.base.CompetitorStore; +import com.sap.sailing.server.RacingEventService; +import com.sap.sailing.server.RacingEventServiceOperation; +import com.sap.sse.common.Util; + +public class AllowBoatResetToDefaults extends AbstractRacingEventServiceOperation { + private static final long serialVersionUID = -3698113910122095903L; + private final Iterable boatIdsAsStrings; + + public AllowBoatResetToDefaults(Iterable boatIdsAsStrings) { + super(); + final ArrayList arrayList = new ArrayList<>(); // to guarantee serializability, even if an unmodifiable or singleton is passed + this.boatIdsAsStrings = arrayList; + Util.addAll(boatIdsAsStrings, arrayList); + } + + @Override + public Void internalApplyTo(RacingEventService toState) throws Exception { + final CompetitorStore competitorAndBoatStore = toState.getBaseDomainFactory().getCompetitorStore(); + for (String boatIdAsString : boatIdsAsStrings) { + Boat boat = competitorAndBoatStore.getExistingBoatByIdAsString(boatIdAsString); + if (boat != null) { + competitorAndBoatStore.allowBoatResetToDefaults(boat); + } + } + return null; + } + + @Override + public RacingEventServiceOperation transformClientOp(RacingEventServiceOperation serverOp) { + // TODO Auto-generated method stub + return null; + } + + @Override + public RacingEventServiceOperation transformServerOp(RacingEventServiceOperation clientOp) { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/CreateBoat.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/CreateBoat.java new file mode 100644 index 00000000000..1a971e019f7 --- /dev/null +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/CreateBoat.java @@ -0,0 +1,55 @@ +package com.sap.sailing.server.operationaltransformation; + +import java.io.Serializable; + +import com.sap.sailing.domain.base.Boat; +import com.sap.sailing.domain.base.BoatClass; +import com.sap.sailing.server.RacingEventService; +import com.sap.sailing.server.RacingEventServiceOperation; +import com.sap.sse.common.Color; + +public class CreateBoat extends AbstractRacingEventServiceOperation { + private static final long serialVersionUID = 4868428194661255331L; + private final Serializable boatId; + private final String name; + private final String boatClassName; + private final Color color; + private final String sailId; + + public CreateBoat(Serializable boatId, String name, String boatClassName, String sailId, Color color) { + super(); + this.boatId = boatId; + this.name = name; + this.boatClassName = boatClassName; + this.sailId = sailId; + this.color = color; + } + + /** + * {@link #internalApplyTo(RacingEventService)} already replicates the effects + */ + @Override + public boolean isRequiresExplicitTransitiveReplication() { + return false; + } + + @Override + public Boat internalApplyTo(RacingEventService toState) throws Exception { + BoatClass boatClass = toState.getBaseDomainFactory().getOrCreateBoatClass(boatClassName); + Boat result = toState.getBaseDomainFactory().getCompetitorStore().getOrCreateBoat(boatId, name, boatClass, sailId, color); + return result; + } + + @Override + public RacingEventServiceOperation transformClientOp(RacingEventServiceOperation serverOp) { + // TODO Auto-generated method stub + return null; + } + + @Override + public RacingEventServiceOperation transformServerOp(RacingEventServiceOperation clientOp) { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/CreateCompetitor.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/CreateCompetitor.java index 9fbdb115a5e..5836ccf7469 100755 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/CreateCompetitor.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/CreateCompetitor.java @@ -4,12 +4,9 @@ import java.io.Serializable; import java.net.URI; import java.util.Collections; -import com.sap.sailing.domain.base.BoatClass; import com.sap.sailing.domain.base.Competitor; -import com.sap.sailing.domain.base.Nationality; import com.sap.sailing.domain.base.CompetitorStore.CompetitorUpdateListener; -import com.sap.sailing.domain.base.impl.BoatImpl; -import com.sap.sailing.domain.base.impl.DynamicBoat; +import com.sap.sailing.domain.base.Nationality; import com.sap.sailing.domain.base.impl.DynamicPerson; import com.sap.sailing.domain.base.impl.DynamicTeam; import com.sap.sailing.domain.base.impl.PersonImpl; @@ -24,27 +21,25 @@ public class CreateCompetitor extends AbstractRacingEventServiceOperation { + private static final long serialVersionUID = 1341635844670894476L; + private final String idAsString; + private final String newName; + private final Color newColor; + private final String newSailId; + + /** + * @param idAsString + * Identifies the boat to update + */ + public UpdateBoat(String idAsString, String newName, Color newColor, String newSailId) { + super(); + this.idAsString = idAsString; + this.newName = newName; + this.newColor = newColor; + this.newSailId = newSailId; + } + + /** + * {@link #internalApplyTo(RacingEventService)} already replicates the effects + */ + @Override + public boolean isRequiresExplicitTransitiveReplication() { + return false; + } + + @Override + public Boat internalApplyTo(RacingEventService toState) throws Exception { + Boat result = toState.getBaseDomainFactory().getCompetitorStore().updateBoat(idAsString, newName, newColor, newSailId); + return result; + } + + @Override + public RacingEventServiceOperation transformClientOp(RacingEventServiceOperation serverOp) { + // TODO Auto-generated method stub + return null; + } + + @Override + public RacingEventServiceOperation transformServerOp(RacingEventServiceOperation clientOp) { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/UpdateCompetitor.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/UpdateCompetitor.java index 0d6fe29e24b..6cdcbde5af5 100755 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/UpdateCompetitor.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/UpdateCompetitor.java @@ -15,8 +15,8 @@ public class UpdateCompetitor extends AbstractRacingEventServiceOperationnull, the competitor obtains the "NONE" nationality, usually represented by a white * flag */ - public UpdateCompetitor(String idAsString, String newName, Color newDisplayColor, String newEmail, - String newSailId, Nationality newNationality, URI newTeamImageUri, URI newFlagImageUri, + public UpdateCompetitor(String idAsString, String newName, String newShortName, Color newDisplayColor, String newEmail, + Nationality newNationality, URI newTeamImageUri, URI newFlagImageUri, Double timeOnTimeFactor, Duration timeOnDistanceAllowancePerNauticalMile, String newSearchTag) { super(); this.idAsString = idAsString; this.newName = newName; + this.newShortName = newShortName; this.newDisplayColor = newDisplayColor; - this.newSailId = newSailId; this.newNationality = newNationality; this.newTeamImageUri = newTeamImageUri; this.newEmail = newEmail; @@ -61,7 +61,7 @@ public class UpdateCompetitor extends AbstractRacingEventServiceOperation result = new ArrayList(); RaceDefinition race = this.raceHandle.getRace(); - for (Competitor competitor : race.getCompetitors()) { - result.add(competitor.getName() + ", " + competitor.getBoat().getName()); + for (Entry competitorAndBoatEntry: race.getCompetitorsAndTheirBoats().entrySet()) { + result.add(competitorAndBoatEntry.getKey().getName() + ", " + competitorAndBoatEntry.getValue().getName()); } return result; } diff --git a/java/com.sap.sailing.www/release_notes_admin.html b/java/com.sap.sailing.www/release_notes_admin.html index 5a37f5901b3..44bae93bf8f 100755 --- a/java/com.sap.sailing.www/release_notes_admin.html +++ b/java/com.sap.sailing.www/release_notes_admin.html @@ -21,8 +21,36 @@

Release Notes - Administration Console

- +

March 2018

+
    +
  • Boats are now separate entities. See the "Tracked Races / Boats" tab in the admin console. + When creating a regatta, you now need to specify whether in that regatta the competitors + may sail on changing boats ("Can boats of competitors change per race"). + If boats can change, boat assignments need to be specified per + race. For the TracTrac connector this may happen as in the past, using the corresponding + metadata attributes (boatName, either one of boatId or boatUuid, and boatColor) on the + competitors in the race. For smartphone tracking, boats can be specified in the + "Connectors / Smartphone Tracking" tab by using the boat icon in the actions column of + the leaderboards table ("Boat Registrations"). This defines the boats from which you can + then select in the scope of each race of the regatta. When adding competitors to such + a race then you now have to assign boats from the boats list to each competitor before + you can confirm the competitor-to-race assignments. +
  • +
  • In addition to track competitors and marks using smartphone-tracking it is now possible to track boats. + This is e.g. useful for several national leagues where competitors are assigned dynamically to boats for each race. + In this case the competitors were used to bring a smartphone with them to the assigned boat. + With the recent changes regarding boat and competitor mapping per race, Smartphone-tracking is now enabled to dynamically track boat-mapped devices to the correct competitors in the context of a race. + There are two ways to add boats to be tracked to a regatta: +
      +
    • After adding a denotation to the racelog(s), add competitors to at least one race and ensure that the boats to be tracked are mapped to those
    • +
    • Register boats directly to the regatta
    • +
    + For available boats, you can now add mappings and generate a QR code in the corresponding dialog. + In addition, it is possible to upload tracking files and map them to boats. +
  • +
+
  • Further improvements of maneuver detection algorithm were introduced. Mark Passing is no longer regarded as a separate maneuver type. Instead, it is regarded as a supplementary information which gets appended to a maneuver instance. Therefore, a new filtering dimension was added for maneuver data mining, which is boolean and is named as "Mark Passing".
  • Added leaderboard group name as a dimension to a leaderboard's context
  • diff --git a/java/com.sap.sailing.xmlexport/src/com/sap/sailing/xmlexport/FoilingData.java b/java/com.sap.sailing.xmlexport/src/com/sap/sailing/xmlexport/FoilingData.java index 912164677dc..6ef310ee28a 100644 --- a/java/com.sap.sailing.xmlexport/src/com/sap/sailing/xmlexport/FoilingData.java +++ b/java/com.sap.sailing.xmlexport/src/com/sap/sailing/xmlexport/FoilingData.java @@ -139,7 +139,7 @@ public class FoilingData { result.append("UNKNOWN").append(";"); } result.append(competitor.getName()).append(";"); - result.append(competitor.getBoat().getSailID()).append(";"); + result.append(trackedRace.getBoatOfCompetitor(competitor).getSailID()).append(";"); result.append(competitor.getId()).append(";"); result.append(speedOfCompetitor != null ? speedOfCompetitor.getKnots() : "0.0").append(";"); result.append(rideHeight).append(";"); diff --git a/java/com.sap.sailing.xmlexport/src/com/sap/sailing/xmlexport/LeaderboardData.java b/java/com.sap.sailing.xmlexport/src/com/sap/sailing/xmlexport/LeaderboardData.java index b8b8b8e72a8..de0543b9f4d 100644 --- a/java/com.sap.sailing.xmlexport/src/com/sap/sailing/xmlexport/LeaderboardData.java +++ b/java/com.sap.sailing.xmlexport/src/com/sap/sailing/xmlexport/LeaderboardData.java @@ -19,7 +19,9 @@ import javax.servlet.http.HttpServletResponse; import org.jdom.Element; +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.Fleet; import com.sap.sailing.domain.base.Leg; import com.sap.sailing.domain.base.Mark; @@ -237,7 +239,8 @@ public class LeaderboardData extends ExportAction { MaxPointsReason mpr = leaderboard.getScoreCorrection().getMaxPointsReason(competitorInLeaderboard, column, timepointToBeUsed); if (mpr != null && !mpr.equals(MaxPointsReason.NONE)) { // add this competitor to the list to have him evaluated - Element competitorElement = createCompetitorXML(competitorInLeaderboard, leaderboard, /*shortVersion*/ true, null); + Boat boatOfCompetitor = null; // TODO: Where can we get the boat here? + Element competitorElement = createCompetitorXML(competitorInLeaderboard, boatOfCompetitor, leaderboard, /*shortVersion*/ true, null); Element competitorRaceDataElement = new Element("competitor_race_data"); MaxPointsReason maxPointsReason = leaderboard.getMaxPointsReason(competitorInLeaderboard, column, timepointToBeUsed); addNamedElementWithValue(competitorRaceDataElement, "max_points_reason", maxPointsReason.toString()); @@ -398,7 +401,8 @@ public class LeaderboardData extends ExportAction { if (fleetCompetitorIsSailingIn != null && fleetCompetitorIsSailingIn.equals(fleet) && !competitorsForColumn.contains(competitorInLeaderboard)) { if (mpr != null && !mpr.equals(MaxPointsReason.NONE)) { // add this competitor to the list to have him evaluated - Element competitorElement = createCompetitorXML(competitorInLeaderboard, leaderboard, /*shortVersion*/ true, null); + Boat boatOfCompetitor = leaderboard.getBoatOfCompetitor(competitorInLeaderboard, column, fleetCompetitorIsSailingIn); + Element competitorElement = createCompetitorXML(competitorInLeaderboard, boatOfCompetitor, leaderboard, /*shortVersion*/ true, null); Element competitorRaceDataElement = new Element("competitor_race_data"); MaxPointsReason maxPointsReason = leaderboard.getMaxPointsReason(competitorInLeaderboard, column, race.getEndOfRace()); addNamedElementWithValue(competitorRaceDataElement, "max_points_reason", maxPointsReason.toString()); @@ -419,7 +423,8 @@ public class LeaderboardData extends ExportAction { int raceRank = 0; for (Competitor competitor : allCompetitors) { - Element competitorElement = createCompetitorXML(competitor, leaderboard, /*shortVersion*/ true, null); + Boat boatOfCompetitor = null; // TODO: Where can we get the boat here? + Element competitorElement = createCompetitorXML(competitor, boatOfCompetitor, leaderboard, /*shortVersion*/ true, null); Element competitorRaceDataElement = new Element("competitor_race_data"); MaxPointsReason maxPointsReason = leaderboard.getMaxPointsReason(competitor, column, race.getEndOfRace()); if (maxPointsReason != null && !maxPointsReason.equals(MaxPointsReason.NONE)) { @@ -619,19 +624,21 @@ public class LeaderboardData extends ExportAction { * Creates xml elements related to a competitor. * @param shortVersion when set to true then only the name and UUID is returned */ - private Element createCompetitorXML(Competitor competitor, Leaderboard leaderboard, boolean shortVersion, Util.Pair> competitorConfidenceAndErrorMessages) throws NoWindException, IOException, ServletException { + private Element createCompetitorXML(Competitor competitor, Boat boatOfCompetitor, Leaderboard leaderboard, + boolean shortVersion, Util.Pair> competitorConfidenceAndErrorMessages) throws NoWindException, IOException, ServletException { TimePoint timeSpent = MillisecondsTimePoint.now(); Element competitorElement = new Element("competitor"); addNamedElementWithValue(competitorElement, "uuid", competitor.getId().toString()); addNamedElementWithValue(competitorElement, "name", competitor.getName()); + addNamedElementWithValue(competitorElement, "short_name", competitor.getShortName()); if (shortVersion) return competitorElement; - if (competitor.getBoat() != null) { - addNamedElementWithValue(competitorElement, "sail_id", cleanSailId(competitor.getBoat().getSailID(), competitor)); - addNamedElementWithValue(competitorElement, "boat_class", competitor.getBoat().getBoatClass().getName()); - addNamedElementWithValue(competitorElement, "boat_name", competitor.getBoat().getName()); + if (boatOfCompetitor != null) { + addNamedElementWithValue(competitorElement, "sail_id", cleanSailId(boatOfCompetitor.getSailID(), competitor)); + addNamedElementWithValue(competitorElement, "boat_class", boatOfCompetitor.getBoatClass().getName()); + addNamedElementWithValue(competitorElement, "boat_name", boatOfCompetitor.getName()); } else { addNamedElementWithValue(competitorElement, "sail_id", ""); addNamedElementWithValue(competitorElement, "boat_class", ""); @@ -730,7 +737,8 @@ public class LeaderboardData extends ExportAction { addNamedElementWithValue(legElement, "great_circle_distance_at_end_of_race_in_meters", trackedLeg.getGreatCircleDistance(trackedLeg.getTrackedRace().getEndOfRace()).getMeters()); for (Competitor competitor : trackedLeg.getTrackedRace().getCompetitorsFromBestToWorst(/*timePoint*/ trackedLeg.getTrackedRace().getEndOfRace())) { - Element competitorElement = createCompetitorXML(competitor, leaderboard, /*shortVersion*/ true, null); + Boat boatOfCompetitor = null; // TODO: Where can we get the boat here? + Element competitorElement = createCompetitorXML(competitor, boatOfCompetitor, leaderboard, /*shortVersion*/ true, null); Element competitorLegDataElement = new Element("competitor_leg_data"); TrackedLegOfCompetitor competitorLeg = trackedLeg.getTrackedLeg(competitor); @@ -946,13 +954,13 @@ public class LeaderboardData extends ExportAction { return new Util.Pair>(simpleConfidence, messages); } - private Util.Pair> checkData(Competitor competitor) throws Exception { + private Util.Pair> checkData(Competitor competitor, Boat boatOfCompetitor) throws Exception { double simpleConfidence = 1.0; Vector messages = new Vector(); if (competitor.getName() == null || competitor.getName().equals("")) { messages.add("Competitor " + competitor.getId() + " has no name!"); simpleConfidence -= 1; } - if (competitor.getBoat() == null || competitor.getBoat().getSailID() == null || competitor.getBoat().getSailID().equals("")) { + if (boatOfCompetitor == null || boatOfCompetitor.getSailID() == null || boatOfCompetitor.getSailID().equals("")) { messages.add("Competitor " + competitor.getId() + " has no sail id that can be used!"); simpleConfidence -= 1; } @@ -1029,8 +1037,12 @@ public class LeaderboardData extends ExportAction { final List racesElements = new ArrayList(); final List competitorElements = new ArrayList(); for (Competitor competitor : leaderboard.getAllCompetitors()) { - Util.Pair> competitorConfidenceAndErrorMessages = checkData(competitor); - competitorElements.add(createCompetitorXML(competitor, leaderboard, /*shortVersion*/ false, competitorConfidenceAndErrorMessages)); + Boat boatOfCompetitor = null; + if (competitor.hasBoat()) { + boatOfCompetitor = ((CompetitorWithBoat) competitor).getBoat(); + } + Util.Pair> competitorConfidenceAndErrorMessages = checkData(competitor, boatOfCompetitor); + competitorElements.add(createCompetitorXML(competitor, boatOfCompetitor, leaderboard, /*shortVersion*/ false, competitorConfidenceAndErrorMessages)); } List windData = new ArrayList(); TrackedRace raceBefore = null; int sameDayGroupIndex = 0; int raceCounter = 0; diff --git a/java/com.sap.sailing.xrr.structureimport/src/com/sap/sailing/xrr/structureimport/StructureImporter.java b/java/com.sap.sailing.xrr.structureimport/src/com/sap/sailing/xrr/structureimport/StructureImporter.java index 5c9ab774003..eb36b1d8d13 100755 --- a/java/com.sap.sailing.xrr.structureimport/src/com/sap/sailing/xrr/structureimport/StructureImporter.java +++ b/java/com.sap.sailing.xrr.structureimport/src/com/sap/sailing/xrr/structureimport/StructureImporter.java @@ -100,7 +100,8 @@ public class StructureImporter { final TimePoint endDate = null; // TODO can regatta end time be inferred from XRR document? RegattaImpl regatta = new RegattaImpl(RegattaImpl.getDefaultName(event.getTitle(), ((Division) event .getRaceOrDivisionOrRegattaSeriesResult().get(0)).getTitle()), - baseDomainFactory.getOrCreateBoatClass(result.getA().getBoatClass()), startDate, endDate, getSeries(buildStructure), false, + baseDomainFactory.getOrCreateBoatClass(result.getA().getBoatClass()), + /* canBoatsOfCompetitorsChangePerRace */ true, startDate, endDate, getSeries(buildStructure), false, this.baseDomainFactory.createScoringScheme(ScoringSchemeType.LOW_POINT), event.getEventID(), null, OneDesignRankingMetric::new); addSpecificRegattas.add(regatta); } @@ -201,6 +202,7 @@ public class StructureImporter { Person person = (Person) obj; String idAsString = person.getPersonID(); String name = person.getGivenName() + " " + person.getFamilyName(); + String shortName = null; // Can we get a short name from Manage2Sail? Color color = null; String email = null; URI flagImage = null; @@ -208,7 +210,7 @@ public class StructureImporter { .toString()); BoatAndTeam boatAndTeam = getBoatAndTeam(idAsString, name, nationality, boatClass); this.baseDomainFactory.convertToCompetitorDTO(this.baseDomainFactory.getOrCreateCompetitor( - UUID.fromString(idAsString), name, color, email, flagImage, boatAndTeam.getTeam(), boatAndTeam.getBoat(), + UUID.fromString(idAsString), name, shortName, color, email, flagImage, boatAndTeam.getTeam(), /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null)); } else { break; @@ -271,7 +273,7 @@ public class StructureImporter { } private DynamicBoat createBoat(String name, Boat boat, BoatClass boatClass) { - DynamicBoat boat1 = new BoatImpl(name + " boat", boatClass, boat.getSailNumber()); + DynamicBoat boat1 = new BoatImpl(name + " boat", name + " boat", boatClass, boat.getSailNumber()); return boat1; } diff --git a/java/com.sap.sse.common/src/com/sap/sse/common/util/NaturalComparator.java b/java/com.sap.sse.common/src/com/sap/sse/common/util/NaturalComparator.java index 9dcfe60ac59..2bd6cbc8314 100755 --- a/java/com.sap.sse.common/src/com/sap/sse/common/util/NaturalComparator.java +++ b/java/com.sap.sse.common/src/com/sap/sse/common/util/NaturalComparator.java @@ -17,6 +17,9 @@ public class NaturalComparator implements Comparator { this.caseSensitive = caseSensitive; } + /** + * Creates a case-sensitive natural comparator. + */ public NaturalComparator() { caseSensitive = true; } diff --git a/java/com.sap.sse/src/com/sap/sse/pairinglist/CompetitionFormat.java b/java/com.sap.sse/src/com/sap/sse/pairinglist/CompetitionFormat.java index 6288fa70823..cc0f44c86d7 100644 --- a/java/com.sap.sse/src/com/sap/sse/pairinglist/CompetitionFormat.java +++ b/java/com.sap.sse/src/com/sap/sse/pairinglist/CompetitionFormat.java @@ -21,4 +21,10 @@ public interface CompetitionFormat getCompetitorAllocation(); + + default int getMaxNumberOfCompetitorAllocationsNeeded() { + final int div = getCompetitorsCount() / getGroupsCount(); + final int mod = getCompetitorsCount() % getGroupsCount(); + return div + (mod > 0 ? 1 : 0); // round up + } } diff --git a/java/com.sap.sse/src/com/sap/sse/pairinglist/PairingList.java b/java/com.sap.sse/src/com/sap/sse/pairinglist/PairingList.java index b80642cf680..83f543e1e1e 100644 --- a/java/com.sap.sse/src/com/sap/sse/pairinglist/PairingList.java +++ b/java/com.sap.sse/src/com/sap/sse/pairinglist/PairingList.java @@ -10,7 +10,7 @@ import com.sap.sse.common.Util.Pair; */ public interface PairingList { /** - * Returns pairs of {@link Competitor}s and its {@link CompetitorAllocation} its within a group of a specific flight. + * Returns pairs of {@link Competitor}s and its {@link CompetitorAllocation} within a group of a specific flight. * * @return {@link Pair}s of {@link Competitor}s and {@link CompetitorAllocation} */ diff --git a/java/com.sap.sse/src/com/sap/sse/pairinglist/impl/PairingListImpl.java b/java/com.sap.sse/src/com/sap/sse/pairinglist/impl/PairingListImpl.java index a6f4f8344e7..d0dee5b3809 100644 --- a/java/com.sap.sse/src/com/sap/sse/pairinglist/impl/PairingListImpl.java +++ b/java/com.sap.sse/src/com/sap/sse/pairinglist/impl/PairingListImpl.java @@ -19,6 +19,11 @@ public class PairingListImpl imp * @param standardDev: describes quality of our pList (the lower the standardDev, the better the pairing list) */ public PairingListImpl(PairingListTemplate template, CompetitionFormat competitionFormat) { + final int numberOfCompetitorAllocations = Util.size(competitionFormat.getCompetitorAllocation()); + if (numberOfCompetitorAllocations < competitionFormat.getMaxNumberOfCompetitorAllocationsNeeded()) { + throw new IllegalArgumentException("Too few competitor allocations ("+numberOfCompetitorAllocations+ + "). "+competitionFormat.getMaxNumberOfCompetitorAllocationsNeeded()+" are needed."); + } this.pairingListTemplate = template; this.competitionFormat = competitionFormat; this.competitors = Util.asList(competitionFormat.getCompetitors()); @@ -38,8 +43,9 @@ public class PairingListImpl imp final List> result = new ArrayList<>(); for (int slot = 0; slot < competitorIndicesInRace.size(); slot++) { - if (competitorIndicesInRace.get(slot) >= 0) { - result.add(new Pair(competitors.get(competitorIndicesInRace.get(slot)), + final Integer index = competitorIndicesInRace.get(slot); + if (index >= 0) { + result.add(new Pair(competitors.get(index), Util.get(competitionFormat.getCompetitorAllocation(), slot))); } else { result.add(new Pair(null, diff --git a/mobile/com.sap.sailing.android.shared/src/com/sap/sailing/android/shared/application/LoggableApplication.java b/mobile/com.sap.sailing.android.shared/src/com/sap/sailing/android/shared/application/LoggableApplication.java index dba8a4d7a88..e0e36be7b05 100644 --- a/mobile/com.sap.sailing.android.shared/src/com/sap/sailing/android/shared/application/LoggableApplication.java +++ b/mobile/com.sap.sailing.android.shared/src/com/sap/sailing/android/shared/application/LoggableApplication.java @@ -2,16 +2,16 @@ package com.sap.sailing.android.shared.application; import java.lang.ref.WeakReference; -import android.app.Application; -import android.content.Context; -import android.content.Intent; - import com.sap.sailing.android.shared.R; import com.sap.sailing.android.shared.logging.ExLog; import com.sap.sailing.android.shared.logging.LifecycleLogger; import com.sap.sailing.android.shared.logging.LoggingExceptionHandler; import com.sap.sailing.android.shared.util.PrefUtils; +import android.app.Application; +import android.content.Context; +import android.content.Intent; + /** *

    Registers an additional exception handler for uncaught exception to have some crash logging.

    *

    Offers a static {@link StringContext} to handle i18n in ugly cases.

    @@ -19,7 +19,7 @@ import com.sap.sailing.android.shared.util.PrefUtils; */ public class LoggableApplication extends Application { - private final static String TAG = LoggableApplication.class.getName(); + private final static String TAG = LoggableApplication.class.getSimpleName(); private static StringContext stringContext; diff --git a/mobile/com.sap.sailing.android.shared/src/com/sap/sailing/android/shared/data/CheckinUrlInfo.java b/mobile/com.sap.sailing.android.shared/src/com/sap/sailing/android/shared/data/CheckinUrlInfo.java index 23981b6b5f9..5703282e8ef 100644 --- a/mobile/com.sap.sailing.android.shared/src/com/sap/sailing/android/shared/data/CheckinUrlInfo.java +++ b/mobile/com.sap.sailing.android.shared/src/com/sap/sailing/android/shared/data/CheckinUrlInfo.java @@ -4,6 +4,7 @@ public class CheckinUrlInfo { public static int TYPE_COMPETITOR = 0; public static int TYPE_MARK = 1; + public static int TYPE_BOAT = 2; public String urlString; public String checkinDigest; diff --git a/mobile/com.sap.sailing.android.shared/src/com/sap/sailing/android/shared/services/sending/MessagePersistenceManager.java b/mobile/com.sap.sailing.android.shared/src/com/sap/sailing/android/shared/services/sending/MessagePersistenceManager.java index 28a3b4ed777..9427b708c1e 100644 --- a/mobile/com.sap.sailing.android.shared/src/com/sap/sailing/android/shared/services/sending/MessagePersistenceManager.java +++ b/mobile/com.sap.sailing.android.shared/src/com/sap/sailing/android/shared/services/sending/MessagePersistenceManager.java @@ -22,7 +22,7 @@ import android.os.Bundle; public class MessagePersistenceManager { - private final static String TAG = MessagePersistenceManager.class.getName(); + private final static String TAG = MessagePersistenceManager.class.getSimpleName(); private final static String delayedMessagesFileName = "delayedMessages.txt"; diff --git a/mobile/com.sap.sailing.android.shared/src/com/sap/sailing/android/shared/services/sending/MessageSendingService.java b/mobile/com.sap.sailing.android.shared/src/com/sap/sailing/android/shared/services/sending/MessageSendingService.java index 824c143f0e3..8fa4b0c4b1d 100644 --- a/mobile/com.sap.sailing.android.shared/src/com/sap/sailing/android/shared/services/sending/MessageSendingService.java +++ b/mobile/com.sap.sailing.android.shared/src/com/sap/sailing/android/shared/services/sending/MessageSendingService.java @@ -68,7 +68,7 @@ public class MessageSendingService extends Service implements MessageSendingList public static final String charsetName = "UTF-8"; - protected final static String TAG = MessageSendingService.class.getName(); + protected final static String TAG = MessageSendingService.class.getSimpleName(); private ConnectivityManager connectivityManager; private Handler handler; diff --git a/mobile/com.sap.sailing.android.shared/src/com/sap/sailing/android/shared/ui/activities/AbstractBaseActivity.java b/mobile/com.sap.sailing.android.shared/src/com/sap/sailing/android/shared/ui/activities/AbstractBaseActivity.java index 4ecc6ae7ff8..36377e248e4 100644 --- a/mobile/com.sap.sailing.android.shared/src/com/sap/sailing/android/shared/ui/activities/AbstractBaseActivity.java +++ b/mobile/com.sap.sailing.android.shared/src/com/sap/sailing/android/shared/ui/activities/AbstractBaseActivity.java @@ -1,5 +1,7 @@ package com.sap.sailing.android.shared.ui.activities; +import com.sap.sailing.android.shared.logging.ExLog; + import android.app.ProgressDialog; import android.content.DialogInterface; import android.support.v4.app.Fragment; @@ -7,11 +9,9 @@ import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentTransaction; import android.support.v7.app.AlertDialog; -import com.sap.sailing.android.shared.logging.ExLog; - public abstract class AbstractBaseActivity extends SendingServiceAwareActivity { - private static final String TAG = AbstractBaseActivity.class.getName(); + private static final String TAG = AbstractBaseActivity.class.getSimpleName(); /** * An object used to synchronize access to the {@link #progressDialog} field to avoid concurrency diff --git a/mobile/com.sap.sailing.android.shared/src/com/sap/sailing/android/shared/util/ViewHelper.java b/mobile/com.sap.sailing.android.shared/src/com/sap/sailing/android/shared/util/ViewHelper.java index 3f1d4cd8d2d..1a019dbdd98 100644 --- a/mobile/com.sap.sailing.android.shared/src/com/sap/sailing/android/shared/util/ViewHelper.java +++ b/mobile/com.sap.sailing.android.shared/src/com/sap/sailing/android/shared/util/ViewHelper.java @@ -1,8 +1,14 @@ package com.sap.sailing.android.shared.util; +import com.sap.sse.common.impl.HSVColor; + +import android.graphics.Color; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import android.util.SparseArray; import android.view.View; import android.view.ViewGroup; +import android.widget.TextView; public class ViewHelper { @@ -41,4 +47,19 @@ public class ViewHelper { } } } + + /** + * Set the background to the color and tint the text color to be readable + * + * @param view TextView to colorize + * @param backgroundColor background color + */ + public static void setColors(@NonNull TextView view, @Nullable String backgroundColor) { + com.sap.sse.common.Color color = HSVColor.getCssColor(backgroundColor); + if (color != null) { + float textColor = (1 - color.getAsHSV().getC()) * 255f; + view.setTextColor(Color.argb(255, (int) textColor, (int) textColor, (int) textColor)); + view.setBackgroundColor(Color.parseColor(backgroundColor)); + } + } } diff --git a/mobile/com.sap.sailing.android.tracking.app/res/layout/fragment_buoy.xml b/mobile/com.sap.sailing.android.tracking.app/res/layout/fragment_buoy.xml index 05887b6d3f9..ae0f3953ef4 100644 --- a/mobile/com.sap.sailing.android.tracking.app/res/layout/fragment_buoy.xml +++ b/mobile/com.sap.sailing.android.tracking.app/res/layout/fragment_buoy.xml @@ -1,13 +1,17 @@ + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@drawable/fiori_main_background"> diff --git a/mobile/com.sap.sailing.android.tracking.app/res/layout/ragatta_listview_row.xml b/mobile/com.sap.sailing.android.tracking.app/res/layout/ragatta_listview_row.xml index 2b8b357fcb0..c554279c225 100644 --- a/mobile/com.sap.sailing.android.tracking.app/res/layout/ragatta_listview_row.xml +++ b/mobile/com.sap.sailing.android.tracking.app/res/layout/ragatta_listview_row.xml @@ -28,7 +28,8 @@ android:singleLine="false" android:textColor="@color/fiori_text_color" android:textSize="18sp" - android:textStyle="bold"/> + android:textStyle="bold" + tools:text="Event" /> + android:textSize="18sp" + tools:text="Regatta" /> - + android:layout_gravity="center_vertical" + android:layout_weight="0.3"> + + + + \ No newline at end of file diff --git a/mobile/com.sap.sailing.android.tracking.app/res/values/api_endpoints.xml b/mobile/com.sap.sailing.android.tracking.app/res/values/api_endpoints.xml index f00f1a97148..817e614cf35 100644 --- a/mobile/com.sap.sailing.android.tracking.app/res/values/api_endpoints.xml +++ b/mobile/com.sap.sailing.android.tracking.app/res/values/api_endpoints.xml @@ -8,5 +8,6 @@ /sailingserver/api/v1/competitors/{competitor_id} /sailingserver/api/v1/competitors/{competitor_id}/team /sailingserver/api/v1/competitors/{competitor_id}/team/image - /sailingserver/api/v1/leaderboards/{leaderboardName}/marks/{markId} + /sailingserver/api/v1/leaderboards/{leaderboardName}/marks/{mark_id} + /sailingserver/api/v1/boats/{boat_id} diff --git a/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/adapter/RegattaAdapter.java b/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/adapter/RegattaAdapter.java index f3257d48ccf..6755ecdb5b3 100644 --- a/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/adapter/RegattaAdapter.java +++ b/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/adapter/RegattaAdapter.java @@ -1,16 +1,16 @@ package com.sap.sailing.android.tracking.app.adapter; -import android.content.Context; -import android.database.Cursor; -import android.view.View; -import android.widget.TextView; - import com.sap.sailing.android.shared.data.CheckinUrlInfo; import com.sap.sailing.android.shared.ui.adapters.AbstractRegattaAdapter; import com.sap.sailing.android.shared.util.ViewHelper; import com.sap.sailing.android.tracking.app.R; import com.sap.sailing.android.tracking.app.provider.AnalyticsContract; +import android.content.Context; +import android.database.Cursor; +import android.view.View; +import android.widget.TextView; + public class RegattaAdapter extends AbstractRegattaAdapter { public RegattaAdapter(Context context, int layout, Cursor cursor, int flags) { @@ -21,8 +21,8 @@ public class RegattaAdapter extends AbstractRegattaAdapter { public void bindView(View view, Context context, Cursor cursor) { TextView regattaName = ViewHelper.get(view,R.id.regattaName); if (regattaName != null) { - String regatteNameText = cursor.getString(cursor.getColumnIndex(AnalyticsContract.Leaderboard.LEADERBOARD_DISPLAY_NAME)); - regattaName.setText(regatteNameText); + String regattaNameText = cursor.getString(cursor.getColumnIndex(AnalyticsContract.Leaderboard.LEADERBOARD_DISPLAY_NAME)); + regattaName.setText(regattaNameText); } TextView regattaEvent = ViewHelper.get(view, R.id.regatta_event); if (regattaEvent != null) { @@ -38,6 +38,9 @@ public class RegattaAdapter extends AbstractRegattaAdapter { detailText += cursor.getString(cursor.getColumnIndex(AnalyticsContract.Competitor.COMPETITOR_DISPLAY_NAME)); } else if (type == CheckinUrlInfo.TYPE_MARK) { detailText += cursor.getString(cursor.getColumnIndex(AnalyticsContract.Mark.MARK_NAME)); + } else if (type == CheckinUrlInfo.TYPE_BOAT) { + detailText += cursor.getString(cursor.getColumnIndex(AnalyticsContract.Boat.BOAT_NAME)); + ViewHelper.setColors(detail, cursor.getString(cursor.getColumnIndex(AnalyticsContract.Boat.BOAT_COLOR))); } detail.setText(detailText); } diff --git a/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/provider/AnalyticsContract.java b/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/provider/AnalyticsContract.java index 85caf5e6667..5b48835a9c5 100644 --- a/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/provider/AnalyticsContract.java +++ b/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/provider/AnalyticsContract.java @@ -1,13 +1,15 @@ package com.sap.sailing.android.tracking.app.provider; +import com.sap.sailing.android.tracking.app.provider.AnalyticsDatabase.Tables; + import android.content.ContentResolver; import android.net.Uri; import android.provider.BaseColumns; -import com.sap.sailing.android.tracking.app.provider.AnalyticsDatabase.Tables; - public class AnalyticsContract { + final static String VND_STRING = "/vnd.sap.sailing_analytics"; + interface CompetitorColumns { String COMPETITOR_ID = "competitor_id"; String COMPETITOR_DISPLAY_NAME = "competitor_display_name"; @@ -33,12 +35,19 @@ public class AnalyticsContract { String EVENT_CHECKIN_DIGEST = "event_checkin_digest"; } - interface markColums { + interface MarkColumns { String MARK_ID = "mark_id"; String MARK_NAME = "mark_name"; String MARK_CHECKIN_DIGEST = "mark_checkin_digest"; } + interface BoatColumns { + String BOAT_ID = "boat_id"; + String BOAT_NAME = "boat_name"; + String BOAT_CHECKIN_DIGEST = "boat_checkin_digest"; + String BOAT_COLOR = "boat_color"; + } + interface CheckinColumns { String CHECKIN_URI_VALUE = "uri_value"; String CHECKIN_URI_CHECKIN_DIGEST = "uri_checkin_digest"; @@ -47,11 +56,11 @@ public class AnalyticsContract { public static final String CONTENT_AUTHORITY = "com.sap.sailing.android.tracking.app.provider.db"; - public static final Uri BASE_CONTENT_URI = Uri.parse("content://" + CONTENT_AUTHORITY); + static final Uri BASE_CONTENT_URI = Uri.parse("content://" + CONTENT_AUTHORITY); - public static class LeaderboardsEventsCompetitorsMarksJoined { - public final static Uri CONTENT_URI = BASE_CONTENT_URI.buildUpon().appendPath("leaderboards_events_competitors_marks_joined") - .build(); + public static class LeaderboardsEventsCompetitorsMarksBoatsJoined { + public final static Uri CONTENT_URI = BASE_CONTENT_URI.buildUpon() + .appendPath(AnalyticsProvider.ALL_JOINED).build(); } public static class EventLeaderboardCompetitorJoined { @@ -67,45 +76,43 @@ public class AnalyticsContract { public static class Competitor implements CompetitorColumns, BaseColumns { public static final Uri CONTENT_URI = BASE_CONTENT_URI.buildUpon().appendPath(Tables.COMPETITORS).build(); - public static final String CONTENT_TYPE = ContentResolver.CURSOR_DIR_BASE_TYPE - + "/vnd.sap_sailing_analytics.competitor"; - public static final String CONTENT_ITEM_TYPE = ContentResolver.CURSOR_ITEM_BASE_TYPE - + "/vnd.sap_sailing_analytics.competitor"; + static final String CONTENT_TYPE = ContentResolver.CURSOR_DIR_BASE_TYPE + VND_STRING + ".competitor"; + static final String CONTENT_ITEM_TYPE = ContentResolver.CURSOR_ITEM_BASE_TYPE + VND_STRING + ".competitor"; } public static class Event implements EventColumns, BaseColumns { public static final Uri CONTENT_URI = BASE_CONTENT_URI.buildUpon().appendPath(Tables.EVENTS).build(); - public static final String CONTENT_TYPE = ContentResolver.CURSOR_DIR_BASE_TYPE - + "/vnd.sap_sailing_analytics.event"; - public static final String CONTENT_ITEM_TYPE = ContentResolver.CURSOR_ITEM_BASE_TYPE - + "/vnd.sap_sailing_analytics.event"; + static final String CONTENT_TYPE = ContentResolver.CURSOR_DIR_BASE_TYPE + VND_STRING + ".event"; + static final String CONTENT_ITEM_TYPE = ContentResolver.CURSOR_ITEM_BASE_TYPE + VND_STRING + ".event"; } public static class Leaderboard implements LeaderboardColumns { public static final Uri CONTENT_URI = BASE_CONTENT_URI.buildUpon().appendPath(Tables.LEADERBOARDS).build(); - public static final String CONTENT_TYPE = ContentResolver.CURSOR_DIR_BASE_TYPE - + "/vnd.sap_sailing_analytics.leaderboard"; - public static final String CONTENT_ITEM_TYPE = ContentResolver.CURSOR_ITEM_BASE_TYPE - + "/vnd.sap_sailing_analytics.leaderboard"; + static final String CONTENT_TYPE = ContentResolver.CURSOR_DIR_BASE_TYPE + VND_STRING + ".leaderboard"; + static final String CONTENT_ITEM_TYPE = ContentResolver.CURSOR_ITEM_BASE_TYPE + VND_STRING + ".leaderboard"; } public static class Checkin implements CheckinColumns, BaseColumns { public static final Uri CONTENT_URI = BASE_CONTENT_URI.buildUpon().appendPath(Tables.CHECKIN_URIS).build(); - public static final String CONTENT_TYPE = ContentResolver.CURSOR_DIR_BASE_TYPE - + "/vnd.sap_sailing_analytics.uri"; - public static final String CONTENT_ITEM_TYPE = ContentResolver.CURSOR_ITEM_BASE_TYPE - + "/vnd.sap_sailing_analytics.uri"; + static final String CONTENT_TYPE = ContentResolver.CURSOR_DIR_BASE_TYPE + VND_STRING + ".uri"; + static final String CONTENT_ITEM_TYPE = ContentResolver.CURSOR_ITEM_BASE_TYPE + VND_STRING + ".uri"; } - public static class Mark implements markColums, BaseColumns { + public static class Mark implements MarkColumns, BaseColumns { public static final Uri CONTENT_URI = BASE_CONTENT_URI.buildUpon().appendPath(Tables.MARKS).build(); - public static final String CONTENT_TYPE = ContentResolver.CURSOR_DIR_BASE_TYPE - + "/vnd.sap_sailing_analytics.Mark"; - public static final String CONTENT_ITEM_TYPE = ContentResolver.CURSOR_ITEM_BASE_TYPE - + "/vnd.sap_sailing_analytics.Mark"; + + static final String CONTENT_TYPE = ContentResolver.CURSOR_DIR_BASE_TYPE + VND_STRING + ".mark"; + static final String CONTENT_ITEM_TYPE = ContentResolver.CURSOR_ITEM_BASE_TYPE + VND_STRING + ".mark"; + } + + public static class Boat implements BoatColumns, BaseColumns { + public static final Uri CONTENT_URI = BASE_CONTENT_URI.buildUpon().appendPath(Tables.BOATS).build(); + + static final String CONTENT_TYPE = ContentResolver.CURSOR_DIR_BASE_TYPE + VND_STRING + ".boat"; + static final String CONTENT_ITEM_TYPE = ContentResolver.CURSOR_ITEM_BASE_TYPE + VND_STRING + ".boat"; } private AnalyticsContract() { diff --git a/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/provider/AnalyticsDatabase.java b/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/provider/AnalyticsDatabase.java index e26f6c0ed2a..752525fbfc7 100644 --- a/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/provider/AnalyticsDatabase.java +++ b/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/provider/AnalyticsDatabase.java @@ -1,19 +1,19 @@ package com.sap.sailing.android.tracking.app.provider; -import android.content.Context; -import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteOpenHelper; -import android.provider.BaseColumns; - import com.sap.sailing.android.shared.logging.ExLog; import com.sap.sailing.android.tracking.app.provider.AnalyticsContract.Checkin; +import com.sap.sailing.android.tracking.app.provider.AnalyticsContract.CheckinColumns; import com.sap.sailing.android.tracking.app.provider.AnalyticsContract.Competitor; import com.sap.sailing.android.tracking.app.provider.AnalyticsContract.CompetitorColumns; import com.sap.sailing.android.tracking.app.provider.AnalyticsContract.Event; import com.sap.sailing.android.tracking.app.provider.AnalyticsContract.EventColumns; import com.sap.sailing.android.tracking.app.provider.AnalyticsContract.Leaderboard; import com.sap.sailing.android.tracking.app.provider.AnalyticsContract.LeaderboardColumns; -import com.sap.sailing.android.tracking.app.provider.AnalyticsContract.CheckinColumns; + +import android.content.Context; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteOpenHelper; +import android.provider.BaseColumns; public class AnalyticsDatabase extends SQLiteOpenHelper { @@ -21,12 +21,20 @@ public class AnalyticsDatabase extends SQLiteOpenHelper { private static final String DATABASE_NAME = "sap_sailing_analytics.db"; - private static final int DATABASE_VERSION = 3; + private static final int DATABASE_VERSION = 4; - private static final String createMarkTable = "CREATE TABLE " + Tables.MARKS + " (" + BaseColumns._ID - + " INTEGER PRIMARY KEY AUTOINCREMENT, " + AnalyticsContract.Mark.MARK_ID + " TEXT, " + private static final String createMarkTable = "CREATE TABLE " + Tables.MARKS + " (" + + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + + AnalyticsContract.Mark.MARK_ID + " TEXT, " + AnalyticsContract.Mark.MARK_NAME + " TEXT, " - + AnalyticsContract.Mark.MARK_CHECKIN_DIGEST + " TEXT );" ; + + AnalyticsContract.Mark.MARK_CHECKIN_DIGEST + " TEXT);" ; + + private static final String createBoatTable = "CREATE TABLE " + Tables.BOATS + " (" + + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + + AnalyticsContract.Boat.BOAT_ID + " TEXT, " + + AnalyticsContract.Boat.BOAT_NAME + " TEXT, " + + AnalyticsContract.Boat.BOAT_CHECKIN_DIGEST + " TEXT," + + AnalyticsContract.Boat.BOAT_COLOR + " TEXT );" ; private final Context mContext; @@ -36,6 +44,7 @@ public class AnalyticsDatabase extends SQLiteOpenHelper { String LEADERBOARDS = "leaderboards"; String CHECKIN_URIS = "checkin_uris"; String MARKS = "marks"; + String BOATS = "boats"; String EVENTS_JOIN_LEADERBOARDS_JOIN_MARKS = Tables.LEADERBOARDS + " INNER JOIN " + Tables.EVENTS + " ON (" + Tables.LEADERBOARDS + "." + Leaderboard.LEADERBOARD_CHECKIN_DIGEST + " = " + Tables.EVENTS @@ -49,7 +58,7 @@ public class AnalyticsDatabase extends SQLiteOpenHelper { + Tables.LEADERBOARDS + "." + Leaderboard.LEADERBOARD_CHECKIN_DIGEST + " = " + Tables.COMPETITORS + "." + Competitor.COMPETITOR_CHECKIN_DIGEST + ") "; - String LEADERBOARDS_JOIN_EVENTS_JOIN_COMPETITORS_JOIN_MARKS = Tables.LEADERBOARDS + " INNER JOIN " + Tables.EVENTS + String LEADERBOARDS_JOIN_EVENTS_JOIN_COMPETITORS_JOIN_MARKS_JOINS_BOATS = Tables.LEADERBOARDS + " INNER JOIN " + Tables.EVENTS + " ON (" + Tables.LEADERBOARDS + "." + Leaderboard.LEADERBOARD_CHECKIN_DIGEST + " = " + Tables.EVENTS + "." + Event.EVENT_CHECKIN_DIGEST + ") " + " INNER JOIN " + Tables.CHECKIN_URIS + " ON (" + Tables.LEADERBOARDS + "." + Leaderboard.LEADERBOARD_CHECKIN_DIGEST + " = " + Tables.CHECKIN_URIS + "." @@ -59,7 +68,10 @@ public class AnalyticsDatabase extends SQLiteOpenHelper { + Competitor.COMPETITOR_CHECKIN_DIGEST + ") " + " LEFT OUTER JOIN " + Tables.MARKS + " ON (" + Tables.LEADERBOARDS + "." + Leaderboard.LEADERBOARD_CHECKIN_DIGEST + " = " + Tables.MARKS + "." - + AnalyticsContract.Mark.MARK_CHECKIN_DIGEST + ") "; + + AnalyticsContract.Mark.MARK_CHECKIN_DIGEST + ") " + + " LEFT OUTER JOIN " + Tables.BOATS + " ON (" + + Tables.LEADERBOARDS + "." + Leaderboard.LEADERBOARD_CHECKIN_DIGEST + " = " + Tables.BOATS + "." + + AnalyticsContract.Boat.BOAT_CHECKIN_DIGEST + ") "; } public AnalyticsDatabase(Context context) { @@ -69,46 +81,68 @@ public class AnalyticsDatabase extends SQLiteOpenHelper { @Override public void onCreate(SQLiteDatabase db) { - db.execSQL("CREATE TABLE " + Tables.LEADERBOARDS + " (" + BaseColumns._ID - + " INTEGER PRIMARY KEY AUTOINCREMENT, " + LeaderboardColumns.LEADERBOARD_CHECKIN_DIGEST + " TEXT, " + db.beginTransaction(); + try { + db.execSQL("CREATE TABLE " + Tables.LEADERBOARDS + " (" + + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + + LeaderboardColumns.LEADERBOARD_CHECKIN_DIGEST + " TEXT, " + LeaderboardColumns.LEADERBOARD_NAME + " TEXT, " + LeaderboardColumns.LEADERBOARD_DISPLAY_NAME + " TEXT);"); - db.execSQL("CREATE TABLE " + Tables.CHECKIN_URIS + " (" + BaseColumns._ID - + " INTEGER PRIMARY KEY AUTOINCREMENT, " + CheckinColumns.CHECKIN_URI_CHECKIN_DIGEST + " TEXT, " + db.execSQL("CREATE TABLE " + Tables.CHECKIN_URIS + " (" + + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + + CheckinColumns.CHECKIN_URI_CHECKIN_DIGEST + " TEXT, " + CheckinColumns.CHECKIN_URI_VALUE + " TEXT, " + CheckinColumns.CHECKIN_TYPE + " INTEGER );"); - db.execSQL("CREATE TABLE " + Tables.COMPETITORS + " (" + BaseColumns._ID - + " INTEGER PRIMARY KEY AUTOINCREMENT, " + CompetitorColumns.COMPETITOR_ID + " TEXT, " - + CompetitorColumns.COMPETITOR_DISPLAY_NAME + " TEXT, " + CompetitorColumns.COMPETITOR_COUNTRY_CODE - + " TEXT, " + CompetitorColumns.COMPETITOR_NATIONALITY + " TEXT, " - + CompetitorColumns.COMPETITOR_SAIL_ID + " TEXT, " + CompetitorColumns.COMPETITOR_CHECKIN_DIGEST - + " TEXT )"); + db.execSQL("CREATE TABLE " + Tables.COMPETITORS + " (" + + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + + CompetitorColumns.COMPETITOR_ID + " TEXT, " + + CompetitorColumns.COMPETITOR_DISPLAY_NAME + " TEXT, " + + CompetitorColumns.COMPETITOR_COUNTRY_CODE + " TEXT, " + + CompetitorColumns.COMPETITOR_NATIONALITY + " TEXT, " + + CompetitorColumns.COMPETITOR_SAIL_ID + " TEXT, " + + CompetitorColumns.COMPETITOR_CHECKIN_DIGEST + " TEXT )"); - db.execSQL("CREATE TABLE " + Tables.EVENTS + " ( " + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " - + EventColumns.EVENT_ID + " TEXT, " + EventColumns.EVENT_NAME + " TEXTL, " - + EventColumns.EVENT_DATE_START + " INTEGER, " + EventColumns.EVENT_DATE_END + " INTEGER, " - + EventColumns.EVENT_SERVER + " TEXT, " + EventColumns.EVENT_IMAGE_URL + " TEXT, " + db.execSQL("CREATE TABLE " + Tables.EVENTS + " ( " + + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + + EventColumns.EVENT_ID + " TEXT, " + + EventColumns.EVENT_NAME + " TEXTL, " + + EventColumns.EVENT_DATE_START + " INTEGER, " + + EventColumns.EVENT_DATE_END + " INTEGER, " + + EventColumns.EVENT_SERVER + " TEXT, " + + EventColumns.EVENT_IMAGE_URL + " TEXT, " + EventColumns.EVENT_CHECKIN_DIGEST + " TEXT )"); - db.execSQL(createMarkTable); + db.execSQL(createMarkTable); + db.execSQL(createBoatTable); + + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { ExLog.i(mContext, TAG, "onUpgrade() from " + oldVersion + " to " + newVersion); - if (oldVersion < 2) { - db.execSQL(createMarkTable); - db.execSQL("ALTER TABLE " + Tables.CHECKIN_URIS + " ADD COLUMN " + Checkin.CHECKIN_TYPE + " INTEGER DEFAULT 0"); - } - if (oldVersion < 3) { - db.execSQL("ALTER TABLE " + Tables.LEADERBOARDS + " ADD COLUMN " + Leaderboard.LEADERBOARD_DISPLAY_NAME + " TEXT"); - db.execSQL("UPDATE " + Tables.LEADERBOARDS + " SET " + Leaderboard.LEADERBOARD_DISPLAY_NAME + " = " + Leaderboard.LEADERBOARD_NAME); + db.beginTransaction(); + try { + if (oldVersion < 2) { + db.execSQL(createMarkTable); + db.execSQL("ALTER TABLE " + Tables.CHECKIN_URIS + " ADD COLUMN " + Checkin.CHECKIN_TYPE + " INTEGER DEFAULT 0"); + } + if (oldVersion < 3) { + db.execSQL("ALTER TABLE " + Tables.LEADERBOARDS + " ADD COLUMN " + Leaderboard.LEADERBOARD_DISPLAY_NAME + " TEXT"); + db.execSQL("UPDATE " + Tables.LEADERBOARDS + " SET " + Leaderboard.LEADERBOARD_DISPLAY_NAME + " = " + Leaderboard.LEADERBOARD_NAME); + } + if (oldVersion < 4) { + db.execSQL(createBoatTable); + } + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); } } - public static void deleteDatabase(Context context) { - context.deleteDatabase(DATABASE_NAME); - } } diff --git a/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/provider/AnalyticsProvider.java b/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/provider/AnalyticsProvider.java index 337730764a7..e1046de9df2 100644 --- a/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/provider/AnalyticsProvider.java +++ b/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/provider/AnalyticsProvider.java @@ -2,6 +2,17 @@ package com.sap.sailing.android.tracking.app.provider; import java.util.Arrays; +import com.sap.sailing.android.shared.logging.ExLog; +import com.sap.sailing.android.shared.util.SelectionBuilder; +import com.sap.sailing.android.tracking.app.BuildConfig; +import com.sap.sailing.android.tracking.app.provider.AnalyticsContract.Boat; +import com.sap.sailing.android.tracking.app.provider.AnalyticsContract.Checkin; +import com.sap.sailing.android.tracking.app.provider.AnalyticsContract.Competitor; +import com.sap.sailing.android.tracking.app.provider.AnalyticsContract.Event; +import com.sap.sailing.android.tracking.app.provider.AnalyticsContract.Mark; +import com.sap.sailing.android.tracking.app.provider.AnalyticsContract.Leaderboard; +import com.sap.sailing.android.tracking.app.provider.AnalyticsDatabase.Tables; + import android.content.ContentProvider; import android.content.ContentResolver; import android.content.ContentValues; @@ -12,19 +23,13 @@ import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteQueryBuilder; import android.net.Uri; import android.provider.BaseColumns; - -import com.sap.sailing.android.shared.logging.ExLog; -import com.sap.sailing.android.shared.util.SelectionBuilder; -import com.sap.sailing.android.tracking.app.BuildConfig; -import com.sap.sailing.android.tracking.app.provider.AnalyticsContract.Checkin; -import com.sap.sailing.android.tracking.app.provider.AnalyticsContract.Competitor; -import com.sap.sailing.android.tracking.app.provider.AnalyticsContract.Event; -import com.sap.sailing.android.tracking.app.provider.AnalyticsContract.Leaderboard; -import com.sap.sailing.android.tracking.app.provider.AnalyticsDatabase.Tables; +import android.support.annotation.NonNull; public class AnalyticsProvider extends ContentProvider { - private static final String TAG = AnalyticsProvider.class.getName(); + private static final String TAG = AnalyticsProvider.class.getSimpleName(); + + public static final String ALL_JOINED = "leaderboards_events_competitors_marks_joined"; private AnalyticsDatabase mOpenHelper; @@ -47,11 +52,14 @@ public class AnalyticsProvider extends ContentProvider { private static final int EVENT_GPS_FIXES_JOINED = 700; - private static final int LEADERBOARDS_EVENTS_COMPETITORS_MARKS_JOINED = 800; + private static final int LEADERBOARDS_EVENTS_COMPETITORS_MARKS_BOATS_JOINED = 800; private static final int MARK = 900; private static final int MARK_ID = 901; + private static final int BOAT = 1000; + private static final int BOAT_ID = 1001; + private static UriMatcher buildUriMatcher() { final UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH); final String authority = AnalyticsContract.CONTENT_AUTHORITY; @@ -73,11 +81,14 @@ public class AnalyticsProvider extends ContentProvider { matcher.addURI(authority, "event_gps_fix_joined", EVENT_GPS_FIXES_JOINED); - matcher.addURI(authority, "leaderboards_events_competitors_marks_joined", LEADERBOARDS_EVENTS_COMPETITORS_MARKS_JOINED); + matcher.addURI(authority, "leaderboards_events_competitors_marks_joined", LEADERBOARDS_EVENTS_COMPETITORS_MARKS_BOATS_JOINED); matcher.addURI(authority, Tables.MARKS, MARK); matcher.addURI(authority, Tables.MARKS + "/*", MARK_ID); + matcher.addURI(authority, Tables.BOATS, BOAT); + matcher.addURI(authority, Tables.BOATS + "/*", BOAT_ID); + return matcher; } @@ -88,7 +99,7 @@ public class AnalyticsProvider extends ContentProvider { } @Override - public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { + public Cursor query(@NonNull Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { if (BuildConfig.DEBUG) { ExLog.i(getContext(), TAG, "query() called with: uri = [" + uri + "], projection = [" + Arrays.toString(projection) + "], selection = [" @@ -108,8 +119,8 @@ public class AnalyticsProvider extends ContentProvider { table = Tables.EVENTS_JOIN_LEADERBOARDS_JOIN_COMPETITORS; break; - case LEADERBOARDS_EVENTS_COMPETITORS_MARKS_JOINED: - table = Tables.LEADERBOARDS_JOIN_EVENTS_JOIN_COMPETITORS_JOIN_MARKS; + case LEADERBOARDS_EVENTS_COMPETITORS_MARKS_BOATS_JOINED: + table = Tables.LEADERBOARDS_JOIN_EVENTS_JOIN_COMPETITORS_JOIN_MARKS_JOINS_BOATS; break; default: @@ -132,7 +143,7 @@ public class AnalyticsProvider extends ContentProvider { } @Override - public String getType(Uri uri) { + public String getType(@NonNull Uri uri) { switch (sUriMatcher.match(uri)) { case COMPETITOR: return Competitor.CONTENT_TYPE; @@ -159,10 +170,16 @@ public class AnalyticsProvider extends ContentProvider { return Checkin.CONTENT_ITEM_TYPE; case MARK: - return AnalyticsContract.Mark.CONTENT_TYPE; + return Mark.CONTENT_TYPE; case MARK_ID: - return AnalyticsContract.Mark.CONTENT_ITEM_TYPE; + return Mark.CONTENT_ITEM_TYPE; + + case BOAT: + return Boat.CONTENT_TYPE; + + case BOAT_ID: + return Boat.CONTENT_ITEM_TYPE; default: throw new UnsupportedOperationException("Unknown uri: " + uri); @@ -170,7 +187,7 @@ public class AnalyticsProvider extends ContentProvider { } @Override - public Uri insert(Uri uri, ContentValues values) { + public Uri insert(@NonNull Uri uri, ContentValues values) { if (BuildConfig.DEBUG) { String message = "insert: uri=" + uri + " values=[" + values.toString() + "]"; ExLog.i(getContext(), TAG, message); @@ -199,18 +216,22 @@ public class AnalyticsProvider extends ContentProvider { table = Tables.MARKS; break; + case BOAT: + table = Tables.BOATS; + break; + default: throw new UnsupportedOperationException("Unknown uri: " + uri); } long id = db.insertOrThrow(table, null, values); - notifyChange(AnalyticsContract.LeaderboardsEventsCompetitorsMarksJoined.CONTENT_URI); + notifyChange(AnalyticsContract.LeaderboardsEventsCompetitorsMarksBoatsJoined.CONTENT_URI); notifyChange(uri); return uri.buildUpon().appendPath(String.valueOf(id)).build(); } @Override - public int delete(Uri uri, String selection, String[] selectionArgs) { + public int delete(@NonNull Uri uri, String selection, String[] selectionArgs) { if (BuildConfig.DEBUG) { ExLog.i(getContext(), TAG, "delete() called with: uri = [" + uri + "], selection = [" + selection + "], selectionArgs = [" + Arrays.toString(selectionArgs) + "]"); @@ -240,18 +261,22 @@ public class AnalyticsProvider extends ContentProvider { table = Tables.MARKS; break; + case BOAT: + table = Tables.BOATS; + break; + default: throw new UnsupportedOperationException("Unknown uri: " + uri); } int rowsDeleted = db.delete(table, selection, selectionArgs); - notifyChange(AnalyticsContract.LeaderboardsEventsCompetitorsMarksJoined.CONTENT_URI); + notifyChange(AnalyticsContract.LeaderboardsEventsCompetitorsMarksBoatsJoined.CONTENT_URI); notifyChange(uri); return rowsDeleted; } @Override - public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { + public int update(@NonNull Uri uri, ContentValues values, String selection, String[] selectionArgs) { if (BuildConfig.DEBUG) { ExLog.i(getContext(), TAG, "update() called with: uri = [" + uri + "], values = [" + values + "], selection = [" + selection + "], selectionArgs = [" + Arrays.toString(selectionArgs) + "]"); @@ -264,9 +289,11 @@ public class AnalyticsProvider extends ContentProvider { } private void notifyChange(Uri uri) { - ContentResolver cr = getContext().getContentResolver(); - if (cr != null) { - cr.notifyChange(uri, null); + if (getContext() != null) { + ContentResolver cr = getContext().getContentResolver(); + if (cr != null) { + cr.notifyChange(uri, null); + } } } @@ -304,6 +331,12 @@ public class AnalyticsProvider extends ContentProvider { case MARK_ID: return builder.table(Tables.MARKS).where(BaseColumns._ID + " = ?", uri.getLastPathSegment()); + case BOAT: + return builder.table(Tables.BOATS); + + case BOAT_ID: + return builder.table(Tables.BOATS).where(BaseColumns._ID + " = ?", uri.getLastPathSegment()); + default: throw new UnsupportedOperationException("Unknown uri: " + uri); } diff --git a/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/ui/activities/BuoyActivity.java b/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/ui/activities/BuoyActivity.java index ec2dfc3c904..f8c5d2327cd 100644 --- a/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/ui/activities/BuoyActivity.java +++ b/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/ui/activities/BuoyActivity.java @@ -2,16 +2,7 @@ package com.sap.sailing.android.tracking.app.ui.activities; import org.json.JSONObject; -import android.content.DialogInterface; -import android.content.Intent; -import android.graphics.drawable.ColorDrawable; -import android.os.Bundle; -import android.support.v7.app.AlertDialog; -import android.support.v7.widget.Toolbar; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; - +import com.sap.sailing.android.shared.data.CheckinUrlInfo; import com.sap.sailing.android.shared.data.LeaderboardInfo; import com.sap.sailing.android.shared.logging.ExLog; import com.sap.sailing.android.shared.ui.activities.AbstractBaseActivity; @@ -22,15 +13,28 @@ import com.sap.sailing.android.tracking.app.utils.AboutHelper; import com.sap.sailing.android.tracking.app.utils.AppPreferences; import com.sap.sailing.android.tracking.app.utils.CheckoutHelper; import com.sap.sailing.android.tracking.app.utils.DatabaseHelper; +import com.sap.sailing.android.tracking.app.valueobjects.BoatInfo; import com.sap.sailing.android.tracking.app.valueobjects.EventInfo; import com.sap.sailing.android.tracking.app.valueobjects.MarkInfo; +import android.content.DialogInterface; +import android.content.Intent; +import android.graphics.drawable.ColorDrawable; +import android.os.Bundle; +import android.support.v7.app.AlertDialog; +import android.support.v7.widget.Toolbar; +import android.text.TextUtils; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; + public class BuoyActivity extends AbstractBaseActivity { private static String TAG = BuoyActivity.class.getName(); private EventInfo event; private MarkInfo mark; + private BoatInfo boat; private LeaderboardInfo leaderboard; private String checkinDigest; @@ -44,6 +48,7 @@ public class BuoyActivity extends AbstractBaseActivity { prefs = new AppPreferences(this); mark = DatabaseHelper.getInstance().getMarkInfo(this, checkinDigest); + boat = DatabaseHelper.getInstance().getBoatInfo(this, checkinDigest); event = DatabaseHelper.getInstance().getEventInfo(this, checkinDigest); leaderboard = DatabaseHelper.getInstance().getLeaderboard(this, checkinDigest); @@ -51,19 +56,23 @@ public class BuoyActivity extends AbstractBaseActivity { Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); if (toolbar != null) { setSupportActionBar(toolbar); + if (getSupportActionBar() != null) { + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + getSupportActionBar().setHomeButtonEnabled(true); + toolbar.setNavigationIcon(R.drawable.sap_logo_64dp); + int sidePadding = (int) getResources().getDimension(R.dimen.toolbar_left_padding); + toolbar.setPadding(sidePadding, 0, 0, 0); + getSupportActionBar().setTitle(leaderboard.displayName); + getSupportActionBar().setSubtitle(event.name); + ColorDrawable backgroundDrawable = new ColorDrawable(getResources().getColor(R.color.toolbar_background)); + getSupportActionBar().setBackgroundDrawable(backgroundDrawable); + } } - if (getSupportActionBar() != null) { - getSupportActionBar().setDisplayHomeAsUpEnabled(true); - getSupportActionBar().setHomeButtonEnabled(true); - toolbar.setNavigationIcon(R.drawable.sap_logo_64dp); - int sidePadding = (int) getResources().getDimension(R.dimen.toolbar_left_padding); - toolbar.setPadding(sidePadding, 0, 0, 0); - getSupportActionBar().setTitle(leaderboard.displayName); - getSupportActionBar().setSubtitle(event.name); - ColorDrawable backgroundDrawable = new ColorDrawable(getResources().getColor(R.color.toolbar_background)); - getSupportActionBar().setBackgroundDrawable(backgroundDrawable); + if (TextUtils.isEmpty(mark.markId)) { + replaceFragment(R.id.content_frame, BuoyFragment.newInstance(boat.boatName, boat.boatColor)); + } else { + replaceFragment(R.id.content_frame, BuoyFragment.newInstance(mark.markName, null)); } - replaceFragment(R.id.content_frame, BuoyFragment.newInstance(mark.markName)); } @Override @@ -137,7 +146,9 @@ public class BuoyActivity extends AbstractBaseActivity { */ public void checkout() { CheckoutHelper checkoutHelper = new CheckoutHelper(); - checkoutHelper.checkoutMark(this, leaderboard.name, event.server, mark.markId, new NetworkHelper.NetworkHelperSuccessListener() { + String id = TextUtils.isEmpty(mark.markId) ? boat.boatId : mark.markId; + int type = TextUtils.isEmpty(mark.markId) ? CheckinUrlInfo.TYPE_BOAT : CheckinUrlInfo.TYPE_MARK; + checkoutHelper.checkoutMark(this, leaderboard.name, event.server, id, type, new NetworkHelper.NetworkHelperSuccessListener() { @Override public void performAction(JSONObject response) { DatabaseHelper.getInstance().deleteRegattaFromDatabase(BuoyActivity.this, event.checkinDigest); @@ -148,8 +159,7 @@ public class BuoyActivity extends AbstractBaseActivity { @Override public void performAction(NetworkHelper.NetworkHelperError e) { dismissProgressDialog(); - showErrorPopup(R.string.error, - R.string.error_could_not_complete_operation_on_server_try_again); + showErrorPopup(R.string.error, R.string.error_could_not_complete_operation_on_server_try_again); } }); } diff --git a/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/ui/activities/RegattaActivity.java b/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/ui/activities/RegattaActivity.java index 569096abacf..95b74c65cdc 100755 --- a/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/ui/activities/RegattaActivity.java +++ b/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/ui/activities/RegattaActivity.java @@ -424,8 +424,7 @@ public class RegattaActivity extends AbstractRegattaActivity if (checkinData instanceof CompetitorCheckinData) { CompetitorCheckinData competitorCheckinData = (CompetitorCheckinData) checkinData; DatabaseHelper.getInstance().deleteRegattaFromDatabase(this, checkinDigest); - DatabaseHelper.getInstance().storeCompetitorCheckinRow(this, competitorCheckinData.getEvent(), competitorCheckinData.getCompetitor(), - competitorCheckinData.getLeaderboard(), competitorCheckinData.getCheckinUrl()); + DatabaseHelper.getInstance().storeCompetitorCheckinRow(this, competitorCheckinData); competitor = DatabaseHelper.getInstance().getCompetitor(this, checkinDigest); event = DatabaseHelper.getInstance().getEventInfo(this, checkinDigest); leaderboard = DatabaseHelper.getInstance().getLeaderboard(this, checkinDigest); diff --git a/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/ui/activities/StartActivity.java b/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/ui/activities/StartActivity.java index 3ee0ee21b7a..750e8de0edf 100644 --- a/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/ui/activities/StartActivity.java +++ b/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/ui/activities/StartActivity.java @@ -2,13 +2,6 @@ package com.sap.sailing.android.tracking.app.ui.activities; import java.util.List; -import android.content.Intent; -import android.graphics.drawable.ColorDrawable; -import android.os.Bundle; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; - import com.sap.sailing.android.shared.data.BaseCheckinData; import com.sap.sailing.android.shared.logging.ExLog; import com.sap.sailing.android.shared.ui.activities.AbstractStartActivity; @@ -19,11 +12,19 @@ import com.sap.sailing.android.tracking.app.utils.AboutHelper; import com.sap.sailing.android.tracking.app.utils.AppPreferences; import com.sap.sailing.android.tracking.app.utils.CheckinManager; import com.sap.sailing.android.tracking.app.utils.DatabaseHelper; +import com.sap.sailing.android.tracking.app.valueobjects.BoatCheckinData; import com.sap.sailing.android.tracking.app.valueobjects.CheckinData; import com.sap.sailing.android.tracking.app.valueobjects.CompetitorCheckinData; import com.sap.sailing.android.tracking.app.valueobjects.MarkCheckinData; import com.sap.sailing.android.ui.fragments.AbstractHomeFragment; +import android.content.Intent; +import android.graphics.drawable.ColorDrawable; +import android.os.Bundle; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; + public class StartActivity extends AbstractStartActivity { private AppPreferences prefs; @@ -63,8 +64,7 @@ public class StartActivity extends AbstractStartActivity { @Override public AbstractHomeFragment getHomeFragment() { - HomeFragment homeFragment = (HomeFragment) getSupportFragmentManager().findFragmentById(R.id.content_frame); - return homeFragment; + return (HomeFragment) getSupportFragmentManager().findFragmentById(R.id.content_frame); } private void startRegatta(String checkinDigest) { @@ -120,7 +120,7 @@ public class StartActivity extends AbstractStartActivity { try { DatabaseHelper.getInstance().deleteRegattaFromDatabase(this, checkinData.getCheckinUrl().checkinDigest); DatabaseHelper.getInstance() - .storeCompetitorCheckinRow(this, checkinData.getEvent(), competitorCheckinData.getCompetitor(), checkinData.getLeaderboard(), checkinData.getCheckinUrl()); + .storeCompetitorCheckinRow(this, competitorCheckinData); } catch (DatabaseHelper.GeneralDatabaseHelperException e) { ExLog.e(this, TAG, "Batch insert failed: " + e.getMessage()); displayDatabaseError(); @@ -130,7 +130,16 @@ public class StartActivity extends AbstractStartActivity { try { DatabaseHelper.getInstance().deleteRegattaFromDatabase(this, checkinData.getCheckinUrl().checkinDigest); DatabaseHelper.getInstance() - .storeMarkCheckinRow(this, checkinData.getEvent(), markCheckinData.getMark(), checkinData.getLeaderboard(), checkinData.getCheckinUrl()); + .storeMarkCheckinRow(this, markCheckinData); + } catch (DatabaseHelper.GeneralDatabaseHelperException e) { + ExLog.e(this, TAG, "Batch insert failed: " + e.getMessage()); + displayDatabaseError(); + } + } else if (checkinData instanceof BoatCheckinData) { + BoatCheckinData boatCheckinData = (BoatCheckinData) checkinData; + try { + DatabaseHelper.getInstance().deleteRegattaFromDatabase(this, checkinData.getCheckinUrl().checkinDigest); + DatabaseHelper.getInstance().storeBoatCheckinRow(this, boatCheckinData); } catch (DatabaseHelper.GeneralDatabaseHelperException e) { ExLog.e(this, TAG, "Batch insert failed: " + e.getMessage()); displayDatabaseError(); diff --git a/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/ui/fragments/BuoyFragment.java b/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/ui/fragments/BuoyFragment.java index f9b15afbb3f..a0e5aa532b3 100644 --- a/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/ui/fragments/BuoyFragment.java +++ b/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/ui/fragments/BuoyFragment.java @@ -1,26 +1,29 @@ package com.sap.sailing.android.tracking.app.ui.fragments; -import android.annotation.SuppressLint; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.Button; -import android.widget.TextView; - import com.sap.sailing.android.shared.util.LocationHelper; import com.sap.sailing.android.shared.util.ViewHelper; import com.sap.sailing.android.tracking.app.R; import com.sap.sailing.android.tracking.app.ui.activities.BuoyActivity; +import android.annotation.SuppressLint; +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.TextView; + public class BuoyFragment extends BaseFragment { - private static String EXTRA_MARK_NAME = "mark_name"; + private static String EXTRA_NAME = "mark_name"; + private static String EXTRA_COLOR = "color"; - public static BuoyFragment newInstance(String markName) { + public static BuoyFragment newInstance(String name, @Nullable String color) { BuoyFragment fragment = new BuoyFragment(); Bundle args = new Bundle(); - args.putString(EXTRA_MARK_NAME, markName); + args.putString(EXTRA_NAME, name); + args.putString(EXTRA_COLOR, color); fragment.setArguments(args); return fragment; } @@ -29,8 +32,10 @@ public class BuoyFragment extends BaseFragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_buoy, container, false); - TextView markNameText = ViewHelper.get(view, R.id.buoy_Name); - markNameText.setText(getArguments().getString(EXTRA_MARK_NAME)); + TextView name = ViewHelper.get(view, R.id.buoy_Name); + name.setText(getArguments().getString(EXTRA_NAME)); + ViewHelper.setColors(name, getArguments().getString(EXTRA_COLOR, null)); + Button startTrackingButton = ViewHelper.get(view, R.id.start_tracking); startTrackingButton.setOnClickListener(new View.OnClickListener() { @Override diff --git a/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/ui/fragments/HomeFragment.java b/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/ui/fragments/HomeFragment.java index bf216538b61..4a149d4cadc 100644 --- a/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/ui/fragments/HomeFragment.java +++ b/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/ui/fragments/HomeFragment.java @@ -7,30 +7,6 @@ import java.util.Date; import org.json.JSONException; import org.json.JSONObject; -import android.annotation.SuppressLint; -import android.content.DialogInterface; -import android.content.Intent; -import android.database.Cursor; -import android.net.Uri; -import android.os.Bundle; -import android.support.v4.app.LoaderManager.LoaderCallbacks; -import android.support.v4.content.CursorLoader; -import android.support.v4.content.Loader; -import android.support.v7.app.AlertDialog; -import android.support.v7.internal.view.ContextThemeWrapper; -import android.text.SpannableString; -import android.text.Spanned; -import android.text.style.ClickableSpan; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.AdapterView; -import android.widget.AdapterView.OnItemClickListener; -import android.widget.AdapterView.OnItemLongClickListener; -import android.widget.ListView; -import android.widget.TextView; -import android.widget.Toast; - import com.sap.sailing.android.shared.data.BaseCheckinData; import com.sap.sailing.android.shared.data.CheckinUrlInfo; import com.sap.sailing.android.shared.data.LeaderboardInfo; @@ -54,6 +30,8 @@ import com.sap.sailing.android.tracking.app.utils.CheckinManager; import com.sap.sailing.android.tracking.app.utils.CheckoutHelper; import com.sap.sailing.android.tracking.app.utils.DatabaseHelper; import com.sap.sailing.android.tracking.app.utils.DatabaseHelper.GeneralDatabaseHelperException; +import com.sap.sailing.android.tracking.app.valueobjects.BoatCheckinData; +import com.sap.sailing.android.tracking.app.valueobjects.BoatInfo; import com.sap.sailing.android.tracking.app.valueobjects.CheckinData; import com.sap.sailing.android.tracking.app.valueobjects.CompetitorCheckinData; import com.sap.sailing.android.tracking.app.valueobjects.CompetitorInfo; @@ -62,6 +40,30 @@ import com.sap.sailing.android.tracking.app.valueobjects.MarkCheckinData; import com.sap.sailing.android.tracking.app.valueobjects.MarkInfo; import com.sap.sailing.android.ui.fragments.AbstractHomeFragment; +import android.annotation.SuppressLint; +import android.content.DialogInterface; +import android.content.Intent; +import android.database.Cursor; +import android.net.Uri; +import android.os.Bundle; +import android.support.v4.app.LoaderManager.LoaderCallbacks; +import android.support.v4.content.CursorLoader; +import android.support.v4.content.Loader; +import android.support.v7.app.AlertDialog; +import android.support.v7.internal.view.ContextThemeWrapper; +import android.text.SpannableString; +import android.text.Spanned; +import android.text.style.ClickableSpan; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemClickListener; +import android.widget.AdapterView.OnItemLongClickListener; +import android.widget.ListView; +import android.widget.TextView; +import android.widget.Toast; + public class HomeFragment extends AbstractHomeFragment implements LoaderCallbacks { private final static String TAG = HomeFragment.class.getName(); @@ -126,8 +128,8 @@ public class HomeFragment extends AbstractHomeFragment implements LoaderCallback private void checkInWithAPIAndDisplayTrackingActivity(CheckinData checkinData) { if (checkinData instanceof CompetitorCheckinData) { storeCompetitorCheckinData((CompetitorCheckinData) checkinData); - } else if (checkinData instanceof MarkCheckinData) { - storeMarkCheckinData((MarkCheckinData) checkinData); + } else if (checkinData instanceof MarkCheckinData || checkinData instanceof BoatCheckinData) { + storeMarkCheckinData(checkinData); } performAPICheckin(checkinData); } @@ -135,8 +137,7 @@ public class HomeFragment extends AbstractHomeFragment implements LoaderCallback private void storeCompetitorCheckinData(CompetitorCheckinData checkinData) { if (DatabaseHelper.getInstance().eventLeaderboardCompetitorCombinationAvailable(getActivity(), checkinData.checkinDigest)) { try { - DatabaseHelper.getInstance().storeCompetitorCheckinRow(getActivity(), checkinData.getEvent(), checkinData.getCompetitor(), checkinData - .getLeaderboard(), checkinData.getCheckinUrl()); + DatabaseHelper.getInstance().storeCompetitorCheckinRow(getActivity(), checkinData); adapter.notifyDataSetChanged(); } catch (GeneralDatabaseHelperException e) { ExLog.e(getActivity(), TAG, "Batch insert failed: " + e.getMessage()); @@ -153,11 +154,15 @@ public class HomeFragment extends AbstractHomeFragment implements LoaderCallback } } - private void storeMarkCheckinData(MarkCheckinData checkinData) { + private void storeMarkCheckinData(CheckinData checkinData) { if (DatabaseHelper.getInstance().eventLeaderboardMarkCombinationAvailable(getActivity(), checkinData.checkinDigest)) { try { - DatabaseHelper.getInstance().storeMarkCheckinRow(getActivity(), checkinData.getEvent(), checkinData.getMark(), checkinData - .getLeaderboard(), checkinData.getCheckinUrl()); + if (checkinData instanceof MarkCheckinData) { + DatabaseHelper.getInstance() + .storeMarkCheckinRow(getActivity(), (MarkCheckinData) checkinData); + } else { + DatabaseHelper.getInstance().storeBoatCheckinRow(getActivity(), (BoatCheckinData) checkinData); + } adapter.notifyDataSetChanged(); } catch (GeneralDatabaseHelperException e) { ExLog.e(getActivity(), TAG, "Batch insert failed: " + e.getMessage()); @@ -174,6 +179,7 @@ public class HomeFragment extends AbstractHomeFragment implements LoaderCallback } } + /** * Checkin with API. * @@ -195,6 +201,11 @@ public class HomeFragment extends AbstractHomeFragment implements LoaderCallback requestObject = CheckinHelper .getMarkCheckinJson(markCheckinData.getMark().getId().toString(), markCheckinData.deviceUid, "TODO push device ID!!", date .getTime()); + } else if (checkinData instanceof BoatCheckinData) { + BoatCheckinData boatCheckinData = (BoatCheckinData) checkinData; + requestObject = CheckinHelper + .getBoatCheckinJson(boatCheckinData.getBoat().getId().toString(), boatCheckinData.deviceUid, "TODO push device ID!!", date + .getTime()); } HttpJsonPostRequest request = new HttpJsonPostRequest(getActivity(), new URL(checkinData.checkinURL), requestObject.toString()); NetworkHelper.getInstance(getActivity()) @@ -246,6 +257,10 @@ public class HomeFragment extends AbstractHomeFragment implements LoaderCallback MarkCheckinData checkinData = (MarkCheckinData) data; clearScannedQRCodeInPrefs(); checkInWithAPIAndDisplayTrackingActivity(checkinData); + } else if (data instanceof BoatCheckinData) { + BoatCheckinData checkinData = (BoatCheckinData) data; + clearScannedQRCodeInPrefs(); + checkInWithAPIAndDisplayTrackingActivity(checkinData); } } @@ -258,7 +273,7 @@ public class HomeFragment extends AbstractHomeFragment implements LoaderCallback Intent intent = new Intent(); if (type == CheckinUrlInfo.TYPE_COMPETITOR) { intent.setClass(getActivity(), RegattaActivity.class); - } else if (type == CheckinUrlInfo.TYPE_MARK) { + } else if (type == CheckinUrlInfo.TYPE_MARK || type == CheckinUrlInfo.TYPE_BOAT) { intent.setClass(getActivity(), BuoyActivity.class); } intent.putExtra(getString(R.string.checkin_digest), checkinDigest); @@ -276,8 +291,10 @@ public class HomeFragment extends AbstractHomeFragment implements LoaderCallback AnalyticsDatabase.Tables.LEADERBOARDS + "." + AnalyticsContract.Leaderboard.LEADERBOARD_DISPLAY_NAME, AnalyticsDatabase.Tables.COMPETITORS + "." + AnalyticsContract.Competitor.COMPETITOR_DISPLAY_NAME, AnalyticsDatabase.Tables.MARKS + "." + AnalyticsContract.Mark.MARK_NAME, - AnalyticsDatabase.Tables.CHECKIN_URIS + "." + AnalyticsContract.Checkin.CHECKIN_TYPE}; - return new CursorLoader(getActivity(), AnalyticsContract.LeaderboardsEventsCompetitorsMarksJoined.CONTENT_URI, projection, null, null, null); + AnalyticsDatabase.Tables.CHECKIN_URIS + "." + AnalyticsContract.Checkin.CHECKIN_TYPE, + AnalyticsDatabase.Tables.BOATS + "." + AnalyticsContract.Boat.BOAT_NAME, + AnalyticsDatabase.Tables.BOATS + "." + AnalyticsContract.Boat.BOAT_COLOR }; + return new CursorLoader(getActivity(), AnalyticsContract.LeaderboardsEventsCompetitorsMarksBoatsJoined.CONTENT_URI, projection, null, null, null); default: return null; @@ -360,8 +377,10 @@ public class HomeFragment extends AbstractHomeFragment implements LoaderCallback successListener, failureListener); } else if (type == CheckinUrlInfo.TYPE_MARK) { MarkInfo markInfo = DatabaseHelper.getInstance().getMarkInfo(getActivity(), checkinDigest); - checkoutHelper.checkoutMark((StartActivity) getActivity(), leaderboardInfo.name, eventInfo.server, markInfo.markId, - successListener, failureListener); + checkoutHelper.checkoutMark((StartActivity) getActivity(), leaderboardInfo.name, eventInfo.server, markInfo.markId, type, successListener, failureListener); + } else if (type == CheckinUrlInfo.TYPE_BOAT) { + BoatInfo boatInfo = DatabaseHelper.getInstance().getBoatInfo(getActivity(), checkinDigest); + checkoutHelper.checkoutMark((StartActivity) getActivity(), leaderboardInfo.name, eventInfo.server, boatInfo.boatId, type, successListener, failureListener); } } diff --git a/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/utils/AppPreferences.java b/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/utils/AppPreferences.java index 2c5b1888014..59718cd5294 100644 --- a/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/utils/AppPreferences.java +++ b/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/utils/AppPreferences.java @@ -3,9 +3,6 @@ package com.sap.sailing.android.tracking.app.utils; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; -import android.content.Context; -import android.content.SharedPreferences; - import com.sap.sailing.android.shared.util.BaseAppPreferences; import com.sap.sailing.android.shared.util.PrefUtils; import com.sap.sailing.android.tracking.app.R; @@ -13,6 +10,9 @@ import com.sap.sailing.android.tracking.app.services.TrackingService; import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor; import com.sap.sailing.domain.abstractlog.impl.LogEventAuthorImpl; +import android.content.Context; +import android.content.SharedPreferences; + public class AppPreferences extends BaseAppPreferences { private final SharedPreferences pref; @@ -69,8 +69,13 @@ public class AppPreferences extends BaseAppPreferences { } public String getServerMarkPath(String leaderboardName, String markId) { - String path = context.getString(R.string.preferece_server_mark_path); - return path.replace("{leaderboardName}", leaderboardName).replace("{markId}", markId); + String path = context.getString(R.string.preference_server_mark_path); + return path.replace("{leaderboardName}", leaderboardName).replace("{mark_id}", markId); + } + + public String getServerBoatPath(String boatId) { + String path = context.getString(R.string.preference_server_boat_path); + return path.replace("{boat_id}", boatId); } public int getGPSFixInterval() { diff --git a/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/utils/CheckinHelper.java b/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/utils/CheckinHelper.java index 43a0d7aa3f3..fa679905645 100644 --- a/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/utils/CheckinHelper.java +++ b/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/utils/CheckinHelper.java @@ -3,17 +3,11 @@ package com.sap.sailing.android.tracking.app.utils; import org.json.JSONException; import org.json.JSONObject; -import android.support.annotation.NonNull; - import com.sap.sailing.domain.common.racelog.tracking.DeviceMappingConstants; +import android.support.annotation.NonNull; + public class CheckinHelper { - public static JSONObject getCompetitorCheckinJson(String competitorId, String deviceUuid, String pushDeviceId, long fromMillis) - throws JSONException { - JSONObject jsonObject = new JSONObject(); - jsonObject.put(DeviceMappingConstants.JSON_COMPETITOR_ID_AS_STRING, competitorId); - return getBaseCheckinJsom(deviceUuid, pushDeviceId, fromMillis, jsonObject); - } @NonNull private static JSONObject getBaseCheckinJsom(String deviceUuid, String pushDeviceId, long fromMillis, JSONObject jsonObject) @@ -25,10 +19,28 @@ public class CheckinHelper { return jsonObject; } + @NonNull + public static JSONObject getCompetitorCheckinJson(String competitorId, String deviceUuid, String pushDeviceId, long fromMillis) + throws JSONException { + JSONObject jsonObject = new JSONObject(); + jsonObject.put(DeviceMappingConstants.JSON_COMPETITOR_ID_AS_STRING, competitorId); + return getBaseCheckinJsom(deviceUuid, pushDeviceId, fromMillis, jsonObject); + } + + @NonNull public static JSONObject getMarkCheckinJson(String markId, String deviceUuid, String pushDeviceId, long fromMillis) throws JSONException { JSONObject jsonObject = new JSONObject(); jsonObject.put(DeviceMappingConstants.JSON_MARK_ID_AS_STRING, markId); return getBaseCheckinJsom(deviceUuid, pushDeviceId, fromMillis, jsonObject); } + + @NonNull + public static JSONObject getBoatCheckinJson(String boatId, String deviceUuid, String pushDeviceId, long fromMillis) + throws JSONException { + JSONObject jsonObject = new JSONObject(); + jsonObject.put(DeviceMappingConstants.JSON_BOAT_ID_AS_STRING, boatId); + return getBaseCheckinJsom(deviceUuid, pushDeviceId, fromMillis, jsonObject); + } + } diff --git a/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/utils/CheckinManager.java b/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/utils/CheckinManager.java index fe6a7b354d0..8f5528ff32c 100644 --- a/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/utils/CheckinManager.java +++ b/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/utils/CheckinManager.java @@ -20,6 +20,8 @@ import com.sap.sailing.android.shared.util.JsonHelper; import com.sap.sailing.android.shared.util.NetworkHelper; import com.sap.sailing.android.shared.util.UniqueDeviceUuid; import com.sap.sailing.android.tracking.app.R; +import com.sap.sailing.android.tracking.app.valueobjects.BoatCheckinData; +import com.sap.sailing.android.tracking.app.valueobjects.BoatUrlData; import com.sap.sailing.android.tracking.app.valueobjects.CheckinData; import com.sap.sailing.android.tracking.app.valueobjects.CompetitorCheckinData; import com.sap.sailing.android.tracking.app.valueobjects.CompetitorUrlData; @@ -30,13 +32,13 @@ import com.sap.sailing.domain.abstractlog.race.RaceLog; import com.sap.sailing.domain.abstractlog.race.SimpleRaceLogIdentifier; import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver; import com.sap.sailing.domain.base.EventBase; -import com.sap.sailing.domain.base.Mark; import com.sap.sailing.domain.base.impl.SharedDomainFactoryImpl; import com.sap.sailing.domain.common.racelog.tracking.DeviceMappingConstants; import com.sap.sailing.domain.common.tracking.impl.CompetitorJsonConstants; import com.sap.sailing.domain.racelogtracking.impl.SmartphoneUUIDIdentifierImpl; import com.sap.sailing.server.gateway.deserialization.JsonDeserializationException; import com.sap.sailing.server.gateway.deserialization.coursedata.impl.MarkDeserializer; +import com.sap.sailing.server.gateway.deserialization.impl.BoatJsonDeserializer; import com.sap.sailing.server.gateway.deserialization.impl.CourseAreaJsonDeserializer; import com.sap.sailing.server.gateway.deserialization.impl.EventBaseJsonDeserializer; import com.sap.sailing.server.gateway.deserialization.impl.LeaderboardGroupBaseJsonDeserializer; @@ -52,7 +54,6 @@ import android.widget.Toast; public class CheckinManager { private final static String TAG = CheckinManager.class.getName(); - private CheckinData checkinData; private CheckinDataActivity activity; private AppPreferences prefs; private UrlData urlData; @@ -94,33 +95,38 @@ public class CheckinManager { int port = uri.getPort(); Exception exception = null; try { - String leaderboardNameFromQR = URLEncoder.encode(uri.getQueryParameter(DeviceMappingConstants.URL_LEADERBOARD_NAME), "UTF-8") + String defaultCharset = "UTF-8"; + String leaderboardNameFromQR = URLEncoder.encode(uri.getQueryParameter(DeviceMappingConstants.URL_LEADERBOARD_NAME), defaultCharset) .replace("+", "%20"); Set parameterNames = uri.getQueryParameterNames(); if (parameterNames.contains(DeviceMappingConstants.URL_COMPETITOR_ID_AS_STRING)) { CompetitorUrlData competitorUrlData = new CompetitorUrlData(server, port); - competitorUrlData.competitorId = uri.getQueryParameter(DeviceMappingConstants.URL_COMPETITOR_ID_AS_STRING); + competitorUrlData.competitorId = URLEncoder.encode(uri.getQueryParameter(DeviceMappingConstants.URL_COMPETITOR_ID_AS_STRING), defaultCharset); competitorUrlData.competitorUrl = competitorUrlData.hostWithPort + prefs.getServerCompetitorPath(competitorUrlData.competitorId); urlData = competitorUrlData; } else if (parameterNames.contains(DeviceMappingConstants.URL_MARK_ID_AS_STRING)) { MarkUrlData markUrlData = new MarkUrlData(server, port); - markUrlData.setMarkId(uri.getQueryParameter(DeviceMappingConstants.URL_MARK_ID_AS_STRING)); + markUrlData.setMarkId(URLEncoder.encode(uri.getQueryParameter(DeviceMappingConstants.URL_MARK_ID_AS_STRING), defaultCharset)); markUrlData.setMarkUrl(markUrlData.hostWithPort + prefs.getServerMarkPath(leaderboardNameFromQR, markUrlData.getMarkId())); urlData = markUrlData; + } else if (parameterNames.contains(DeviceMappingConstants.URL_BOAT_ID_AS_STRING)) { + BoatUrlData boatUrlData = new BoatUrlData(server, port); + boatUrlData.setBoatId(URLEncoder.encode(uri.getQueryParameter(DeviceMappingConstants.URL_BOAT_ID_AS_STRING), defaultCharset)); + boatUrlData.setBoatUrl(boatUrlData.hostWithPort + prefs.getServerBoatPath(boatUrlData.getBoatId())); + urlData = boatUrlData; } else { - ExLog.e(activity, TAG, "Neither mark nor competitor checkin"); + ExLog.e(activity, TAG, "Neither competitor, boat or boat checkin"); exception = new Exception(); } if (urlData != null) { urlData.uriStr = uri.toString(); urlData.checkinURLStr = urlData.hostWithPort + prefs.getServerCheckinPath().replace("{leaderboard-name}", leaderboardNameFromQR); - urlData.eventId = uri.getQueryParameter(DeviceMappingConstants.URL_EVENT_ID); + urlData.eventId = URLEncoder.encode(uri.getQueryParameter(DeviceMappingConstants.URL_EVENT_ID), defaultCharset); urlData.leaderboardName = leaderboardNameFromQR; urlData.deviceUuid = new SmartphoneUUIDIdentifierImpl(UUID.fromString(UniqueDeviceUuid.getUniqueId(activity))); urlData.eventUrl = urlData.hostWithPort + prefs.getServerEventPath(urlData.eventId); urlData.leaderboardUrl = urlData.hostWithPort + prefs.getServerLeaderboardPath(urlData.leaderboardName); } - } catch (UnsupportedEncodingException e) { ExLog.e(activity, TAG, "Failed to encode leaderboard name: " + e.getMessage()); exception = e; @@ -136,9 +142,9 @@ public class CheckinManager { return urlData; } - private void getLeaderBoardFromServer(HttpGetRequest getLeaderboardRequest) { + private void getLeaderBoardFromServer(HttpGetRequest request) { NetworkHelper.getInstance(activity) - .executeHttpJsonRequestAsync(getLeaderboardRequest, new NetworkHelper.NetworkHelperSuccessListener() { + .executeHttpJsonRequestAsync(request, new NetworkHelper.NetworkHelperSuccessListener() { @Override public void performAction(JSONObject response) { final String leaderboardDisplayName = response.optString("displayName", urlData.leaderboardName); @@ -160,8 +166,8 @@ public class CheckinManager { }); } - private void getEventFromServer(HttpGetRequest getEventRequest, final String leaderboardDisplayName) { - NetworkHelper.getInstance(activity).executeHttpJsonRequestAsync(getEventRequest, new NetworkHelper.NetworkHelperSuccessListener() { + private void getEventFromServer(HttpGetRequest request, final String leaderboardDisplayName) { + NetworkHelper.getInstance(activity).executeHttpJsonRequestAsync(request, new NetworkHelper.NetworkHelperSuccessListener() { @Override public void performAction(JSONObject response) { EventBaseJsonDeserializer deserializer = new EventBaseJsonDeserializer( @@ -189,11 +195,12 @@ public class CheckinManager { handleApiError(); return; } + HttpGetRequest getRequest; if (urlData instanceof CompetitorUrlData) { CompetitorUrlData competitorUrlData = (CompetitorUrlData) urlData; try { - HttpGetRequest getCompetitorRequest = new HttpGetRequest(new URL(competitorUrlData.competitorUrl), activity); - getCompetitorFromServer(getCompetitorRequest, competitorUrlData, leaderboardDisplayName); + getRequest = new HttpGetRequest(new URL(competitorUrlData.competitorUrl), activity); + getCompetitorFromServer(getRequest, competitorUrlData, leaderboardDisplayName); } catch (MalformedURLException e2) { ExLog.e(activity, TAG, "Error: Failed to perform checking due to a MalformedURLException: " + e2.getMessage()); handleApiError(); @@ -201,12 +208,21 @@ public class CheckinManager { } else if (urlData instanceof MarkUrlData) { MarkUrlData markUrlData = (MarkUrlData) urlData; try { - HttpGetRequest getMarkRequest = new HttpGetRequest(new URL(markUrlData.getMarkUrl()), activity); - getMarkFromServer(getMarkRequest, markUrlData, leaderboardDisplayName); + getRequest = new HttpGetRequest(new URL(markUrlData.getMarkUrl()), activity); + getMarkFromServer(getRequest, markUrlData, leaderboardDisplayName); } catch (MalformedURLException exception) { ExLog.e(activity, TAG, "Error: Failed to perform checking due to a MalformedURLException: " + exception.getMessage()); handleApiError(); } + } else if (urlData instanceof BoatUrlData) { + BoatUrlData boatUrlData = (BoatUrlData) urlData; + try { + getRequest = new HttpGetRequest(new URL(boatUrlData.getBoatUrl()), activity); + getBoatFromServer(getRequest, boatUrlData, leaderboardDisplayName); + } catch (MalformedURLException exception) { + ExLog.e(activity, TAG, "Error: Failed to perform checking du to a MalformedURLException: " + exception.getMessage()); + handleApiError(); + } } } @@ -220,25 +236,25 @@ public class CheckinManager { }); } - private void getCompetitorFromServer(HttpGetRequest getCompetitorRequest, final CompetitorUrlData urlData, final String leaderboardDisplayName) { + private void getCompetitorFromServer(HttpGetRequest request, final CompetitorUrlData data, final String leaderboardDisplayName) { NetworkHelper.getInstance(activity) - .executeHttpJsonRequestAsync(getCompetitorRequest, new NetworkHelper.NetworkHelperSuccessListener() { + .executeHttpJsonRequestAsync(request, new NetworkHelper.NetworkHelperSuccessListener() { @Override public void performAction(JSONObject response) { activity.dismissProgressDialog(); try { - urlData.competitorName = response.getString(CompetitorJsonConstants.FIELD_NAME); - urlData.competitorId = response.getString(CompetitorJsonConstants.FIELD_ID); - urlData.competitorSailId = response.getString(CompetitorJsonConstants.FIELD_SAIL_ID); - urlData.competitorNationality = response.getString(CompetitorJsonConstants.FIELD_NATIONALITY); - urlData.competitorCountryCode = response.getString(CompetitorJsonConstants.FIELD_COUNTRY_CODE); + data.competitorName = response.getString(CompetitorJsonConstants.FIELD_NAME); + data.competitorId = response.getString(CompetitorJsonConstants.FIELD_ID); + data.competitorSailId = response.getString(CompetitorJsonConstants.FIELD_SAIL_ID); + data.competitorNationality = response.getString(CompetitorJsonConstants.FIELD_NATIONALITY); + data.competitorCountryCode = response.getString(CompetitorJsonConstants.FIELD_COUNTRY_CODE); // TODO Bug 3358: get optional team image from CompetitorJsonConstants.FIELD_TEAM_IMAGE_URI } catch (JSONException e) { - ExLog.e(activity, TAG, "Error getting data from call on URL: " + urlData.competitorUrl + ", Error: " + e.getMessage()); + ExLog.e(activity, TAG, "Error getting data from call on URL: " + data.competitorUrl + ", Error: " + e.getMessage()); handleApiError(); return; } - saveCheckinDataAndNotifyListeners(urlData, leaderboardDisplayName); + saveCheckinDataAndNotifyListeners(data, leaderboardDisplayName); } }, new NetworkHelper.NetworkHelperFailureListener() { @Override @@ -249,71 +265,90 @@ public class CheckinManager { }); } - private void getMarkFromServer(HttpGetRequest getMarkRequest, final MarkUrlData urlData, final String leaderboardDisplayName) { + private void getMarkFromServer(HttpGetRequest request, final MarkUrlData data, final String leaderboardDisplayName) { NetworkHelper.getInstance(activity) - .executeHttpJsonRequestAsync(getMarkRequest, new NetworkHelper.NetworkHelperSuccessListener() { + .executeHttpJsonRequestAsync(request, new NetworkHelper.NetworkHelperSuccessListener() { @Override public void performAction(JSONObject response) { + activity.dismissProgressDialog(); try { - org.json.simple.JSONObject simpleMark; - simpleMark = JsonHelper.convertToSimple(response); + org.json.simple.JSONObject simpleMark = JsonHelper.convertToSimple(response); MarkDeserializer markDeserializer = new MarkDeserializer(new SharedDomainFactoryImpl(null)); - Mark mark = markDeserializer.deserialize(simpleMark); - urlData.setMark(mark); + data.setMark(markDeserializer.deserialize(simpleMark)); } catch (ParseException|JsonDeserializationException e) { ExLog.e(activity, TAG, "Failed to deserialize mark"); - if (activity != null) { - activity.dismissProgressDialog(); - displayAPIErrorRecommendRetry(); - } + handleApiError(); return; } - saveCheckinDataAndNotifyListeners(urlData, leaderboardDisplayName); + saveCheckinDataAndNotifyListeners(data, leaderboardDisplayName); } }, new NetworkHelper.NetworkHelperFailureListener() { @Override public void performAction(NetworkHelper.NetworkHelperError e) { ExLog.e(activity, TAG, "Failed to get mark from server: " + e.getMessage()); - if (activity != null) { - activity.dismissProgressDialog(); - displayAPIErrorRecommendRetry(); - } + handleApiError(); } }); } - private void saveCheckinDataAndNotifyListeners(UrlData urlData, String leaderboardDisplayName) { - CheckinData data; - if (urlData instanceof CompetitorUrlData) { - CompetitorUrlData competitorUrlData = (CompetitorUrlData) urlData; - data = new CompetitorCheckinData(competitorUrlData, leaderboardDisplayName); + private void getBoatFromServer(HttpGetRequest request, final BoatUrlData data, final String leaderboardDisplayName) { + NetworkHelper.getInstance(activity) + .executeHttpJsonRequestAsync(request, new NetworkHelper.NetworkHelperSuccessListener() { + @Override + public void performAction(JSONObject response) { + activity.dismissProgressDialog(); + try { + org.json.simple.JSONObject simpleBoat = JsonHelper.convertToSimple(response); + BoatJsonDeserializer boatDeserializer = BoatJsonDeserializer.create(new SharedDomainFactoryImpl(null)); + data.setBoat(boatDeserializer.deserialize(simpleBoat)); + } catch (ParseException|JsonDeserializationException e) { + ExLog.e(activity, TAG, "Failed to deserialize boat"); + handleApiError(); + return; + } + saveCheckinDataAndNotifyListeners(data, leaderboardDisplayName); + } + }, new NetworkHelper.NetworkHelperFailureListener() { + @Override + public void performAction(NetworkHelper.NetworkHelperError e) { + ExLog.e(activity, TAG, "Failed to get boat from server: " + e.getMessage()); + handleApiError(); + } + }); + } + + private void saveCheckinDataAndNotifyListeners(UrlData data, String leaderboardDisplayName) { + CheckinData checkinData; + if (data instanceof CompetitorUrlData) { + CompetitorUrlData competitorUrlData = (CompetitorUrlData) data; + checkinData = new CompetitorCheckinData(competitorUrlData, leaderboardDisplayName); + } else if (data instanceof BoatUrlData) { + BoatUrlData boatUrlData = (BoatUrlData) data; + checkinData = new BoatCheckinData(boatUrlData, leaderboardDisplayName); } else { - MarkUrlData markUrlData = (MarkUrlData) urlData; - data = new MarkCheckinData(markUrlData, leaderboardDisplayName); + MarkUrlData markUrlData = (MarkUrlData) data; + checkinData = new MarkCheckinData(markUrlData, leaderboardDisplayName); } - data.setUpdate(update); + checkinData.setUpdate(update); try { - data.setCheckinDigestFromString(urlData.uriStr); + checkinData.setCheckinDigestFromString(data.uriStr); activity.dismissProgressDialog(); - setCheckinData(data); + setCheckinData(checkinData); } catch (UnsupportedEncodingException | NoSuchAlgorithmException e) { - ExLog.e(activity, TAG, "Failed to get generate digest of qr-code string (" + urlData.uriStr + "). " + e.getMessage()); + ExLog.e(activity, TAG, "Failed to get generate digest of qr-code string (" + data.uriStr + "). " + e.getMessage()); handleApiError(); } } - public void setCheckinData(CheckinData data) { - checkinData = data; - activity.onCheckinDataAvailable(getCheckinData()); - } - - public CheckinData getCheckinData() { - return checkinData; + private void setCheckinData(CheckinData data) { + activity.onCheckinDataAvailable(data); } private void handleApiError() { prefs.setLastScannedQRCode(null); - activity.dismissProgressDialog(); + if (activity != null) { + activity.dismissProgressDialog(); + } displayAPIErrorRecommendRetry(); setCheckinData(null); } @@ -322,6 +357,7 @@ public class CheckinManager { * Shows a pop-up-dialog that informs the user than an API-call has failed and recommends a retry. */ private void displayAPIErrorRecommendRetry() { + setCheckinData(null); AlertDialog.Builder builder = new AlertDialog.Builder(activity, R.style.AppTheme_AlertDialog); builder.setMessage(activity.getString(R.string.notify_user_api_call_failed)); builder.setCancelable(true); @@ -334,7 +370,6 @@ public class CheckinManager { }); builder.show(); - setCheckinData(null); } } diff --git a/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/utils/CheckoutHelper.java b/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/utils/CheckoutHelper.java index 068b9491e0c..7dfd8bb72ed 100644 --- a/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/utils/CheckoutHelper.java +++ b/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/utils/CheckoutHelper.java @@ -6,8 +6,7 @@ import java.net.URL; import org.json.JSONException; import org.json.JSONObject; -import android.net.Uri; - +import com.sap.sailing.android.shared.data.CheckinUrlInfo; import com.sap.sailing.android.shared.data.http.HttpJsonPostRequest; import com.sap.sailing.android.shared.logging.ExLog; import com.sap.sailing.android.shared.ui.activities.AbstractBaseActivity; @@ -16,6 +15,8 @@ import com.sap.sailing.android.shared.util.UniqueDeviceUuid; import com.sap.sailing.android.tracking.app.R; import com.sap.sailing.domain.common.racelog.tracking.DeviceMappingConstants; +import android.net.Uri; + public class CheckoutHelper { private static String TAG = CheckoutHelper.class.getName(); @@ -49,8 +50,7 @@ public class CheckoutHelper { } } - public void checkoutMark(AbstractBaseActivity activity, String leaderboardName, String eventServer, String markId, - NetworkHelper.NetworkHelperSuccessListener successListener, NetworkHelper.NetworkHelperFailureListener failureListener) { + public void checkoutMark(AbstractBaseActivity activity, String leaderboardName, String eventServer, String id, int type, NetworkHelper.NetworkHelperSuccessListener successListener, NetworkHelper.NetworkHelperFailureListener failureListener) { AppPreferences prefs = new AppPreferences(activity); final String checkoutURLStr = eventServer + prefs.getServerCheckoutPath().replace("{leaderboard-name}", Uri.encode(leaderboardName)); @@ -59,7 +59,11 @@ public class CheckoutHelper { JSONObject checkoutData = new JSONObject(); try { - checkoutData.put(DeviceMappingConstants.JSON_MARK_ID_AS_STRING, markId); + if (CheckinUrlInfo.TYPE_MARK == type) { + checkoutData.put(DeviceMappingConstants.JSON_MARK_ID_AS_STRING, id); + } else { + checkoutData.put(DeviceMappingConstants.JSON_BOAT_ID_AS_STRING, id); + } checkoutData.put(DeviceMappingConstants.JSON_DEVICE_UUID, UniqueDeviceUuid.getUniqueId(activity)); checkoutData.put(DeviceMappingConstants.JSON_TO_MILLIS, System.currentTimeMillis()); } catch (JSONException e) { diff --git a/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/utils/DatabaseHelper.java b/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/utils/DatabaseHelper.java index dd6bba917a3..29964eeb8eb 100644 --- a/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/utils/DatabaseHelper.java +++ b/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/utils/DatabaseHelper.java @@ -3,15 +3,6 @@ package com.sap.sailing.android.tracking.app.utils; import java.util.ArrayList; import java.util.List; -import android.content.ContentProviderOperation; -import android.content.ContentResolver; -import android.content.ContentValues; -import android.content.Context; -import android.content.OperationApplicationException; -import android.database.Cursor; -import android.os.RemoteException; -import android.provider.BaseColumns; - import com.sap.sailing.android.shared.data.CheckinUrlInfo; import com.sap.sailing.android.shared.data.LeaderboardInfo; import com.sap.sailing.android.shared.logging.ExLog; @@ -22,16 +13,31 @@ import com.sap.sailing.android.tracking.app.provider.AnalyticsContract.Competito import com.sap.sailing.android.tracking.app.provider.AnalyticsContract.Event; import com.sap.sailing.android.tracking.app.provider.AnalyticsContract.Leaderboard; import com.sap.sailing.android.tracking.app.provider.AnalyticsDatabase; +import com.sap.sailing.android.tracking.app.valueobjects.BoatCheckinData; +import com.sap.sailing.android.tracking.app.valueobjects.BoatInfo; +import com.sap.sailing.android.tracking.app.valueobjects.CompetitorCheckinData; import com.sap.sailing.android.tracking.app.valueobjects.CompetitorInfo; import com.sap.sailing.android.tracking.app.valueobjects.EventInfo; +import com.sap.sailing.android.tracking.app.valueobjects.MarkCheckinData; import com.sap.sailing.android.tracking.app.valueobjects.MarkInfo; +import com.sap.sailing.domain.base.Boat; import com.sap.sailing.domain.base.Mark; +import android.content.ContentProviderOperation; +import android.content.ContentResolver; +import android.content.ContentValues; +import android.content.Context; +import android.content.OperationApplicationException; +import android.database.Cursor; +import android.os.RemoteException; +import android.provider.BaseColumns; +import android.text.TextUtils; + public class DatabaseHelper { private final static String TAG = DatabaseHelper.class.getName(); - protected static DatabaseHelper mInstance; + private static DatabaseHelper mInstance; public static synchronized DatabaseHelper getInstance() { if (mInstance == null) { @@ -155,6 +161,22 @@ public class DatabaseHelper { return markInfo; } + public BoatInfo getBoatInfo(Context context, String checkinDigest) { + BoatInfo boatInfo = new BoatInfo(); + boatInfo.checkinDigest = checkinDigest; + Cursor boatCursor = context.getContentResolver().query(AnalyticsContract.Boat.CONTENT_URI, null, + AnalyticsContract.Boat.BOAT_CHECKIN_DIGEST + " = ?", new String[] { checkinDigest }, null); + if (boatCursor != null) { + if (boatCursor.moveToFirst()) { + boatInfo.boatId = boatCursor.getString(boatCursor.getColumnIndex(AnalyticsContract.Boat.BOAT_ID)); + boatInfo.boatName = boatCursor.getString(boatCursor.getColumnIndex(AnalyticsContract.Boat.BOAT_NAME)); + boatInfo.boatColor = boatCursor.getString(boatCursor.getColumnIndex(AnalyticsContract.Boat.BOAT_COLOR)); + } + boatCursor.close(); + } + return boatInfo; + } + public void deleteRegattaFromDatabase(Context context, String checkinDigest) { ContentResolver cr = context.getContentResolver(); @@ -163,6 +185,7 @@ public class DatabaseHelper { int d3 = cr.delete(Leaderboard.CONTENT_URI, Leaderboard.LEADERBOARD_CHECKIN_DIGEST + " = ?", new String[] { checkinDigest }); int d4 = cr.delete(Checkin.CONTENT_URI, Checkin.CHECKIN_URI_CHECKIN_DIGEST + " = ?", new String[] { checkinDigest }); int d5 = cr.delete(AnalyticsContract.Mark.CONTENT_URI, AnalyticsContract.Mark.MARK_CHECKIN_DIGEST + " = ?", new String[] { checkinDigest }); + int d6 = cr.delete(AnalyticsContract.Boat.CONTENT_URI, AnalyticsContract.Boat.BOAT_CHECKIN_DIGEST + " = ?", new String[] { checkinDigest }); if (BuildConfig.DEBUG) { ExLog.i(context, TAG, "Checkout, number of events deleted: " + d1); @@ -170,6 +193,7 @@ public class DatabaseHelper { ExLog.i(context, TAG, "Checkout, number of leaderboards deleted: " + d3); ExLog.i(context, TAG, "Checkout, number of checkinurls deleted: " + d4); ExLog.i(context, TAG, "Checkout, number of marks deleted: " + d5); + ExLog.i(context, TAG, "Checkout, number of boats deleted: " + d6); } } @@ -177,14 +201,14 @@ public class DatabaseHelper { * When checking in, store info on the event, the competitor and the leaderboard in the database. * * @param context android context - * @param event event to be stored - * @param competitor competitor to be stored - * @param leaderboard leaderboard to be stored * @return success or failure * @throws GeneralDatabaseHelperException */ - public void storeCompetitorCheckinRow(Context context, EventInfo event, CompetitorInfo competitor, - LeaderboardInfo leaderboard, CheckinUrlInfo checkinURL) throws GeneralDatabaseHelperException { + public void storeCompetitorCheckinRow(Context context, CompetitorCheckinData checkin) throws GeneralDatabaseHelperException { + EventInfo event = checkin.getEvent(); + CompetitorInfo competitor = checkin.getCompetitor(); + LeaderboardInfo leaderboard = checkin.getLeaderboard(); + CheckinUrlInfo checkinURL = checkin.getCheckinUrl(); ContentResolver contentResolver = context.getContentResolver(); ArrayList opList = new ArrayList<>(); @@ -199,7 +223,7 @@ public class DatabaseHelper { cv.put(Competitor.COMPETITOR_CHECKIN_DIGEST, competitor.checkinDigest); opList.add(ContentProviderOperation.newInsert(Competitor.CONTENT_URI).withValues(cv).build()); - storeBasicInformation(leaderboard, event, checkinURL, opList, contentResolver); + addBasicInformationToOperationList(opList, leaderboard, event, checkinURL); try { contentResolver.applyBatch(AnalyticsContract.CONTENT_AUTHORITY, opList); @@ -208,8 +232,11 @@ public class DatabaseHelper { } } - public void storeMarkCheckinRow(Context context, EventInfo event, Mark mark, - LeaderboardInfo leaderboard, CheckinUrlInfo checkinURL) throws GeneralDatabaseHelperException { + public void storeMarkCheckinRow(Context context, MarkCheckinData checkin) throws GeneralDatabaseHelperException { + EventInfo event = checkin.getEvent(); + Mark mark = checkin.getMark(); + LeaderboardInfo leaderboard = checkin.getLeaderboard(); + CheckinUrlInfo checkinURL = checkin.getCheckinUrl(); // Store Mark information ContentResolver contentResolver = context.getContentResolver(); @@ -220,7 +247,7 @@ public class DatabaseHelper { cv.put(AnalyticsContract.Mark.MARK_CHECKIN_DIGEST, checkinURL.checkinDigest); opList.add(ContentProviderOperation.newInsert(AnalyticsContract.Mark.CONTENT_URI).withValues(cv).build()); - storeBasicInformation(leaderboard, event, checkinURL, opList, contentResolver); + addBasicInformationToOperationList(opList, leaderboard, event, checkinURL); try { contentResolver.applyBatch(AnalyticsContract.CONTENT_AUTHORITY, opList); @@ -229,15 +256,44 @@ public class DatabaseHelper { } } - private void storeBasicInformation(LeaderboardInfo leaderboard, EventInfo event, CheckinUrlInfo checkinURL, - ArrayList opList, ContentResolver contentResolver) { + public void storeBoatCheckinRow(Context context, BoatCheckinData checkin) throws GeneralDatabaseHelperException { + EventInfo event = checkin.getEvent(); + LeaderboardInfo leaderboard = checkin.getLeaderboard(); + CheckinUrlInfo checkinUrlInfo = checkin.getCheckinUrl(); + Boat boat = checkin.getBoat(); - // inserting leaderboard first + // boat + ContentResolver contentResolver = context.getContentResolver(); + ArrayList opList = new ArrayList<>(); + ContentValues cv = new ContentValues(); + cv.put(AnalyticsContract.Boat.BOAT_ID, boat.getId().toString()); + if (TextUtils.isEmpty(boat.getName())) { + cv.put(AnalyticsContract.Boat.BOAT_NAME, boat.getSailID()); + } else { + cv.put(AnalyticsContract.Boat.BOAT_NAME, boat.getName()); + } + cv.put(AnalyticsContract.Boat.BOAT_CHECKIN_DIGEST, checkinUrlInfo.checkinDigest); + cv.put(AnalyticsContract.Boat.BOAT_COLOR, boat.getColor().getAsHtml()); + opList.add(ContentProviderOperation.newInsert(AnalyticsContract.Boat.CONTENT_URI).withValues(cv).build()); + + addBasicInformationToOperationList(opList, leaderboard, event, checkinUrlInfo); + + try { + contentResolver.applyBatch(AnalyticsContract.CONTENT_AUTHORITY, opList); + } catch (RemoteException | OperationApplicationException e) { + throw new GeneralDatabaseHelperException(e.getMessage()); + } + } + + private void addBasicInformationToOperationList(ArrayList opList, LeaderboardInfo leaderboard, EventInfo event, + CheckinUrlInfo checkinURL) { + + // inserting leaderboard ContentValues cv = new ContentValues(); cv.put(Leaderboard.LEADERBOARD_NAME, leaderboard.name); cv.put(Leaderboard.LEADERBOARD_DISPLAY_NAME, leaderboard.displayName); cv.put(Leaderboard.LEADERBOARD_CHECKIN_DIGEST, leaderboard.checkinDigest); - contentResolver.insert(Leaderboard.CONTENT_URI, cv); + opList.add(0, ContentProviderOperation.newInsert(Leaderboard.CONTENT_URI).withValues(cv).build()); // now insert event cv.clear(); @@ -248,7 +304,6 @@ public class DatabaseHelper { cv.put(Event.EVENT_SERVER, event.server); cv.put(Event.EVENT_IMAGE_URL, event.imageUrl); cv.put(Event.EVENT_CHECKIN_DIGEST, event.checkinDigest); - opList.add(ContentProviderOperation.newInsert(Event.CONTENT_URI).withValues(cv).build()); // checkin url @@ -256,7 +311,6 @@ public class DatabaseHelper { cv.put(Checkin.CHECKIN_URI_VALUE, checkinURL.urlString); cv.put(Checkin.CHECKIN_URI_CHECKIN_DIGEST, checkinURL.checkinDigest); cv.put(Checkin.CHECKIN_TYPE, checkinURL.type); - opList.add(ContentProviderOperation.newInsert(Checkin.CONTENT_URI).withValues(cv).build()); } diff --git a/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/valueobjects/BoatCheckinData.java b/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/valueobjects/BoatCheckinData.java new file mode 100644 index 00000000000..db931e4234d --- /dev/null +++ b/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/valueobjects/BoatCheckinData.java @@ -0,0 +1,23 @@ +package com.sap.sailing.android.tracking.app.valueobjects; + +import com.sap.sailing.android.shared.data.CheckinUrlInfo; +import com.sap.sailing.domain.base.Boat; + +public class BoatCheckinData extends CheckinData { + + private Boat mBoat; + + public BoatCheckinData(BoatUrlData boatUrlData, String leaderboardDisplayName) { + super(boatUrlData, leaderboardDisplayName); + mBoat = boatUrlData.getBoat(); + } + + public Boat getBoat() { + return mBoat; + } + + @Override + public int getCheckinType() { + return CheckinUrlInfo.TYPE_BOAT; + } +} diff --git a/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/valueobjects/BoatInfo.java b/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/valueobjects/BoatInfo.java new file mode 100644 index 00000000000..dbf80bc17f9 --- /dev/null +++ b/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/valueobjects/BoatInfo.java @@ -0,0 +1,10 @@ +package com.sap.sailing.android.tracking.app.valueobjects; + +public class BoatInfo { + + public String boatId; + public String boatName; + public String checkinDigest; + public String boatColor; + +} diff --git a/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/valueobjects/BoatUrlData.java b/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/valueobjects/BoatUrlData.java new file mode 100644 index 00000000000..7f3e65c2dfe --- /dev/null +++ b/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/valueobjects/BoatUrlData.java @@ -0,0 +1,38 @@ +package com.sap.sailing.android.tracking.app.valueobjects; + +import com.sap.sailing.domain.base.Boat; + +public class BoatUrlData extends UrlData { + + private String mBoatId; + private String mBoatUrl; + private Boat mBoat; + + public BoatUrlData(String server, int port) { + super(server, port); + } + + public String getBoatId() { + return mBoatId; + } + + public void setBoatId(String boatId) { + mBoatId = boatId; + } + + public String getBoatUrl() { + return mBoatUrl; + } + + public void setBoatUrl(String boatUrl) { + mBoatUrl = boatUrl; + } + + public Boat getBoat() { + return mBoat; + } + + public void setBoat(Boat boat) { + mBoat = boat; + } +} diff --git a/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/valueobjects/CompetitorCheckinData.java b/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/valueobjects/CompetitorCheckinData.java index 7062a3d875d..c6dfd3c6770 100644 --- a/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/valueobjects/CompetitorCheckinData.java +++ b/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/valueobjects/CompetitorCheckinData.java @@ -4,7 +4,6 @@ import com.sap.sailing.android.shared.data.CheckinUrlInfo; public class CompetitorCheckinData extends CheckinData { - public String competitorName; public String competitorId; public String competitorUrl; diff --git a/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/valueobjects/MarkCheckinData.java b/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/valueobjects/MarkCheckinData.java index 37b41ffcad1..26b34f62faa 100644 --- a/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/valueobjects/MarkCheckinData.java +++ b/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/valueobjects/MarkCheckinData.java @@ -5,7 +5,7 @@ import com.sap.sailing.domain.base.Mark; public class MarkCheckinData extends CheckinData { - public Mark mark; + private Mark mark; public MarkCheckinData(MarkUrlData markUrlData, String leaderboardDisplayName) { super(markUrlData, leaderboardDisplayName); diff --git a/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/valueobjects/MarkInfo.java b/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/valueobjects/MarkInfo.java index 2bb3e5cbaa8..13fea685c2d 100644 --- a/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/valueobjects/MarkInfo.java +++ b/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/valueobjects/MarkInfo.java @@ -5,4 +5,5 @@ public class MarkInfo { public String markId; public String markName; public String checkinDigest; + } diff --git a/mobile/com.sap.sailing.racecommittee.app/res/layout/race_penalty_row_item.xml b/mobile/com.sap.sailing.racecommittee.app/res/layout/race_penalty_row_item.xml index 992c1c896d5..f2a93b08392 100644 --- a/mobile/com.sap.sailing.racecommittee.app/res/layout/race_penalty_row_item.xml +++ b/mobile/com.sap.sailing.racecommittee.app/res/layout/race_penalty_row_item.xml @@ -54,18 +54,32 @@ android:src="?attr/create" /> + + + competitors = new ArrayList(); - competitors.add(new CompetitorImpl(UUID.randomUUID(), "SAP Extreme Sailing Team", Color.BLUE, null, null, null, null, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null)); - competitors.add(new CompetitorImpl(UUID.randomUUID(), "The Wave Muscat", Color.LIGHT_GRAY, null, null, null, null, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null)); - competitors.add(new CompetitorImpl(UUID.randomUUID(), "Red Bull Extreme Sailing Team", Color.RED, null, null, null, null, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null)); - competitors.add(new CompetitorImpl(UUID.randomUUID(), "Team Korea", Color.GREEN, null, null, null, null, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null)); - competitors.add(new CompetitorImpl(UUID.randomUUID(), "Realteam", Color.BLACK, null, null, null, null, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null)); + competitors.add(new CompetitorImpl(UUID.randomUUID(), "SAP Extreme Sailing Team", "SAP", Color.BLUE, null, null, null, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null)); + competitors.add(new CompetitorImpl(UUID.randomUUID(), "The Wave Muscat", "Muscat", Color.LIGHT_GRAY, null, null, null, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null)); + competitors.add(new CompetitorImpl(UUID.randomUUID(), "Red Bull Extreme Sailing Team", "Red Bull", Color.RED, null, null, null, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null)); + competitors.add(new CompetitorImpl(UUID.randomUUID(), "Team Korea", "Korea", Color.GREEN, null, null, null, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null)); + competitors.add(new CompetitorImpl(UUID.randomUUID(), "Realteam", "Realteam", Color.BLACK, null, null, null, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null)); RaceLog log = new RaceLogImpl(UUID.randomUUID()); final AbstractLogEventAuthor author = AppPreferences.on(context).getAuthor(); @@ -223,25 +225,25 @@ public class OfflineDataManager extends DataManager { } @Override - public LoaderCallbacks>> createCompetitorsLoader( - final ManagedRace managedRace, LoadClient> callback) { - return new ImmediateDataLoaderCallbacks>(context, callback, - new Callable>() { + public LoaderCallbacks>> createCompetitorsLoader( + final ManagedRace managedRace, LoadClient> callback) { + return new ImmediateDataLoaderCallbacks>(context, callback, + new Callable>() { @Override - public Collection call() throws Exception { - return managedRace.getCompetitors(); + public Map call() throws Exception { + return managedRace.getCompetitorsAndBoats(); } }); } @Override - public LoaderCallbacks>> createStartOrderLoader(final ManagedRace managedRace, - LoadClient> callback) { - return new ImmediateDataLoaderCallbacks>(context, callback, - new Callable>() { + public LoaderCallbacks>> createStartOrderLoader(final ManagedRace managedRace, + LoadClient> callback) { + return new ImmediateDataLoaderCallbacks>(context, callback, + new Callable>() { @Override - public Collection call() throws Exception { - return managedRace.getCompetitors(); + public Map call() throws Exception { + return managedRace.getCompetitorsAndBoats(); } }); } diff --git a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/data/OnlineDataManager.java b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/data/OnlineDataManager.java index 6e2b0adf66b..5b3610a81d4 100644 --- a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/data/OnlineDataManager.java +++ b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/data/OnlineDataManager.java @@ -6,12 +6,14 @@ import java.net.URL; import java.util.ArrayList; import java.util.Collection; import java.util.List; +import java.util.Map; import java.util.concurrent.Callable; import com.sap.sailing.android.shared.logging.ExLog; import com.sap.sailing.android.shared.services.sending.MessageSendingService; import com.sap.sailing.domain.abstractlog.race.SimpleRaceLogIdentifier; import com.sap.sailing.domain.abstractlog.race.impl.SimpleRaceLogIdentifierImpl; +import com.sap.sailing.domain.base.Boat; import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.CourseArea; import com.sap.sailing.domain.base.CourseBase; @@ -64,21 +66,17 @@ import com.sap.sailing.server.gateway.deserialization.coursedata.impl.CourseBase import com.sap.sailing.server.gateway.deserialization.coursedata.impl.GateDeserializer; import com.sap.sailing.server.gateway.deserialization.coursedata.impl.MarkDeserializer; import com.sap.sailing.server.gateway.deserialization.coursedata.impl.WaypointDeserializer; -import com.sap.sailing.server.gateway.deserialization.impl.BoatClassJsonDeserializer; -import com.sap.sailing.server.gateway.deserialization.impl.BoatJsonDeserializer; -import com.sap.sailing.server.gateway.deserialization.impl.CompetitorJsonDeserializer; +import com.sap.sailing.server.gateway.deserialization.impl.CompetitorAndBoatJsonDeserializer; import com.sap.sailing.server.gateway.deserialization.impl.CourseAreaJsonDeserializer; import com.sap.sailing.server.gateway.deserialization.impl.DeviceConfigurationJsonDeserializer; import com.sap.sailing.server.gateway.deserialization.impl.EventBaseJsonDeserializer; import com.sap.sailing.server.gateway.deserialization.impl.LeaderboardGroupBaseJsonDeserializer; -import com.sap.sailing.server.gateway.deserialization.impl.NationalityJsonDeserializer; -import com.sap.sailing.server.gateway.deserialization.impl.PersonJsonDeserializer; import com.sap.sailing.server.gateway.deserialization.impl.RaceColumnFactorJsonDeserializer; import com.sap.sailing.server.gateway.deserialization.impl.RegattaConfigurationJsonDeserializer; -import com.sap.sailing.server.gateway.deserialization.impl.TeamJsonDeserializer; import com.sap.sailing.server.gateway.deserialization.impl.VenueJsonDeserializer; import com.sap.sailing.server.gateway.deserialization.racegroup.impl.RaceGroupDeserializer; import com.sap.sse.common.Util; +import com.sap.sse.common.Util.Pair; import android.app.LoaderManager.LoaderCallbacks; import android.content.Context; @@ -248,15 +246,15 @@ public class OnlineDataManager extends DataManager { } @Override - public LoaderCallbacks>> createCompetitorsLoader( - final ManagedRace managedRace, LoadClient> callback) { - return new DataLoaderCallbacks<>(callback, new LoaderCreator>() { + public LoaderCallbacks>> createCompetitorsLoader( + final ManagedRace managedRace, LoadClient> callback) { + return new DataLoaderCallbacks<>(callback, new LoaderCreator>() { @Override - public Loader>> create(int id, Bundle args) throws Exception { + public Loader>> create(int id, Bundle args) throws Exception { ExLog.i(context, TAG, "Creating Competitor-OnlineDataLoader " + id); - JsonDeserializer competitorDeserializer = new CompetitorJsonDeserializer(domainFactory.getCompetitorStore(), new TeamJsonDeserializer(new PersonJsonDeserializer(new NationalityJsonDeserializer(domainFactory))), new BoatJsonDeserializer(new BoatClassJsonDeserializer(domainFactory))); - DataParser> parser = new CompetitorsDataParser(competitorDeserializer); - DataHandler> handler = new CompetitorsDataHandler(OnlineDataManager.this, managedRace); + JsonDeserializer> competitorAndBoatDeserializer = CompetitorAndBoatJsonDeserializer.create(domainFactory); + DataParser> parser = new CompetitorsDataParser(competitorAndBoatDeserializer); + DataHandler> handler = new CompetitorsDataHandler(OnlineDataManager.this, managedRace); ManagedRaceIdentifier identifier = managedRace.getIdentifier(); @@ -268,22 +266,22 @@ public class OnlineDataManager extends DataManager { params.add(new Util.Pair(RaceLogServletConstants.PARAMS_LEADERBOARD_NAME, raceGroupName)); params.add(new Util.Pair(RaceLogServletConstants.PARAMS_RACE_COLUMN_NAME, raceColumnName)); params.add(new Util.Pair(RaceLogServletConstants.PARAMS_RACE_FLEET_NAME, fleetName)); - URL url = UrlHelper.generateUrl(preferences.getServerBaseURL(), "/sailingserver/rc/competitors", params); + URL url = UrlHelper.generateUrl(preferences.getServerBaseURL(), "/sailingserver/rc/competitorsAndBoats", params); return new OnlineDataLoader<>(context, url, parser, handler); } }, getContext()); } @Override - public LoaderCallbacks>> createStartOrderLoader( - final ManagedRace managedRace, LoadClient> callback) { - return new DataLoaderCallbacks<>(callback, new LoaderCreator>() { + public LoaderCallbacks>> createStartOrderLoader( + final ManagedRace managedRace, LoadClient> callback) { + return new DataLoaderCallbacks<>(callback, new LoaderCreator>() { @Override - public Loader>> create(int id, Bundle args) throws Exception { + public Loader>> create(int id, Bundle args) throws Exception { ExLog.i(context, TAG, "Creating StartOrder-Competitor-OnlineDataLoader " + id); - JsonDeserializer competitorDeserializer = new CompetitorJsonDeserializer(domainFactory.getCompetitorStore(), new TeamJsonDeserializer(new PersonJsonDeserializer(new NationalityJsonDeserializer(domainFactory))), new BoatJsonDeserializer(new BoatClassJsonDeserializer(domainFactory))); - DataParser> parser = new CompetitorsDataParser(competitorDeserializer); - DataHandler> handler = new CompetitorsDataHandler(OnlineDataManager.this, managedRace); + JsonDeserializer> competitorAndBoatDeserializer = CompetitorAndBoatJsonDeserializer.create(domainFactory); + DataParser> parser = new CompetitorsDataParser(competitorAndBoatDeserializer); + DataHandler> handler = new CompetitorsDataHandler(OnlineDataManager.this, managedRace); // https://dev.sapsailing.com/sailingserver/api/v1/regattas/ESS%202016%20Cardiff/races/Race%2016/startorder Uri.Builder uri = Uri.parse(preferences.getServerBaseURL()).buildUpon(); diff --git a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/data/ReadonlyDataManager.java b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/data/ReadonlyDataManager.java index 7d1e712827a..d63b421f35a 100755 --- a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/data/ReadonlyDataManager.java +++ b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/data/ReadonlyDataManager.java @@ -2,7 +2,9 @@ package com.sap.sailing.racecommittee.app.data; import java.io.Serializable; import java.util.Collection; +import java.util.Map; +import com.sap.sailing.domain.base.Boat; import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.CourseArea; import com.sap.sailing.domain.base.CourseBase; @@ -130,8 +132,8 @@ public interface ReadonlyDataManager { * {@link LoaderManager#initLoader(int, android.os.Bundle, LoaderCallbacks)} or * {@link LoaderManager#restartLoader(int, android.os.Bundle, LoaderCallbacks)}. */ - LoaderCallbacks>> createCompetitorsLoader(ManagedRace managedRace, - LoadClient> callback); + LoaderCallbacks>> createCompetitorsLoader(ManagedRace managedRace, + LoadClient> callback); /** * Create a new {@link LoaderCallbacks} object for loading {@link Competitor} @@ -142,8 +144,8 @@ public interface ReadonlyDataManager { * {@link LoaderManager#initLoader(int, android.os.Bundle, LoaderCallbacks)} or * {@link LoaderManager#restartLoader(int, android.os.Bundle, LoaderCallbacks)}. */ - LoaderCallbacks>> createStartOrderLoader(ManagedRace managedRace, - LoadClient> callback); + LoaderCallbacks>> createStartOrderLoader(ManagedRace managedRace, + LoadClient> callback); /** * Create a new {@link LoaderCallbacks} object for loading {@link LeaderboardResult} diff --git a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/data/deserializer/LeaderboardDeserializer.java b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/data/deserializer/LeaderboardDeserializer.java index aff28ca8a12..e0095536239 100644 --- a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/data/deserializer/LeaderboardDeserializer.java +++ b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/data/deserializer/LeaderboardDeserializer.java @@ -1,47 +1,56 @@ package com.sap.sailing.racecommittee.app.data.deserializer; -import java.io.Serializable; import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; import java.util.List; import java.util.Map; import org.json.simple.JSONArray; import org.json.simple.JSONObject; -import com.sap.sailing.racecommittee.app.domain.impl.CompetitorWithRaceRankImpl; import com.sap.sailing.racecommittee.app.domain.impl.LeaderboardResult; -import com.sap.sailing.racecommittee.app.domain.impl.RaceRankImpl; import com.sap.sailing.server.gateway.deserialization.JsonDeserializationException; import com.sap.sailing.server.gateway.deserialization.JsonDeserializer; +import com.sap.sse.common.Util; public class LeaderboardDeserializer implements JsonDeserializer { @Override public LeaderboardResult deserialize(JSONObject object) throws JsonDeserializationException { - List competitorWithRaceRanks = new ArrayList<>(); + Map>> result = new HashMap<>(); + + List> rankList; JSONArray competitors = (JSONArray) object.get("competitors"); for (int i = 0; i < competitors.size(); i++) { JSONObject jsonCompetitor = (JSONObject) competitors.get(i); - /* competitor data */ - String name = (String) jsonCompetitor.get("name"); - Serializable id = (Serializable) jsonCompetitor.get("id"); - CompetitorWithRaceRankImpl competitor = - new CompetitorWithRaceRankImpl(id, name, /* color */ null, /* email */ null, /* flagImage */ null, - /* dynamicTeam */ null, /* dynamicBoat */ null, /* onTimeFactor */ null, /* onDistance */ null, - /* searchTag */ null); + String id = (String) jsonCompetitor.get("id"); - /* races with rank */ JSONObject jsonRaceScores = (JSONObject) jsonCompetitor.get("raceScores"); for (Map.Entry raceScore : jsonRaceScores.entrySet()) { - String key = (String) raceScore.getKey(); - JSONObject value = (JSONObject) raceScore.getValue(); - Long rank = (Long) value.get("rank"); - RaceRankImpl raceRank = new RaceRankImpl(key, rank); - competitor.addRaceRank(raceRank); + String race = (String) raceScore.getKey(); + JSONObject jsonRace = (JSONObject) raceScore.getValue(); + Long rank = (Long) jsonRace.get("rank"); + rankList = result.get(race); + if (rankList == null) { + rankList = new ArrayList<>(); + } + rankList.add(new Util.Pair<>(rank, id)); + result.put(race, rankList); } - - competitorWithRaceRanks.add(competitor); } - return new LeaderboardResult(competitorWithRaceRanks); + + // sort by rank + for (Map.Entry>> item : result.entrySet()) { + Collections.sort(item.getValue(), new Comparator>() { + @Override + public int compare(Util.Pair left, Util.Pair right) { + return left.getA().intValue() - right.getA().intValue(); + } + }); + } + + return new LeaderboardResult(result); } } diff --git a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/data/handlers/CompetitorsDataHandler.java b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/data/handlers/CompetitorsDataHandler.java index af522733453..ee2f9151b67 100644 --- a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/data/handlers/CompetitorsDataHandler.java +++ b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/data/handlers/CompetitorsDataHandler.java @@ -1,12 +1,13 @@ package com.sap.sailing.racecommittee.app.data.handlers; -import java.util.Collection; +import java.util.Map; +import com.sap.sailing.domain.base.Boat; import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.racecommittee.app.data.OnlineDataManager; import com.sap.sailing.racecommittee.app.domain.ManagedRace; -public class CompetitorsDataHandler extends DataHandler> { +public class CompetitorsDataHandler extends DataHandler> { private ManagedRace race; @@ -21,12 +22,12 @@ public class CompetitorsDataHandler extends DataHandler> } @Override - public Collection getCachedResults() { - return race.getCompetitors(); + public Map getCachedResults() { + return race.getCompetitorsAndBoats(); } @Override - public void onResult(Collection data, boolean isCached) { + public void onResult(Map data, boolean isCached) { race.setCompetitors(data); } diff --git a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/data/parsers/CompetitorsDataParser.java b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/data/parsers/CompetitorsDataParser.java index 385de805049..06daa0c4206 100644 --- a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/data/parsers/CompetitorsDataParser.java +++ b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/data/parsers/CompetitorsDataParser.java @@ -1,37 +1,39 @@ package com.sap.sailing.racecommittee.app.data.parsers; import java.io.Reader; -import java.util.ArrayList; -import java.util.Collection; +import java.util.HashMap; +import java.util.Map; import org.json.simple.JSONArray; import org.json.simple.JSONObject; import org.json.simple.JSONValue; +import com.sap.sailing.domain.base.Boat; import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.server.gateway.deserialization.JsonDeserializer; import com.sap.sailing.server.gateway.deserialization.impl.Helpers; +import com.sap.sse.common.Util.Pair; -public class CompetitorsDataParser implements DataParser> { +public class CompetitorsDataParser implements DataParser> { - private JsonDeserializer deserializer; + private JsonDeserializer> deserializer; - public CompetitorsDataParser(JsonDeserializer deserializer) { + public CompetitorsDataParser(JsonDeserializer> deserializer) { this.deserializer = deserializer; } - public Collection parse(Reader reader) throws Exception { + public Map parse(Reader reader) throws Exception { Object parsedResult = JSONValue.parseWithException(reader); JSONArray jsonArray = Helpers.toJSONArraySafe(parsedResult); - Collection competitors = new ArrayList(); + Map competitorsAndBoats = new HashMap<>(); for (Object element : jsonArray) { JSONObject json = Helpers.toJSONObjectSafe(element); - Competitor competitor = deserializer.deserialize(json); - competitors.add(competitor); + Pair competitorAndBoatPair = deserializer.deserialize(json); + competitorsAndBoats.put(competitorAndBoatPair.getA(), competitorAndBoatPair.getB()); } - return competitors; + return competitorsAndBoats; } } diff --git a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/domain/ManagedRace.java b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/domain/ManagedRace.java index e55755e14fa..10d4f1c675d 100644 --- a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/domain/ManagedRace.java +++ b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/domain/ManagedRace.java @@ -1,9 +1,11 @@ package com.sap.sailing.racecommittee.app.domain; import java.util.Collection; +import java.util.Map; import com.sap.sailing.domain.abstractlog.race.RaceLog; import com.sap.sailing.domain.abstractlog.race.state.RaceState; +import com.sap.sailing.domain.base.Boat; import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.CourseBase; import com.sap.sailing.domain.base.racegroup.FilterableRace; @@ -58,18 +60,20 @@ public interface ManagedRace extends FilterableRace, ManagedRaceIdentifier, Name */ Collection getCompetitors(); + Map getCompetitorsAndBoats(); + CourseBase getCourseOnServer(); void setCourseOnServer(CourseBase course); /** - * sets the list of competitors for a race. As the competitors are retrieved later from the backend, the list of + * sets the list of competitors and boats for a race. As the competitors are retrieved later from the backend, the list of * competitors has to be settable. * * @param competitors * the retrieved list of competitors for this race */ - void setCompetitors(Collection competitors); + void setCompetitors(Map competitorsAndBoats); /** * Returns true if {@link RaceState} has been calculated and set diff --git a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/domain/impl/CompetitorResultWithIdImpl.java b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/domain/impl/CompetitorResultWithIdImpl.java index 89712d978e8..a3f80a50d7f 100644 --- a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/domain/impl/CompetitorResultWithIdImpl.java +++ b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/domain/impl/CompetitorResultWithIdImpl.java @@ -4,6 +4,7 @@ import java.io.Serializable; import com.sap.sailing.domain.abstractlog.race.CompetitorResult; import com.sap.sailing.domain.abstractlog.race.impl.CompetitorResultImpl; +import com.sap.sailing.domain.base.Boat; import com.sap.sailing.domain.common.MaxPointsReason; import com.sap.sse.common.TimePoint; @@ -11,21 +12,32 @@ public class CompetitorResultWithIdImpl extends CompetitorResultImpl { private static final long serialVersionUID = 4373611281142932067L; private long mId; + private Boat mBoat; - public CompetitorResultWithIdImpl(long id, CompetitorResult result) { - this(id, result.getCompetitorId(), result.getCompetitorDisplayName(), result.getOneBasedRank(), + public CompetitorResultWithIdImpl(long id, Boat boat, CompetitorResult result) { + this(id, boat, result.getCompetitorId(), result.getCompetitorDisplayName(), result.getOneBasedRank(), result.getMaxPointsReason(), result.getScore(), result.getFinishingTime(), result.getComment(), result.getMergeState()); } - public CompetitorResultWithIdImpl(long id, Serializable competitorId, String competitorDisplayName, + public CompetitorResultWithIdImpl(long id, Boat boat, Serializable competitorId, String competitorDisplayName, int oneBasedRank, MaxPointsReason maxPointsReason, Double score, TimePoint finishingTime, String comment, MergeState mergeState) { super(competitorId, competitorDisplayName, oneBasedRank, maxPointsReason, score, finishingTime, comment, mergeState); + mId = id; + mBoat = boat; } public long getId() { return mId; } + + public Boat getBoat() { + return mBoat; + } + + public void setBoat(Boat boat) { + mBoat = boat; + } } diff --git a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/domain/impl/CompetitorWithRaceRankImpl.java b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/domain/impl/CompetitorWithRaceRankImpl.java deleted file mode 100644 index 9dc37216ebf..00000000000 --- a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/domain/impl/CompetitorWithRaceRankImpl.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.sap.sailing.racecommittee.app.domain.impl; - -import java.io.Serializable; -import java.net.URI; -import java.util.ArrayList; -import java.util.List; - -import com.sap.sailing.domain.base.impl.CompetitorImpl; -import com.sap.sailing.domain.base.impl.DynamicBoat; -import com.sap.sailing.domain.base.impl.DynamicTeam; -import com.sap.sse.common.Color; -import com.sap.sse.common.Duration; - -public class CompetitorWithRaceRankImpl extends CompetitorImpl { - private static final long serialVersionUID = -6427316368214398951L; - private List mRankList; - - public CompetitorWithRaceRankImpl(Serializable id, String name, Color color, String email, URI flagImage, DynamicTeam team, DynamicBoat boat, - Double timeOnTimeFactor, Duration timeOnDistanceAllowancePerNauticalMile, String searchTag) { - super(id, name, color, email, flagImage, team, boat, timeOnTimeFactor, timeOnDistanceAllowancePerNauticalMile, searchTag); - - mRankList = new ArrayList<>(); - } - - public void addRaceRank(RaceRankImpl raceRank) { - mRankList.add(raceRank); - } - - public long getRaceRank(String raceName) { - long result = 0; - for (RaceRankImpl raceRank : mRankList) { - if (raceRank.getRace().equals(raceName)) { - result = raceRank.getRank(); - } - } - return result; - } -} diff --git a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/domain/impl/LeaderboardResult.java b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/domain/impl/LeaderboardResult.java index fd9884d4b34..ad5e73d1b10 100644 --- a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/domain/impl/LeaderboardResult.java +++ b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/domain/impl/LeaderboardResult.java @@ -1,17 +1,24 @@ package com.sap.sailing.racecommittee.app.domain.impl; import java.util.List; +import java.util.Map; + +import com.sap.sse.common.Util; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; public class LeaderboardResult { - private List mCompetitors; + private Map>> mResult; - public LeaderboardResult(List competitors) { - mCompetitors = competitors; + public LeaderboardResult(@NonNull Map>> result) { + mResult = result; } - public List getCompetitors() { - return mCompetitors; + @Nullable + public List> getResult(@NonNull String raceName) { + return mResult.get(raceName); } } diff --git a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/domain/impl/ManagedRaceImpl.java b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/domain/impl/ManagedRaceImpl.java index 77e995520b2..5cdd831f9de 100755 --- a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/domain/impl/ManagedRaceImpl.java +++ b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/domain/impl/ManagedRaceImpl.java @@ -1,12 +1,14 @@ package com.sap.sailing.racecommittee.app.domain.impl; -import java.util.ArrayList; import java.util.Collection; +import java.util.HashMap; +import java.util.Map; import com.sap.sailing.domain.abstractlog.race.RaceLog; import com.sap.sailing.domain.abstractlog.race.analyzing.impl.FinishingTimeFinder; import com.sap.sailing.domain.abstractlog.race.analyzing.impl.StartTimeFinder; import com.sap.sailing.domain.abstractlog.race.state.RaceState; +import com.sap.sailing.domain.base.Boat; import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.CourseBase; import com.sap.sailing.domain.base.Fleet; @@ -27,7 +29,7 @@ public class ManagedRaceImpl implements ManagedRace { private static final long serialVersionUID = -4936566684992524001L; private final ManagedRaceIdentifier identifier; private RaceState state; - private Collection competitors; + private Map competitorsAndBoats; private CourseBase courseOnServer; private ManagedRaceCalculator calculator; private double factor; @@ -51,7 +53,7 @@ public class ManagedRaceImpl implements ManagedRace { */ private ManagedRaceImpl(ManagedRaceIdentifier identifier, double factor, Double explicitFactor, int zeroBasedIndexInFleet) { this.identifier = identifier; - this.competitors = new ArrayList<>(); + this.competitorsAndBoats = new HashMap<>(); this.courseOnServer = null; this.factor = factor; this.explicitFactor = explicitFactor; @@ -125,12 +127,18 @@ public class ManagedRaceImpl implements ManagedRace { @Override public Collection getCompetitors() { - return competitors; + return competitorsAndBoats.keySet(); } @Override - public void setCompetitors(Collection competitors) { - this.competitors = competitors; + public Map getCompetitorsAndBoats() { + return competitorsAndBoats; + } + + @Override + public void setCompetitors(Map competitorsAndBoats) { + this.competitorsAndBoats.clear(); + this.competitorsAndBoats.putAll(competitorsAndBoats); } @Override diff --git a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/services/RaceStateService.java b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/services/RaceStateService.java index 8d1f5617434..eb643430c13 100755 --- a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/services/RaceStateService.java +++ b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/services/RaceStateService.java @@ -8,22 +8,6 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; -import android.annotation.TargetApi; -import android.app.AlarmManager; -import android.app.NotificationManager; -import android.app.PendingIntent; -import android.app.Service; -import android.content.Context; -import android.content.Intent; -import android.graphics.BitmapFactory; -import android.os.Binder; -import android.os.Build; -import android.os.IBinder; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.v4.app.NotificationCompat; -import android.util.Pair; - import com.sap.sailing.android.shared.logging.ExLog; import com.sap.sailing.domain.abstractlog.race.RaceLogEvent; import com.sap.sailing.domain.abstractlog.race.RaceLogEventVisitor; @@ -47,6 +31,22 @@ import com.sap.sailing.server.gateway.serialization.impl.CompetitorJsonSerialize import com.sap.sailing.server.gateway.serialization.racelog.impl.RaceLogEventSerializer; import com.sap.sse.common.impl.MillisecondsTimePoint; +import android.annotation.TargetApi; +import android.app.AlarmManager; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.graphics.BitmapFactory; +import android.os.Binder; +import android.os.Build; +import android.os.IBinder; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.app.NotificationCompat; +import android.util.Pair; + public class RaceStateService extends Service { private final static String TAG = RaceStateService.class.getName(); diff --git a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/adapters/CompetitorAdapter.java b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/adapters/CompetitorAndBoatAdapter.java similarity index 65% rename from mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/adapters/CompetitorAdapter.java rename to mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/adapters/CompetitorAndBoatAdapter.java index f526340bafb..71b9ceba3ff 100644 --- a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/adapters/CompetitorAdapter.java +++ b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/adapters/CompetitorAndBoatAdapter.java @@ -1,6 +1,13 @@ package com.sap.sailing.racecommittee.app.ui.adapters; import java.util.List; +import java.util.Map; + +import com.sap.sailing.android.shared.logging.ExLog; +import com.sap.sailing.android.shared.util.ViewHelper; +import com.sap.sailing.domain.base.Boat; +import com.sap.sailing.domain.base.Competitor; +import com.sap.sailing.racecommittee.app.R; import android.content.Context; import android.support.v7.widget.RecyclerView; @@ -9,21 +16,19 @@ import android.view.View; import android.view.ViewGroup; import android.widget.TextView; -import com.sap.sailing.android.shared.logging.ExLog; -import com.sap.sailing.domain.base.Competitor; -import com.sap.sailing.racecommittee.app.R; +public class CompetitorAndBoatAdapter extends RecyclerView.Adapter { -public class CompetitorAdapter extends RecyclerView.Adapter { + private static final String TAG = CompetitorAndBoatAdapter.class.getName(); - private static final String TAG = CompetitorAdapter.class.getName(); - - private Context mContext; - private List mData; + private final Context mContext; + private final List> mData; + private boolean mCanBoatsOfCompetitorsChangePerRace; private CompetitorClick mListener; - public CompetitorAdapter(Context context, List data) { + public CompetitorAndBoatAdapter(Context context, List> data, boolean canBoatsOfCompetitorsChangePerRace) { mContext = context; mData = data; + mCanBoatsOfCompetitorsChangePerRace = canBoatsOfCompetitorsChangePerRace; } @Override @@ -34,11 +39,16 @@ public class CompetitorAdapter extends RecyclerView.Adapter { - private Context mContext; - private CompetitorResultsList mCompetitor; + private final Context mContext; + private final CompetitorResultsList mCompetitor; + private final boolean mCanBoatsOfCompetitorsChangePerRace; private FinishEvents mListener; - public FinishListAdapter(Context context, CompetitorResultsList competitor) { + public FinishListAdapter(Context context, CompetitorResultsList competitor, boolean canBoatsOfCompetitorsChangePerRace) { setHasStableIds(true); mContext = context; mCompetitor = competitor; + mCanBoatsOfCompetitorsChangePerRace = canBoatsOfCompetitorsChangePerRace; } public void setListener(FinishEvents listener) { @@ -52,6 +54,12 @@ public class FinishListAdapter extends BaseDraggableSwipeAdapter mCompetitor; private List mFiltered; - private ItemListener mListener; + private Map mBoats; private OrderBy mOrderBy = OrderBy.SAILING_NUMBER; private String mFilter; - public PenaltyAdapter(Context context, @NonNull ItemListener listener) { + public PenaltyAdapter(Context context, @NonNull ItemListener listener, boolean canBoatsOfCompetitorsChangePerRace) { mContext = context; mListener = listener; + mCanBoatsOfCompetitorsChangePerRace = canBoatsOfCompetitorsChangePerRace; + + mBoats = new HashMap<>(); } @Override @@ -71,19 +83,24 @@ public class PenaltyAdapter extends RecyclerView.Adapter competitor) { + public void setCompetitor(List competitor, @Nullable Map data) { + if (data != null) { + mBoats.clear(); + for (Map.Entry entry : data.entrySet()) { + mBoats.put(entry.getKey().getId(), entry.getValue()); + } + } mCompetitor = competitor; mFiltered = filterData(); sortData(); @@ -189,8 +212,16 @@ public class PenaltyAdapter extends RecyclerView.Adapter> { + + private NaturalNamedComparator comparator; + + public CompetitorNameComparator() { + this.comparator = new NaturalNamedComparator<>(); + } + + @Override + public int compare(Map.Entry leftCompetitor, Map.Entry rightCompetitor) { + if (leftCompetitor != null && leftCompetitor.getKey() != null && + rightCompetitor != null && rightCompetitor.getKey() != null) { + Competitor left = leftCompetitor.getKey(); + Competitor right = rightCompetitor.getKey(); + + return comparator.compare(left, right); + } + return 0; + } +} diff --git a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/comparators/CompetitorSailIdComparator.java b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/comparators/CompetitorSailIdComparator.java index 27c99015799..3fe01c47f75 100644 --- a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/comparators/CompetitorSailIdComparator.java +++ b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/comparators/CompetitorSailIdComparator.java @@ -1,12 +1,14 @@ package com.sap.sailing.racecommittee.app.ui.comparators; import java.util.Comparator; +import java.util.Map; +import com.sap.sailing.domain.base.Boat; import com.sap.sailing.domain.base.Competitor; import com.sap.sse.common.Util; import com.sap.sse.common.util.NaturalComparator; -public class CompetitorSailIdComparator implements Comparator { +public class CompetitorSailIdComparator implements Comparator> { private Comparator comparator; @@ -15,25 +17,27 @@ public class CompetitorSailIdComparator implements Comparator { } @Override - public int compare(Competitor left, Competitor right) { - // if left is null, right will be greater - if (left == null || left.getBoat() == null || left.getBoat().getSailID() == null) { - return -1; - } + public int compare(Map.Entry leftCompetitor, Map.Entry rightCompetitor) { + if (leftCompetitor != null && leftCompetitor.getValue() != null && + rightCompetitor != null && rightCompetitor.getValue() != null) { + Boat left = leftCompetitor.getValue(); + Boat right = rightCompetitor.getValue(); + + // if right is null, left will be greater + if (right == null || right.getSailID() == null) { + return 1; + } - // if right is null, left will be greater - if (right == null || right.getBoat() == null || right.getBoat().getSailID() == null) { - return 1; + String leftBoat = ""; + for (String lh : Util.splitAlongWhitespaceRespectingDoubleQuotedPhrases(left.getSailID())) { + leftBoat = lh; + } + String rightBoat = ""; + for (String rh : Util.splitAlongWhitespaceRespectingDoubleQuotedPhrases(right.getSailID())) { + rightBoat = rh; + } + return comparator.compare(leftBoat, rightBoat); } - - String leftBoat = ""; - for (String lh : Util.splitAlongWhitespaceRespectingDoubleQuotedPhrases(left.getBoat().getSailID())) { - leftBoat = lh; - } - String rightBoat = ""; - for (String rh : Util.splitAlongWhitespaceRespectingDoubleQuotedPhrases(right.getBoat().getSailID())) { - rightBoat = rh; - } - return comparator.compare(leftBoat, rightBoat); + return 0; } } diff --git a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/raceinfo/PenaltyFragment.java b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/raceinfo/PenaltyFragment.java index 020f9dcb044..1084f89c825 100644 --- a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/raceinfo/PenaltyFragment.java +++ b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/raceinfo/PenaltyFragment.java @@ -3,9 +3,6 @@ package com.sap.sailing.racecommittee.app.ui.fragments.raceinfo; import java.io.Serializable; import java.lang.ref.WeakReference; import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -26,6 +23,7 @@ import com.sap.sailing.domain.abstractlog.race.state.ReadonlyRaceState; import com.sap.sailing.domain.abstractlog.race.state.impl.BaseRaceStateChangedListener; import com.sap.sailing.domain.abstractlog.race.state.racingprocedure.RacingProcedure; import com.sap.sailing.domain.abstractlog.race.state.racingprocedure.line.ConfigurableStartModeFlagRacingProcedure; +import com.sap.sailing.domain.base.Boat; import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.SharedDomainFactory; import com.sap.sailing.domain.common.MaxPointsReason; @@ -37,7 +35,6 @@ import com.sap.sailing.racecommittee.app.data.ReadonlyDataManager; import com.sap.sailing.racecommittee.app.data.clients.LoadClient; import com.sap.sailing.racecommittee.app.domain.impl.CompetitorResultEditableImpl; import com.sap.sailing.racecommittee.app.domain.impl.CompetitorResultWithIdImpl; -import com.sap.sailing.racecommittee.app.domain.impl.CompetitorWithRaceRankImpl; import com.sap.sailing.racecommittee.app.domain.impl.LeaderboardResult; import com.sap.sailing.racecommittee.app.ui.adapters.CompetitorResultsList; import com.sap.sailing.racecommittee.app.ui.adapters.PenaltyAdapter; @@ -205,12 +202,6 @@ public class PenaltyFragment extends BaseFragment implements PopupMenu.OnMenuIte }); } mEntryCount = ViewHelper.get(layout, R.id.competitor_entry_count); - mAdapter = new PenaltyAdapter(getActivity(), this); - RecyclerView recyclerView = ViewHelper.get(layout, R.id.competitor_list); - if (recyclerView != null) { - recyclerView.setLayoutManager(new LinearLayoutManager(getActivity())); - recyclerView.setAdapter(mAdapter); - } return layout; } @@ -231,6 +222,13 @@ public class PenaltyFragment extends BaseFragment implements PopupMenu.OnMenuIte public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); + mAdapter = new PenaltyAdapter(getActivity(), this, getRace().getRaceGroup().canBoatsOfCompetitorsChangePerRace()); + RecyclerView recyclerView = ViewHelper.get(getView(), R.id.competitor_list); + if (recyclerView != null) { + recyclerView.setLayoutManager(new LinearLayoutManager(getActivity())); + recyclerView.setAdapter(mAdapter); + } + if (mPenaltyDropDown != null) { int selection = mPenaltyAdapter.getPosition(MaxPointsReason.OCS.name()); RacingProcedure procedure = getRaceState().getRacingProcedure(); @@ -380,14 +378,14 @@ public class PenaltyFragment extends BaseFragment implements PopupMenu.OnMenuIte domainFactory.getCompetitorStore().allowCompetitorResetToDefaults(competitor); } final Loader competitorLoader = getLoaderManager() - .initLoader(COMPETITOR_LOADER, null, dataManager.createCompetitorsLoader(getRace(), new LoadClient>() { + .initLoader(COMPETITOR_LOADER, null, dataManager.createCompetitorsLoader(getRace(), new LoadClient>() { @Override public void onLoadFailed(Exception reason) { Toast.makeText(getActivity(), getString(R.string.competitor_load_error, reason.toString()), Toast.LENGTH_LONG).show(); } @Override - public void onLoadSucceeded(Collection data, boolean isCached) { + public void onLoadSucceeded(Map data, boolean isCached) { if (isAdded() && !isCached) { onLoadCompetitorsSucceeded(data); } @@ -414,14 +412,14 @@ public class PenaltyFragment extends BaseFragment implements PopupMenu.OnMenuIte leaderboardResultLoader.forceLoad(); } - private void onLoadCompetitorsSucceeded(Collection data) { + private void onLoadCompetitorsSucceeded(Map data) { mCompetitorResults.clear(); - for (Competitor item : data) { // add loaded competitors + for (Competitor item : data.keySet()) { // add loaded competitors String name = ""; - if (item.getBoat() != null) { - name += item.getBoat().getSailID(); + if (item.getShortInfo() != null) { + name += item.getShortInfo() + " - "; } - name += " - " + item.getName(); + name += item.getName(); CompetitorResult result = new CompetitorResultImpl(item.getId(), name, 0, MaxPointsReason.NONE, /* score */ null, /* finishingTime */ null, /* comment */ null, MergeState.OK); mCompetitorResults.add(new CompetitorResultEditableImpl(result)); @@ -439,31 +437,27 @@ public class PenaltyFragment extends BaseFragment implements PopupMenu.OnMenuIte mCompetitorResults.add(pos, new CompetitorResultEditableImpl(result)); } } - mAdapter.setCompetitor(mCompetitorResults); + mAdapter.setCompetitor(mCompetitorResults, data); setPublishButton(); } private void onLoadLeaderboardResultSucceeded(LeaderboardResult data) { final String raceName = getRace().getName(); - List sortByRank = data.getCompetitors(); - Collections.sort(sortByRank, new Comparator() { - @Override - public int compare(CompetitorWithRaceRankImpl left, CompetitorWithRaceRankImpl right) { - return (int) left.getRaceRank(raceName) - (int) right.getRaceRank(raceName); - } - }); + List> sortByRank = data.getResult(raceName); List sortedList = new ArrayList<>(); - for (CompetitorWithRaceRankImpl item : sortByRank) { - for (CompetitorResultEditableImpl competitor : mCompetitorResults) { - if (competitor.getCompetitorId().toString().equals(item.getId())) { - sortedList.add(competitor); - break; + if (sortByRank != null) { + for (Util.Pair item : sortByRank) { + for (CompetitorResultEditableImpl competitor : mCompetitorResults) { + if (competitor.getCompetitorId().toString().equals(item.getB())) { + sortedList.add(competitor); + break; + } } } } mCompetitorResults.clear(); mCompetitorResults.addAll(sortedList); - mAdapter.setCompetitor(mCompetitorResults); + mAdapter.setCompetitor(mCompetitorResults, null); } @Override @@ -672,7 +666,7 @@ public class PenaltyFragment extends BaseFragment implements PopupMenu.OnMenuIte } } competitor.setMergeState(MergeState.OK); - CompetitorResultWithIdImpl item = new CompetitorResultWithIdImpl(0, competitor.getCompetitorId(), competitor + CompetitorResultWithIdImpl item = new CompetitorResultWithIdImpl(0, getBoat(competitor.getCompetitorId()), competitor.getCompetitorId(), competitor .getCompetitorDisplayName(), competitor.getOneBasedRank(), competitor.getMaxPointsReason(), competitor.getScore(), competitor .getFinishingTime(), competitor.getComment(), competitor.getMergeState()); AlertDialog.Builder builder = new AlertDialog.Builder(context, R.style.AppTheme_AlertDialog); @@ -725,6 +719,18 @@ public class PenaltyFragment extends BaseFragment implements PopupMenu.OnMenuIte } } + @Nullable + private Boat getBoat(Serializable competitorId) { + Boat boat = null; + for (Map.Entry entry : getRace().getCompetitorsAndBoats().entrySet()) { + if (entry.getKey().getId().equals(competitorId)) { + boat = entry.getValue(); + break; + } + } + return boat; + } + private CompetitorResultsImpl getCompetitorResultsDiff(CompetitorResults results) { CompetitorResultsImpl result = new CompetitorResultsImpl(); diff --git a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/raceinfo/TrackingListFragment.java b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/raceinfo/TrackingListFragment.java index 25ea2a93083..d9abf3165e0 100644 --- a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/raceinfo/TrackingListFragment.java +++ b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/raceinfo/TrackingListFragment.java @@ -28,6 +28,7 @@ import com.sap.sailing.domain.abstractlog.race.impl.CompetitorResultImpl; import com.sap.sailing.domain.abstractlog.race.impl.CompetitorResultsImpl; import com.sap.sailing.domain.abstractlog.race.state.ReadonlyRaceState; import com.sap.sailing.domain.abstractlog.race.state.impl.BaseRaceStateChangedListener; +import com.sap.sailing.domain.base.Boat; import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.CompetitorStore; import com.sap.sailing.domain.base.SharedDomainFactory; @@ -39,13 +40,12 @@ import com.sap.sailing.racecommittee.app.data.OnlineDataManager; import com.sap.sailing.racecommittee.app.data.ReadonlyDataManager; import com.sap.sailing.racecommittee.app.data.clients.LoadClient; import com.sap.sailing.racecommittee.app.domain.impl.CompetitorResultWithIdImpl; -import com.sap.sailing.racecommittee.app.domain.impl.CompetitorWithRaceRankImpl; import com.sap.sailing.racecommittee.app.domain.impl.LeaderboardResult; -import com.sap.sailing.racecommittee.app.ui.adapters.CompetitorAdapter; +import com.sap.sailing.racecommittee.app.ui.adapters.CompetitorAndBoatAdapter; import com.sap.sailing.racecommittee.app.ui.adapters.CompetitorResultsList; import com.sap.sailing.racecommittee.app.ui.adapters.FinishListAdapter; +import com.sap.sailing.racecommittee.app.ui.comparators.CompetitorNameComparator; import com.sap.sailing.racecommittee.app.ui.comparators.CompetitorSailIdComparator; -import com.sap.sailing.racecommittee.app.ui.comparators.NaturalNamedComparator; import com.sap.sailing.racecommittee.app.ui.fragments.RaceFragment; import com.sap.sailing.racecommittee.app.ui.layouts.CompetitorEditLayout; import com.sap.sailing.racecommittee.app.ui.layouts.HeaderLayout; @@ -56,6 +56,7 @@ import com.sap.sse.common.TimePoint; import com.sap.sse.common.Util; import com.sap.sse.common.impl.MillisecondsTimePoint; +import android.annotation.TargetApi; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; @@ -64,6 +65,7 @@ import android.graphics.drawable.NinePatchDrawable; import android.os.Build; import android.os.Bundle; import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import android.support.v4.content.ContextCompat; import android.support.v7.app.ActionBar; import android.support.v7.app.AlertDialog; @@ -85,7 +87,7 @@ import android.widget.TextView; import android.widget.Toast; public class TrackingListFragment extends BaseFragment - implements CompetitorAdapter.CompetitorClick, FinishListAdapter.FinishEvents, View.OnClickListener, AdapterView.OnItemSelectedListener, + implements CompetitorAndBoatAdapter.CompetitorClick, FinishListAdapter.FinishEvents, View.OnClickListener, AdapterView.OnItemSelectedListener, PopupMenu.OnMenuItemClickListener, SearchView.SearchTextWatcher { private static final int COMPETITOR_LOADER = 0; @@ -103,25 +105,25 @@ public class TrackingListFragment extends BaseFragment private FinishListAdapter mAdapter; private RecyclerView.Adapter mFinishedAdapter; - private CompetitorAdapter mCompetitorAdapter; + private CompetitorAndBoatAdapter mCompetitorAdapter; private CompetitorResultsList mFinishedData; private CompetitorResults mDraftData; private CompetitorResults mConfirmedData; - private List mCompetitorData; - private List mFilteredCompetitorData; + private List> mCompetitorData; + private List> mFilteredCompetitorData; private int mId = 0; private HeaderLayout mHeader; - private Comparator mComparator; - private List> mComparators; + private Comparator> mComparator; + private List>> mComparators; private String mFilter; private View mTools; private StateChangeListener mStateChangeListener; public TrackingListFragment() { - mCompetitorData = Collections.synchronizedList(new ArrayList()); - mFilteredCompetitorData = Collections.synchronizedList(new ArrayList()); + mCompetitorData = Collections.synchronizedList(new ArrayList>()); + mFilteredCompetitorData = Collections.synchronizedList(new ArrayList>()); } public static TrackingListFragment newInstance(Bundle args, int startMode) { @@ -131,6 +133,7 @@ public class TrackingListFragment extends BaseFragment return fragment; } + @TargetApi(Build.VERSION_CODES.KITKAT) @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View layout = inflater.inflate(R.layout.race_tracking_list, container, false); @@ -224,7 +227,7 @@ public class TrackingListFragment extends BaseFragment super.onActivityCreated(savedInstanceState); mComparators = new ArrayList<>(); mComparators.add(SORT_SAIL_NUMBER, new CompetitorSailIdComparator()); - mComparators.add(SORT_NAME, new NaturalNamedComparator()); + mComparators.add(SORT_NAME, new CompetitorNameComparator()); mComparator = mComparators.get(SORT_SAIL_NUMBER); mFinishedData = initializeFinishList(); mConfirmedData = new CompetitorResultsImpl(); @@ -234,7 +237,7 @@ public class TrackingListFragment extends BaseFragment RecyclerView competitorView = (RecyclerView) getView().findViewById(R.id.list_positioning_all); if (competitorView != null) { LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity()); - mCompetitorAdapter = new CompetitorAdapter(getActivity(), mFilteredCompetitorData); + mCompetitorAdapter = new CompetitorAndBoatAdapter(getActivity(), mFilteredCompetitorData, getRace().getRaceGroup().canBoatsOfCompetitorsChangePerRace()); mCompetitorAdapter.setListener(this); competitorView.setLayoutManager(layoutManager); competitorView.setAdapter(mCompetitorAdapter); @@ -250,13 +253,13 @@ public class TrackingListFragment extends BaseFragment mDragDropManager.setDraggingItemShadowDrawable(drawable); mSwipeManager = new RecyclerViewSwipeManager(); LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity()); - mAdapter = new FinishListAdapter(getActivity(), mFinishedData); + mAdapter = new FinishListAdapter(getActivity(), mFinishedData, getRace().getRaceGroup().canBoatsOfCompetitorsChangePerRace()); mAdapter.setListener(this); - @SuppressWarnings("unchecked") - RecyclerView.Adapter dragManager = mDragDropManager.createWrappedAdapter(mAdapter); + @SuppressWarnings("unchecked") RecyclerView.Adapter dragManager = mDragDropManager + .createWrappedAdapter(mAdapter); mFinishedAdapter = dragManager; - @SuppressWarnings("unchecked") - RecyclerView.Adapter swipeManager = mSwipeManager.createWrappedAdapter(mFinishedAdapter); + @SuppressWarnings("unchecked") RecyclerView.Adapter swipeManager = mSwipeManager + .createWrappedAdapter(mFinishedAdapter); mFinishedAdapter = swipeManager; mFinishView.setLayoutManager(layoutManager); mFinishView.setAdapter(mFinishedAdapter); @@ -289,7 +292,7 @@ public class TrackingListFragment extends BaseFragment viewPanel(MOVE_NONE); } } - Util.addAll(getRace().getCompetitors(), mCompetitorData); + Util.addAll(getRace().getCompetitorsAndBoats().entrySet(), mCompetitorData); mFilteredCompetitorData.clear(); mFilteredCompetitorData.addAll(mCompetitorData); sortCompetitors(); @@ -468,7 +471,7 @@ public class TrackingListFragment extends BaseFragment } final Loader competitorLoader = getLoaderManager() - .initLoader(COMPETITOR_LOADER, null, dataManager.createCompetitorsLoader(getRace(), new LoadClient>() { + .initLoader(COMPETITOR_LOADER, null, dataManager.createCompetitorsLoader(getRace(), new LoadClient>() { @Override public void onLoadFailed(Exception reason) { @@ -476,7 +479,7 @@ public class TrackingListFragment extends BaseFragment } @Override - public void onLoadSucceeded(Collection data, boolean isCached) { + public void onLoadSucceeded(Map data, boolean isCached) { if (isAdded() && !isCached) { onLoadCompetitorsSucceeded(data); } @@ -508,32 +511,29 @@ public class TrackingListFragment extends BaseFragment leaderboardResultLoader.forceLoad(); } - protected void onLoadCompetitorsSucceeded(Collection data) { + protected void onLoadCompetitorsSucceeded(Map data) { + Collection competitors = data.keySet(); mCompetitorData.clear(); - mCompetitorData.addAll(data); + Util.addAll(data.entrySet(), mCompetitorData); mFilteredCompetitorData.clear(); - mFilteredCompetitorData.addAll(data); + Util.addAll(data.entrySet(), mFilteredCompetitorData); sortCompetitors(); - deleteCompetitorsFromFinishedList(data); + deleteCompetitorsFromFinishedList(competitors); deleteCompetitorsFromCompetitorList(); mCompetitorAdapter.notifyDataSetChanged(); } protected void onLoadLeaderboardSucceeded(LeaderboardResult data) { final String raceName = getRace().getName(); - List sortByRank = data.getCompetitors(); - Collections.sort(sortByRank, new Comparator() { - @Override - public int compare(CompetitorWithRaceRankImpl left, CompetitorWithRaceRankImpl right) { - return (int) left.getRaceRank(raceName) - (int) right.getRaceRank(raceName); - } - }); - List sortedList = new ArrayList<>(); - for (CompetitorWithRaceRankImpl item : sortByRank) { - for (Competitor competitor : mCompetitorData) { - if (competitor.getId().toString().equals(item.getId())) { - sortedList.add(competitor); - break; + List> sortByRank = data.getResult(raceName); + List> sortedList = new ArrayList<>(); + if (sortByRank != null) { + for (Util.Pair item : sortByRank) { + for (Map.Entry competitor : mCompetitorData) { + if (competitor.getKey().getId().toString().equals(item.getB())) { + sortedList.add(competitor); + break; + } } } } @@ -553,7 +553,9 @@ public class TrackingListFragment extends BaseFragment } if (item.getOneBasedRank() == 0 && item.getMaxPointsReason() == MaxPointsReason.NONE) { toBeDeleted.add(item); + continue; } + item.setBoat(getBoat(item.getCompetitorId())); } mFinishedData.removeAll(toBeDeleted); setPublishButton(); @@ -563,8 +565,12 @@ public class TrackingListFragment extends BaseFragment private void deleteCompetitorsFromCompetitorList() { for (CompetitorResultWithIdImpl item : mFinishedData) { Competitor competitor = getCompetitorStore().getExistingCompetitorById(item.getCompetitorId()); - mCompetitorData.remove(competitor); - mFilteredCompetitorData.remove(competitor); + for (Map.Entry entry : getRace().getCompetitorsAndBoats().entrySet()) { + if (entry.getKey().equals(competitor)) { + mCompetitorData.remove(entry); + mFilteredCompetitorData.remove(entry); + } + } } } @@ -576,8 +582,9 @@ public class TrackingListFragment extends BaseFragment CompetitorResultsList positioning = new CompetitorResultsList<>(Collections .synchronizedList(new ArrayList())); if (getRaceState() != null && getRaceState().getFinishPositioningList() != null) { - for (CompetitorResult results : getRaceState().getFinishPositioningList()) { - positioning.add(new CompetitorResultWithIdImpl(mId, results)); + for (CompetitorResult result : getRaceState().getFinishPositioningList()) { + Boat boat = getBoat(result.getCompetitorId()); + positioning.add(new CompetitorResultWithIdImpl(mId, boat, result)); mId++; } } @@ -585,18 +592,35 @@ public class TrackingListFragment extends BaseFragment return positioning; } + @Nullable + private Boat getBoat(Serializable competitorId) { + Boat boat = null; + for (Map.Entry entry : getRace().getCompetitorsAndBoats().entrySet()) { + if (entry.getKey().getId().equals(competitorId)) { + boat = entry.getValue(); + break; + } + } + return boat; + } + @Override public void onCompetitorClick(Competitor competitor) { moveCompetitorToFinishList(competitor); - removeCompetitorFromList(competitor); + for (Map.Entry entry : getRace().getCompetitorsAndBoats().entrySet()) { + if (entry.getKey().equals(competitor)) { + removeCompetitorFromList(entry); + break; + } + } } private void moveCompetitorToFinishList(Competitor competitor) { String name = ""; - if (competitor.getBoat() != null) { - name += competitor.getBoat().getSailID(); + if (competitor.getShortInfo() != null) { + name += competitor.getShortInfo() + " - "; } - name += " - " + competitor.getName(); + name += competitor.getName(); int pos = mAdapter.getFirstRankZeroPosition(); // FIXME mFinishedData.size()+1 also counts penalized competitors before which the competitor is to be inserted! I just wonder how the position shown in the app seems correct... int greatestOneBasedRankSoFar = 0; @@ -605,7 +629,8 @@ public class TrackingListFragment extends BaseFragment greatestOneBasedRankSoFar = result.getOneBasedRank(); } } - mFinishedData.add(pos, new CompetitorResultWithIdImpl(mId, competitor.getId(), name, greatestOneBasedRankSoFar + 1, MaxPointsReason.NONE, + mFinishedData.add(pos, new CompetitorResultWithIdImpl(mId, getBoat(competitor.getId()), competitor.getId(), name, + greatestOneBasedRankSoFar + 1, MaxPointsReason.NONE, /* score */ null, /* finishingTime */ null, /* comment */ null, MergeState.OK)); mId++; setPublishButton(); @@ -615,10 +640,10 @@ public class TrackingListFragment extends BaseFragment } } - private void removeCompetitorFromList(Competitor competitor) { - mCompetitorData.remove(competitor); - int pos = mFilteredCompetitorData.indexOf(competitor); - mFilteredCompetitorData.remove(competitor); + private void removeCompetitorFromList(Map.Entry entry) { + mCompetitorData.remove(entry); + int pos = mFilteredCompetitorData.indexOf(entry); + mFilteredCompetitorData.remove(entry); sortCompetitors(); mCompetitorAdapter.notifyItemRemoved(pos); } @@ -662,9 +687,11 @@ public class TrackingListFragment extends BaseFragment setPublishButton(); mFinishedAdapter.notifyItemRemoved(position); } - Competitor competitor = getCompetitorStore().getExistingCompetitorById(item.getCompetitorId()); - if (competitor != null) { - addNewCompetitorToCompetitorList(competitor); + for (Map.Entry entry : getRace().getCompetitorsAndBoats().entrySet()) { + if (entry.getKey().getId().equals(item.getCompetitorId())) { + addNewCompetitorToCompetitorList(entry); + break; + } } } @@ -687,18 +714,18 @@ public class TrackingListFragment extends BaseFragment private CompetitorResultWithIdImpl cloneCompetitorResultAndAdjustRank(CompetitorResultWithIdImpl competitorToReplaceWithAdjustedPosition, final int newOneBasedRank) { - return new CompetitorResultWithIdImpl(competitorToReplaceWithAdjustedPosition.getId(), competitorToReplaceWithAdjustedPosition - .getCompetitorId(), competitorToReplaceWithAdjustedPosition + return new CompetitorResultWithIdImpl(competitorToReplaceWithAdjustedPosition.getId(), getBoat(competitorToReplaceWithAdjustedPosition + .getCompetitorId()), competitorToReplaceWithAdjustedPosition.getCompetitorId(), competitorToReplaceWithAdjustedPosition .getCompetitorDisplayName(), newOneBasedRank, competitorToReplaceWithAdjustedPosition .getMaxPointsReason(), competitorToReplaceWithAdjustedPosition.getScore(), competitorToReplaceWithAdjustedPosition .getFinishingTime(), competitorToReplaceWithAdjustedPosition.getComment(), MergeState.OK); } - private void addNewCompetitorToCompetitorList(Competitor competitor) { - mCompetitorData.add(competitor); - mFilteredCompetitorData.add(competitor); + private void addNewCompetitorToCompetitorList(Map.Entry entry) { + mCompetitorData.add(entry); + mFilteredCompetitorData.add(entry); sortCompetitors(); - mCompetitorAdapter.notifyItemInserted(mFilteredCompetitorData.indexOf(competitor)); + mCompetitorAdapter.notifyItemInserted(mFilteredCompetitorData.indexOf(entry)); } @Override @@ -748,9 +775,9 @@ public class TrackingListFragment extends BaseFragment builder.setOnCancelListener(new DialogInterface.OnCancelListener() { @Override public void onCancel(DialogInterface dialog) { - CompetitorResultWithIdImpl newItem = new CompetitorResultWithIdImpl(item.getId(), item.getCompetitorId(), item - .getCompetitorDisplayName(), item.getOneBasedRank(), item.getMaxPointsReason(), item.getScore(), item.getFinishingTime(), item - .getComment(), MergeState.OK); + CompetitorResultWithIdImpl newItem = new CompetitorResultWithIdImpl(item.getId(), getBoat(item.getCompetitorId()), item + .getCompetitorId(), item.getCompetitorDisplayName(), item.getOneBasedRank(), item.getMaxPointsReason(), item.getScore(), item + .getFinishingTime(), item.getComment(), MergeState.OK); updateItem(item, newItem); } }); @@ -810,9 +837,9 @@ public class TrackingListFragment extends BaseFragment protected void setMaxPointsReasonForItem(CompetitorResultWithIdImpl item, CharSequence maxPointsReasonName) { MaxPointsReason maxPointsReason = MaxPointsReason.valueOf(maxPointsReasonName.toString()); - CompetitorResultWithIdImpl newItem = new CompetitorResultWithIdImpl(item.getId(), item.getCompetitorId(), item - .getCompetitorDisplayName(), item.getOneBasedRank(), maxPointsReason, item.getScore(), item.getFinishingTime(), item.getComment(), item - .getMergeState()); + CompetitorResultWithIdImpl newItem = new CompetitorResultWithIdImpl(item.getId(), getBoat(item.getCompetitorId()), item + .getCompetitorId(), item.getCompetitorDisplayName(), item.getOneBasedRank(), maxPointsReason, item.getScore(), item + .getFinishingTime(), item.getComment(), item.getMergeState()); updateItem(item, newItem); getRaceState().setFinishPositioningListChanged(MillisecondsTimePoint.now(), getCompetitorResults()); } @@ -939,14 +966,14 @@ public class TrackingListFragment extends BaseFragment if (TextUtils.isEmpty(mFilter)) { mFilteredCompetitorData.addAll(mCompetitorData); } else { - for (Competitor competitor : mCompetitorData) { + for (Map.Entry entry : mCompetitorData) { String name = ""; - if (competitor.getBoat() != null) { - name += competitor.getBoat().getSailID(); + if (entry.getKey().getShortInfo() != null) { + name += entry.getKey().getShortInfo() + " - "; } - name += " - " + competitor.getName(); + name += entry.getKey().getName(); if (StringHelper.on(getActivity()).containsIgnoreCase(name, mFilter)) { - mFilteredCompetitorData.add(competitor); + mFilteredCompetitorData.add(entry); } } } @@ -973,6 +1000,7 @@ public class TrackingListFragment extends BaseFragment } } if (item != null) { // result is in list + Boat boat = getBoat(item.getCompetitorId()); // check one based rank if (item.getOneBasedRank() != result.getOneBasedRank()) { if (draft != null) { @@ -984,9 +1012,9 @@ public class TrackingListFragment extends BaseFragment } else { state = MergeState.ERROR; } - newItem = new CompetitorResultWithIdImpl(item.getId(), item.getCompetitorId(), item.getCompetitorDisplayName(), result - .getOneBasedRank(), item.getMaxPointsReason(), item.getScore(), item.getFinishingTime(), item - .getComment(), getMergeState(item, state)); + newItem = new CompetitorResultWithIdImpl(item.getId(), boat, item.getCompetitorId(), item + .getCompetitorDisplayName(), result.getOneBasedRank(), item.getMaxPointsReason(), item.getScore(), item + .getFinishingTime(), item.getComment(), getMergeState(item, state)); item = updateChangedItem(changedCompetitor, item, newItem); } @@ -1001,9 +1029,9 @@ public class TrackingListFragment extends BaseFragment } else { state = MergeState.ERROR; } - newItem = new CompetitorResultWithIdImpl(item.getId(), item.getCompetitorId(), item.getCompetitorDisplayName(), item - .getOneBasedRank(), result.getMaxPointsReason(), item.getScore(), item.getFinishingTime(), item - .getComment(), getMergeState(item, state)); + newItem = new CompetitorResultWithIdImpl(item.getId(), boat, item.getCompetitorId(), item + .getCompetitorDisplayName(), item.getOneBasedRank(), result.getMaxPointsReason(), item.getScore(), item + .getFinishingTime(), item.getComment(), getMergeState(item, state)); item = updateChangedItem(changedCompetitor, item, newItem); } @@ -1019,15 +1047,15 @@ public class TrackingListFragment extends BaseFragment } else { state = MergeState.ERROR; } - newItem = new CompetitorResultWithIdImpl(item.getId(), item.getCompetitorId(), item.getCompetitorDisplayName(), item - .getOneBasedRank(), item.getMaxPointsReason(), result.getScore(), item.getFinishingTime(), item - .getComment(), getMergeState(item, state)); + newItem = new CompetitorResultWithIdImpl(item.getId(), boat, item.getCompetitorId(), item + .getCompetitorDisplayName(), item.getOneBasedRank(), item.getMaxPointsReason(), result.getScore(), item + .getFinishingTime(), item.getComment(), getMergeState(item, state)); item = updateChangedItem(changedCompetitor, item, newItem); } } else if (result.getScore() != null) { - newItem = new CompetitorResultWithIdImpl(item.getId(), item.getCompetitorId(), item.getCompetitorDisplayName(), item - .getOneBasedRank(), item.getMaxPointsReason(), result.getScore(), item.getFinishingTime(), item - .getComment(), getMergeState(item, MergeState.ERROR)); + newItem = new CompetitorResultWithIdImpl(item.getId(), boat, item.getCompetitorId(), item + .getCompetitorDisplayName(), item.getOneBasedRank(), item.getMaxPointsReason(), result.getScore(), item + .getFinishingTime(), item.getComment(), getMergeState(item, MergeState.ERROR)); item = updateChangedItem(changedCompetitor, item, newItem); } @@ -1043,13 +1071,13 @@ public class TrackingListFragment extends BaseFragment } else { state = MergeState.ERROR; } - newItem = new CompetitorResultWithIdImpl(item.getId(), item.getCompetitorId(), item.getCompetitorDisplayName(), item + newItem = new CompetitorResultWithIdImpl(item.getId(), boat, item.getCompetitorId(), item.getCompetitorDisplayName(), item .getOneBasedRank(), item.getMaxPointsReason(), item.getScore(), result.getFinishingTime(), item .getComment(), getMergeState(item, state)); item = updateChangedItem(changedCompetitor, item, newItem); } } else if (result.getFinishingTime() != null) { - newItem = new CompetitorResultWithIdImpl(item.getId(), item.getCompetitorId(), item.getCompetitorDisplayName(), item + newItem = new CompetitorResultWithIdImpl(item.getId(), boat, item.getCompetitorId(), item.getCompetitorDisplayName(), item .getOneBasedRank(), item.getMaxPointsReason(), item.getScore(), result.getFinishingTime(), item .getComment(), getMergeState(item, MergeState.ERROR)); item = updateChangedItem(changedCompetitor, item, newItem); @@ -1067,13 +1095,13 @@ public class TrackingListFragment extends BaseFragment } else { state = MergeState.ERROR; } - newItem = new CompetitorResultWithIdImpl(item.getId(), item.getCompetitorId(), item.getCompetitorDisplayName(), item + newItem = new CompetitorResultWithIdImpl(item.getId(), boat, item.getCompetitorId(), item.getCompetitorDisplayName(), item .getOneBasedRank(), item.getMaxPointsReason(), item.getScore(), item.getFinishingTime(), item.getComment() + " ## " + result.getComment(), getMergeState(item, state)); item = updateChangedItem(changedCompetitor, item, newItem); } } else if (result.getComment() != null) { - newItem = new CompetitorResultWithIdImpl(item.getId(), item.getCompetitorId(), item.getCompetitorDisplayName(), item + newItem = new CompetitorResultWithIdImpl(item.getId(), boat, item.getCompetitorId(), item.getCompetitorDisplayName(), item .getOneBasedRank(), item.getMaxPointsReason(), item.getScore(), item.getFinishingTime(), result .getComment(), getMergeState(item, MergeState.ERROR)); item = updateChangedItem(changedCompetitor, item, newItem); @@ -1081,20 +1109,20 @@ public class TrackingListFragment extends BaseFragment // check merge state if (!item.getMergeState().equals(result.getMergeState())) { - newItem = new CompetitorResultWithIdImpl(item.getId(), item.getCompetitorId(), item.getCompetitorDisplayName(), item + newItem = new CompetitorResultWithIdImpl(item.getId(), boat, item.getCompetitorId(), item.getCompetitorDisplayName(), item .getOneBasedRank(), item.getMaxPointsReason(), item.getScore(), item.getFinishingTime(), item .getComment(), getMergeState(item, result.getMergeState())); item = updateChangedItem(changedCompetitor, item, newItem); } } else { // unknown result, so it will be added if (result.getOneBasedRank() != 0 || result.getMaxPointsReason() != MaxPointsReason.NONE) { - for (Competitor competitor : mCompetitorData) { - if (competitor.getId().equals(result.getCompetitorId())) { - removeCompetitorFromList(competitor); + for (Map.Entry entry : mCompetitorData) { + if (entry.getKey().getId().equals(result.getCompetitorId())) { + removeCompetitorFromList(entry); break; } } - mFinishedData.add(new CompetitorResultWithIdImpl(mFinishedData.size(), result)); + mFinishedData.add(new CompetitorResultWithIdImpl(mFinishedData.size(), getBoat(result.getCompetitorId()), result)); Collections.sort(mFinishedData, new DefaultCompetitorResultComparator(/* lowPoint TODO where to get this from? */ true)); } } diff --git a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/layouts/CompetitorEditLayout.java b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/layouts/CompetitorEditLayout.java index 97fc67436ba..ddb4a83ee84 100644 --- a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/layouts/CompetitorEditLayout.java +++ b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/layouts/CompetitorEditLayout.java @@ -221,7 +221,7 @@ public class CompetitorEditLayout extends ScrollView implements DatePickerDialog } CompetitorResult result = new CompetitorResultImpl(mCompetitor.getCompetitorId(), mCompetitor .getCompetitorDisplayName(), oneBaseRank, maxPointsReason, score, finishingTime, comment, MergeState.OK); - return new CompetitorResultWithIdImpl(mCompetitor.getId(), result); + return new CompetitorResultWithIdImpl(mCompetitor.getId(), mCompetitor.getBoat(), result); } @Override diff --git a/wiki/info/api/api-v1.md b/wiki/info/api/api-v1.md index 20d644d48d0..02b12dc454d 100644 --- a/wiki/info/api/api-v1.md +++ b/wiki/info/api/api-v1.md @@ -47,14 +47,32 @@ These resources have separate life cycles, the hierarchical presentation only de The app has to receive the following information: * ``event-id`` (for description, start- and end-date) * ``leaderboard-name`` (so that the user can checkin for the correct leaderboard) -* ``competitor-id`` (so the app can register the device for the correct competitor) +* either of + * ``competitor-id`` (so the app can register the device for the correct competitor) + * ``boat-id`` (so the app can register the device for the correct boat) + * ``mark-id`` (so the app can register the device for the correct mark) + +This information is represented in a URL with one of the following structures: -This information is represented in a URL with the following structure: ``` http:///tracking/checkin ?event_id= - &leaderboardName= - &competitorId= + &leaderboard_name= + &competitor_id= +``` + +``` +http:///tracking/checkin + ?event_id= + &leaderboard_name= + &boat_id= +``` + +``` +http:///tracking/checkin + ?event_id= + &leaderboard_name= + &mark_id= ``` **Additional Notes:** @@ -133,6 +151,7 @@ _Question: What should be done if a second device is mapped to competitor?_ **Verb:** ``POST`` **Request:** + ``` { "competitorId" : "af855a56-9726-4a9c-a77e-da955bd289bg", @@ -142,7 +161,31 @@ _Question: What should be done if a second device is mapped to competitor?_ "fromMillis" : 1415624564718 } ``` -* **competitorId** ID of the competitor to map the device to. For coming versions, using ``markId`` instead might also be valid to map the device to a mark. + +``` +{ + "boatId" : "af855a56-9726-4a9c-a77e-da955bd289bg", + "deviceType" : "ios", + "deviceUuid" : "af855a56-9726-4a9c-a77e-da955bd289bf", + "pushDeviceId" : "0f744707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bbad78", + "fromMillis" : 1415624564718 +} +``` + +``` +{ + "markId" : "af855a56-9726-4a9c-a77e-da955bd289bg", + "deviceType" : "ios", + "deviceUuid" : "af855a56-9726-4a9c-a77e-da955bd289bf", + "pushDeviceId" : "0f744707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bbad78", + "fromMillis" : 1415624564718 +} +``` + +* either of: + * **competitorId** ID of the competitor to map the device to. + * **boatId** ID of the boat to map the device to. + * **markId** ID of the boat to map the device to. * **deviceType**: Either `android` or `ios`. Needed for selecting correct push notification service. * **deviceUuid**: **UUID** generated by the app on first run. **Not** an identifier that globally identifies the piece of hardware such UDID under iOS, IMEI, or other. * **pushDeviceId** On Android the GCM regId, on iOS the APNS push token. This is needed for sending push notifications to the device.