mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-11-09 20:41:10 +00:00
Merge branch 'master' into bug4006
This commit is contained in:
commit
e78fa7ce12
415 files changed
+20757
-806
No files matched your search
+4
-5
@@ -41,16 +41,15 @@ import java.io.Serializable;
|
||||
*/
|
||||
public interface DeviceIdentifier extends Serializable {
|
||||
/**
|
||||
* The returned {@link String} is used to look up corresponding services for serialization
|
||||
* and persistence.
|
||||
* The returned {@link String} is used to look up corresponding services for serialization and persistence.
|
||||
*
|
||||
* The reason for this design choice is that in future, third parties could easily write their own adapter and only
|
||||
* have to register new OSGi service, and not touch the SAP Sailing Analytics code.
|
||||
* The reason for this design choice is that in the future third parties could easily write their own adapter and
|
||||
* only have to register a new OSGi service, and not touch the SAP Sailing Analytics code.
|
||||
*/
|
||||
String getIdentifierType();
|
||||
|
||||
/**
|
||||
* Create a string representation, that can identify this device.
|
||||
* Create a string representation that can identify this device.
|
||||
* The returned values should be unique for this identifier within its {@link #getIdentifierType() type},
|
||||
* but need not include the {@link #getIdentifierType() type} itself in the representation.
|
||||
*/
|
||||
|
||||
+2
-2
@@ -63,7 +63,7 @@ public class RegattaNameAndRaceName extends RegattaName implements RegattaAndRac
|
||||
|
||||
@Override
|
||||
public QualifiedObjectIdentifier getIdentifier() {
|
||||
return getType().getQualifiedObjectIdentifier(getTypeRelativeObjectIdentifier());
|
||||
return getPermissionType().getQualifiedObjectIdentifier(getTypeRelativeObjectIdentifier());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -76,7 +76,7 @@ public class RegattaNameAndRaceName extends RegattaName implements RegattaAndRac
|
||||
}
|
||||
|
||||
@Override
|
||||
public HasPermissions getType() {
|
||||
public HasPermissions getPermissionType() {
|
||||
return SecuredDomainType.TRACKED_RACE;
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -122,13 +122,13 @@ public class BoatDTO extends NamedSecuredObjectDTO implements WithID, Serializab
|
||||
}
|
||||
|
||||
@Override
|
||||
public HasPermissions getType() {
|
||||
public HasPermissions getPermissionType() {
|
||||
return SecuredDomainType.BOAT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public QualifiedObjectIdentifier getIdentifier() {
|
||||
return getType().getQualifiedObjectIdentifier(getTypeRelativeObjectIdentifier());
|
||||
return getPermissionType().getQualifiedObjectIdentifier(getTypeRelativeObjectIdentifier());
|
||||
}
|
||||
|
||||
public TypeRelativeObjectIdentifier getTypeRelativeObjectIdentifier() {
|
||||
|
||||
+2
-2
@@ -247,13 +247,13 @@ public class CompetitorDTOImpl extends NamedSecuredObjectDTO implements Competit
|
||||
}
|
||||
|
||||
@Override
|
||||
public HasPermissions getType() {
|
||||
public HasPermissions getPermissionType() {
|
||||
return SecuredDomainType.COMPETITOR;
|
||||
}
|
||||
|
||||
@Override
|
||||
public QualifiedObjectIdentifier getIdentifier() {
|
||||
return getType().getQualifiedObjectIdentifier(getTypeRelativeObjectIdentifier());
|
||||
return getPermissionType().getQualifiedObjectIdentifier(getTypeRelativeObjectIdentifier());
|
||||
}
|
||||
|
||||
public TypeRelativeObjectIdentifier getTypeRelativeObjectIdentifier() {
|
||||
|
||||
+2
-2
@@ -174,13 +174,13 @@ public class PreviousCompetitorDTOImpl extends NamedSecuredObjectDTO implements
|
||||
}
|
||||
|
||||
@Override
|
||||
public HasPermissions getType() {
|
||||
public HasPermissions getPermissionType() {
|
||||
return SecuredDomainType.COMPETITOR;
|
||||
}
|
||||
|
||||
@Override
|
||||
public QualifiedObjectIdentifier getIdentifier() {
|
||||
return getType().getQualifiedObjectIdentifier(getTypeRelativeObjectIdentifier());
|
||||
return getPermissionType().getQualifiedObjectIdentifier(getTypeRelativeObjectIdentifier());
|
||||
}
|
||||
|
||||
public TypeRelativeObjectIdentifier getTypeRelativeObjectIdentifier() {
|
||||
|
||||
+2
-2
@@ -129,13 +129,13 @@ public class RaceDTO extends BasicRaceDTO implements SecuredDTO {
|
||||
}
|
||||
|
||||
@Override
|
||||
public HasPermissions getType() {
|
||||
public HasPermissions getPermissionType() {
|
||||
return SecuredDomainType.TRACKED_RACE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public QualifiedObjectIdentifier getIdentifier() {
|
||||
return getType().getQualifiedObjectIdentifier(getTypeRelativeObjectIdentifier());
|
||||
return getPermissionType().getQualifiedObjectIdentifier(getTypeRelativeObjectIdentifier());
|
||||
}
|
||||
|
||||
public TypeRelativeObjectIdentifier getTypeRelativeObjectIdentifier() {
|
||||
|
||||
+2
-2
@@ -171,11 +171,11 @@ public class MediaTrack implements Serializable, WithQualifiedObjectIdentifier {
|
||||
|
||||
@Override
|
||||
public QualifiedObjectIdentifier getIdentifier() {
|
||||
return getType().getQualifiedObjectIdentifier(getTypeRelativeObjectIdentifier());
|
||||
return getPermissionType().getQualifiedObjectIdentifier(getTypeRelativeObjectIdentifier());
|
||||
}
|
||||
|
||||
@Override
|
||||
public HasPermissions getType() {
|
||||
public HasPermissions getPermissionType() {
|
||||
return SecuredDomainType.MEDIA_TRACK;
|
||||
}
|
||||
|
||||
|
||||
+4
@@ -88,4 +88,8 @@ public class SecuredDomainType extends HasPermissionsImpl {
|
||||
"SWISS_TIMING_ARCHIVE_ACCOUNT");
|
||||
public static final HasPermissions TRACTRAC_ACCOUNT = new SecuredDomainType("TRACTRAC_ACCOUNT");
|
||||
public static final HasPermissions WIND_ESTIMATION_MODELS = new SecuredDomainType("WIND_ESTIMATION_MODELS");
|
||||
public static final HasPermissions MARK_PROPERTIES = new SecuredDomainType("MARK_PROPERTIES");
|
||||
public static final HasPermissions MARK_TEMPLATE = new SecuredDomainType("MARK_TEMPLATE");
|
||||
public static final HasPermissions COURSE_TEMPLATE = new SecuredDomainType("COURSE_TEMPLATE");
|
||||
public static final HasPermissions MARK_ROLE = new SecuredDomainType("MARK_ROLE");
|
||||
}
|
||||
+2
-2
@@ -15,7 +15,7 @@ public interface Account extends WithQualifiedObjectIdentifier {
|
||||
String getCreatorName();
|
||||
|
||||
@Override
|
||||
default HasPermissions getType() {
|
||||
default HasPermissions getPermissionType() {
|
||||
return SecuredDomainType.IGTIMI_ACCOUNT;
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ public interface Account extends WithQualifiedObjectIdentifier {
|
||||
|
||||
@Override
|
||||
default QualifiedObjectIdentifier getIdentifier() {
|
||||
return getType().getQualifiedObjectIdentifier(getTypeRelativeObjectIdentifier());
|
||||
return getPermissionType().getQualifiedObjectIdentifier(getTypeRelativeObjectIdentifier());
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -16,7 +16,8 @@ Require-Bundle: com.sap.sse.mongodb,
|
||||
com.sap.sailing.server.gateway.serialization.shared.android,
|
||||
com.sap.sse.shared.android,
|
||||
org.mongodb.mongo-java-driver;bundle-version="3.6.4",
|
||||
com.sap.sse.security.common
|
||||
com.sap.sse.security.common,
|
||||
com.sap.sailing.shared.persistence
|
||||
Export-Package: com.sap.sailing.domain.persistence,
|
||||
com.sap.sailing.domain.persistence.impl;
|
||||
x-friends:="com.sap.sailing.domain.persistence.test,
|
||||
|
||||
Executable → Regular
File mode changed.
Executable → Regular
File mode changed.
+2
-1
@@ -120,5 +120,6 @@ public enum CollectionNames {
|
||||
/**
|
||||
* Contains the known anniversaries
|
||||
*/
|
||||
ANNIVERSARIES
|
||||
ANNIVERSARIES;
|
||||
|
||||
}
|
||||
+52
-40
@@ -1,6 +1,8 @@
|
||||
package com.sap.sailing.domain.persistence.impl;
|
||||
|
||||
import static com.mongodb.client.model.Filters.eq;
|
||||
import static com.sap.sailing.shared.persistence.impl.DomainObjectFactoryImpl.loadDeviceId;
|
||||
import static com.sap.sailing.shared.persistence.impl.DomainObjectFactoryImpl.loadPosition;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.net.MalformedURLException;
|
||||
@@ -187,7 +189,6 @@ import com.sap.sailing.domain.common.DeviceIdentifier;
|
||||
import com.sap.sailing.domain.common.MarkType;
|
||||
import com.sap.sailing.domain.common.MaxPointsReason;
|
||||
import com.sap.sailing.domain.common.PassingInstruction;
|
||||
import com.sap.sailing.domain.common.Position;
|
||||
import com.sap.sailing.domain.common.RaceIdentifier;
|
||||
import com.sap.sailing.domain.common.RankingMetrics;
|
||||
import com.sap.sailing.domain.common.RegattaName;
|
||||
@@ -199,7 +200,6 @@ import com.sap.sailing.domain.common.WindSource;
|
||||
import com.sap.sailing.domain.common.WindSourceType;
|
||||
import com.sap.sailing.domain.common.dto.AnniversaryType;
|
||||
import com.sap.sailing.domain.common.dto.EventType;
|
||||
import com.sap.sailing.domain.common.impl.DegreePosition;
|
||||
import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl;
|
||||
import com.sap.sailing.domain.common.impl.NauticalMileDistance;
|
||||
import com.sap.sailing.domain.common.impl.WindImpl;
|
||||
@@ -211,7 +211,6 @@ import com.sap.sailing.domain.common.orc.ORCPerformanceCurveLegTypes;
|
||||
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;
|
||||
@@ -235,11 +234,8 @@ import com.sap.sailing.domain.leaderboard.meta.LeaderboardGroupMetaLeaderboard;
|
||||
import com.sap.sailing.domain.persistence.DomainObjectFactory;
|
||||
import com.sap.sailing.domain.persistence.MongoRaceLogStoreFactory;
|
||||
import com.sap.sailing.domain.persistence.MongoRegattaLogStoreFactory;
|
||||
import com.sap.sailing.domain.persistence.racelog.tracking.DeviceIdentifierMongoHandler;
|
||||
import com.sap.sailing.domain.persistence.racelog.tracking.impl.PlaceHolderDeviceIdentifierMongoHandler;
|
||||
import com.sap.sailing.domain.racelog.RaceLogIdentifier;
|
||||
import com.sap.sailing.domain.racelog.RaceLogStore;
|
||||
import com.sap.sailing.domain.racelogtracking.impl.PlaceHolderDeviceIdentifierSerializationHandler;
|
||||
import com.sap.sailing.domain.ranking.OneDesignRankingMetric;
|
||||
import com.sap.sailing.domain.ranking.RankingMetricConstructor;
|
||||
import com.sap.sailing.domain.ranking.RankingMetricsFactory;
|
||||
@@ -261,11 +257,12 @@ import com.sap.sailing.server.gateway.deserialization.impl.RegattaConfigurationJ
|
||||
import com.sap.sailing.server.gateway.deserialization.racelog.impl.ImpliedWindSourceDeserializer;
|
||||
import com.sap.sailing.server.gateway.deserialization.racelog.impl.ORCCertificateJsonDeserializer;
|
||||
import com.sap.sailing.server.gateway.serialization.impl.DeviceConfigurationJsonSerializer;
|
||||
import com.sap.sailing.shared.persistence.device.DeviceIdentifierMongoHandler;
|
||||
import com.sap.sailing.shared.persistence.device.impl.PlaceHolderDeviceIdentifierMongoHandler;
|
||||
import com.sap.sse.common.Bearing;
|
||||
import com.sap.sse.common.Color;
|
||||
import com.sap.sse.common.Distance;
|
||||
import com.sap.sse.common.Duration;
|
||||
import com.sap.sse.common.NoCorrespondingServiceRegisteredException;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.common.TimeRange;
|
||||
import com.sap.sse.common.TypeBasedServiceFinder;
|
||||
@@ -339,18 +336,6 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
|
||||
return new WindImpl(loadPosition(object), loadTimePoint(object), loadSpeedWithBearing(object));
|
||||
}
|
||||
|
||||
public Position loadPosition(Document object) {
|
||||
Number latNumber = (Number) object.get(FieldNames.LAT_DEG.name());
|
||||
Double lat = latNumber == null ? null : latNumber.doubleValue();
|
||||
Number lngNumber = (Number) object.get(FieldNames.LNG_DEG.name());
|
||||
Double lng = lngNumber == null ? null : lngNumber.doubleValue();
|
||||
if (lat != null && lng != null) {
|
||||
return new DegreePosition(lat, lng);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static TimePoint loadTimePoint(Document object, String fieldName) {
|
||||
TimePoint result = null;
|
||||
Number timePointAsNumber = (Number) object.get(fieldName);
|
||||
@@ -1913,8 +1898,34 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
|
||||
AbstractLogEventAuthor author, TimePoint logicalTimePoint, Serializable id, Integer passId,
|
||||
List<Competitor> competitors, Document dbObject) {
|
||||
String courseName = (String) dbObject.get(FieldNames.RACE_LOG_COURSE_DESIGN_NAME.name());
|
||||
UUID courseOriginatingTemplateId = (UUID) dbObject.get(FieldNames.RACE_LOG_COURSE_ORIGINATING_TEMPLATE_ID.name());
|
||||
Pair<CourseBase, Boolean> courseData = loadCourseData((Iterable<?>) dbObject.get(FieldNames.RACE_LOG_COURSE_DESIGN.name()),
|
||||
courseName);
|
||||
courseName, courseOriginatingTemplateId);
|
||||
|
||||
// load associated roles
|
||||
final ArrayList<?> markList = dbObject.get(FieldNames.RACE_LOG_COURSE_ASSOCIATED_ROLES.name(), ArrayList.class);
|
||||
if (markList != null) {
|
||||
for (final Object entry : markList) {
|
||||
if (entry instanceof Document) {
|
||||
final Document entryObject = (Document) entry;
|
||||
final UUID markUUID = UUID.fromString(
|
||||
entryObject.getString(FieldNames.RACE_LOG_COURSE_ASSOCIATED_ROLES_MARK_ID.name()));
|
||||
final Mark mark = baseDomainFactory.getExistingMarkById(markUUID);
|
||||
if (mark != null) {
|
||||
final String roleIdAsStringOrNull = entryObject
|
||||
.getString(FieldNames.RACE_LOG_COURSE_ASSOCIATED_ROLES_ROLE_ID.name());
|
||||
if (roleIdAsStringOrNull != null) {
|
||||
courseData.getA().addRoleMapping(mark, UUID.fromString(roleIdAsStringOrNull));
|
||||
}
|
||||
} else {
|
||||
logger.warning(String.format("Could not resolve mark with id %s for course %s.", markUUID, id));
|
||||
}
|
||||
} else {
|
||||
logger.warning(String.format("Unexpected mark entry found for course %s.", id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final String courseDesignerModeName = (String) dbObject.get(FieldNames.RACE_LOG_COURSE_DESIGNER_MODE.name());
|
||||
final CourseDesignerMode courseDesignerMode = courseDesignerModeName == null ? null
|
||||
: CourseDesignerMode.valueOf(courseDesignerModeName);
|
||||
@@ -2450,12 +2461,13 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
|
||||
* been edited "in place" to describe the migration that has happened
|
||||
*/
|
||||
@SuppressWarnings("deprecation") // Used to migrate from PASSINGSIDE to the new PASSINGINSTRUCTIONS
|
||||
private Pair<CourseBase, Boolean> loadCourseData(Iterable<?> dbCourseList, String courseName) {
|
||||
private Pair<CourseBase, Boolean> loadCourseData(Iterable<?> dbCourseList, String courseName,
|
||||
UUID originatingCourseTemplateId) {
|
||||
boolean migrated = false;
|
||||
if (courseName == null) {
|
||||
courseName = "Course";
|
||||
}
|
||||
CourseBase courseData = new CourseDataImpl(courseName);
|
||||
CourseBase courseData = new CourseDataImpl(courseName, originatingCourseTemplateId);
|
||||
int i = 0;
|
||||
for (Object object : dbCourseList) {
|
||||
Document dbObject = (Document) object;
|
||||
@@ -2557,8 +2569,13 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
|
||||
dbObject.remove(FieldNames.GATE_RIGHT.name());
|
||||
}
|
||||
Mark rightMark = loadMark(dbRight);
|
||||
String shortName = (String) dbObject.get(FieldNames.CONTROLPOINTWITHTWOMARKS_SHORT_NAME.name());
|
||||
|
||||
if (shortName == null || shortName.isEmpty()) {
|
||||
shortName = controlPointName;
|
||||
}
|
||||
ControlPointWithTwoMarks gate = baseDomainFactory.createControlPointWithTwoMarks(controlPointId, leftMark,
|
||||
rightMark, controlPointName);
|
||||
rightMark, controlPointName, shortName);
|
||||
return gate;
|
||||
}
|
||||
|
||||
@@ -2567,12 +2584,21 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
|
||||
String markColorAsString = (String) dbObject.get(FieldNames.MARK_COLOR.name());
|
||||
Color markColor = AbstractColor.getCssColor(markColorAsString);
|
||||
String markName = (String) dbObject.get(FieldNames.MARK_NAME.name());
|
||||
String markShortName = (String) dbObject.get(FieldNames.MARK_SHORT_NAME.name());
|
||||
String markPattern = (String) dbObject.get(FieldNames.MARK_PATTERN.name());
|
||||
String markShape = (String) dbObject.get(FieldNames.MARK_SHAPE.name());
|
||||
Object markTypeRaw = dbObject.get(FieldNames.MARK_TYPE.name());
|
||||
Object originatingMarkTemplateIdObject = dbObject.get(FieldNames.MARK_ORIGINATING_MARK_TEMPLATE_ID.name());
|
||||
UUID originatingMarkTemplateId = originatingMarkTemplateIdObject == null ? null
|
||||
: UUID.fromString(originatingMarkTemplateIdObject.toString());
|
||||
Object originatingMarkPropertiesIdObject = dbObject
|
||||
.get(FieldNames.MARK_ORIGINATING_MARK_PROPERTIES_ID.name());
|
||||
UUID originatingMarkPropertiesId = originatingMarkPropertiesIdObject == null ? null
|
||||
: UUID.fromString(originatingMarkPropertiesIdObject.toString());
|
||||
MarkType markType = markTypeRaw == null ? null : MarkType.valueOf((String) markTypeRaw);
|
||||
|
||||
Mark mark = baseDomainFactory.getOrCreateMark(markId, markName, markType, markColor, markShape, markPattern);
|
||||
Mark mark = baseDomainFactory.getOrCreateMark(markId, markName, markShortName, markType, markColor, markShape,
|
||||
markPattern, originatingMarkTemplateId, originatingMarkPropertiesId);
|
||||
return mark;
|
||||
}
|
||||
|
||||
@@ -2683,6 +2709,7 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
private DeviceConfiguration loadConfigurationEntry(Document dbObject) throws JsonDeserializationException, ParseException {
|
||||
final String idAsString = dbObject.getString(FieldNames.CONFIGURATION_ID_AS_STRING.name());
|
||||
final DeviceConfiguration configuration;
|
||||
@@ -2719,22 +2746,6 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
|
||||
return configuration;
|
||||
}
|
||||
|
||||
private DeviceIdentifier loadDeviceId(
|
||||
TypeBasedServiceFinder<DeviceIdentifierMongoHandler> deviceIdentifierServiceFinder, Document deviceId)
|
||||
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);
|
||||
} catch (TransformationException e) {
|
||||
return new PlaceHolderDeviceIdentifierSerializationHandler().deserialize(stringRepresentation, deviceType,
|
||||
stringRepresentation);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Set<URL>> loadResultUrls() {
|
||||
Map<String, Set<URL>> resultUrls = new HashMap<>();
|
||||
@@ -3051,4 +3062,5 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
|
||||
}
|
||||
return fromDb;
|
||||
}
|
||||
|
||||
}
|
||||
+6
-6
@@ -100,8 +100,9 @@ public enum FieldNames {
|
||||
RACE_LOG_IDENTIFIER, RACE_LOG_EVENT, RACE_LOG_EVENT_PASS_ID, RACE_LOG_EVENT_ID, RACE_LOG_EVENT_CREATED_AT, RACE_LOG_EVENT_AUTHOR_NAME,
|
||||
RACE_LOG_EVENT_AUTHOR_PRIORITY, RACE_LOG_EVENT_INVOLVED_BOATS, RACE_LOG_EVENT_FLAG_UPPER, RACE_LOG_EVENT_FLAG_LOWER, RACE_LOG_EVENT_FLAG_DISPLAYED,
|
||||
RACE_LOG_EVENT_START_TIME, RACE_LOG_EVENT_CLASS, RACE_LOG_EVENT_NEXT_STATUS, RACE_LOG_COURSE_DESIGN, RACE_LOG_COURSE_DESIGNER_MODE, WAYPOINT_ID, WAYPOINT_NAME,
|
||||
WAYPOINT_PASSINGINSTRUCTIONS, CONTROLPOINT, CONTROLPOINT_CLASS, MARK_ID, MARK_COLOR, MARK_NAME, MARK_PATTERN, MARK_SHAPE, MARK_TYPE,
|
||||
CONTROLPOINT_VALUE, CONTROLPOINTWITHTWOMARKS_ID, CONTROLPOINTWITHTWOMARKS_NAME, CONTROLPOINTWITHTWOMARKS_LEFT, CONTROLPOINTWITHTWOMARKS_RIGHT,
|
||||
WAYPOINT_PASSINGINSTRUCTIONS, CONTROLPOINT, CONTROLPOINT_CLASS, MARK_ID, MARK_COLOR, MARK_NAME, MARK_SHORT_NAME, MARK_PATTERN, MARK_SHAPE, MARK_TYPE,
|
||||
MARK_ORIGINATING_MARK_TEMPLATE_ID, MARK_ORIGINATING_MARK_PROPERTIES_ID, RACE_LOG_COURSE_ORIGINATING_TEMPLATE_ID, RACE_LOG_COURSE_ASSOCIATED_ROLES, RACE_LOG_COURSE_ASSOCIATED_ROLES_MARK_ID, RACE_LOG_COURSE_ASSOCIATED_ROLES_ROLE_ID,
|
||||
CONTROLPOINT_VALUE, CONTROLPOINTWITHTWOMARKS_ID, CONTROLPOINTWITHTWOMARKS_NAME, CONTROLPOINTWITHTWOMARKS_SHORT_NAME, CONTROLPOINTWITHTWOMARKS_LEFT, CONTROLPOINTWITHTWOMARKS_RIGHT,
|
||||
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,
|
||||
@@ -116,7 +117,7 @@ public enum FieldNames {
|
||||
/** @deprecated Replaced by CONTROLPOINTWITHTWOMARKS_NAME, still used to migrate old races*/ @Deprecated GATE_NAME,
|
||||
/** @deprecated Replaced by CONTROLPOINTWITHTWOMARKS_LEFT, still used to migrate old races*/ @Deprecated GATE_LEFT,
|
||||
/** @deprecated Replaced by CONTROLPOINTWITHTWOMARKS_RIGHT, still used to migrate old races*/ @Deprecated GATE_RIGHT,
|
||||
|
||||
|
||||
//ORC Leg fields
|
||||
ORC_LEG_NR, ORC_LEG_LENGTH_IN_NAUTICAL_MILES, ORC_LEG_TWA_IN_DEG, ORC_LEG_TYPE,
|
||||
|
||||
@@ -141,10 +142,10 @@ public enum FieldNames {
|
||||
CONFIGURATION_CONFIG_PROCEDURES,
|
||||
|
||||
// GPSFixes
|
||||
DEVICE_TYPE_SPECIFIC_ID, DEVICE_ID, DEVICE_TYPE, GPSFIX_TYPE, GPSFIX, DEVICE_STRING_REPRESENTATION,
|
||||
DEVICE_ID, GPSFIX_TYPE, GPSFIX,
|
||||
|
||||
// GPSFixes metadata
|
||||
TIMERANGE, NUM_FIXES,
|
||||
TIMERANGE, NUM_FIXES, LAST_FIX_RECEIVED,
|
||||
|
||||
// Timespan
|
||||
FROM_MILLIS, TO_MILLIS,
|
||||
@@ -183,5 +184,4 @@ public enum FieldNames {
|
||||
ORC_CERTIFICATE_R75_PREDICTION, ORC_CERTIFICATE_R90_PREDICTION, ORC_CERTIFICATE_R110_PREDICTION, ORC_CERTIFICATE_R120_PREDICTION,
|
||||
ORC_CERTIFICATE_R135_PREDICTION, ORC_CERTIFICATE_R150_PREDICTION, ORC_CERTIFICATE_BOATCLASS, ORC_CERTIFICATE_LENGTH, ORC_CERTIFICATE_TWA_SPEED_PREDICTIONS,
|
||||
ORC_CERTIFICATE_CIRCULAR_RANDOM_SPEED_PREDICTIONS, ORC_CERTIFICATE_NON_SPINNAKER_SPEED_PREDICTIONS, ORC_OTHER_RACE_IDENTIFIER, ORC_IMPLIED_WIND_SOURCE;
|
||||
|
||||
}
|
||||
+30
-31
@@ -1,5 +1,8 @@
|
||||
package com.sap.sailing.domain.persistence.impl;
|
||||
|
||||
import static com.sap.sailing.shared.persistence.impl.MongoObjectFactoryImpl.getPassingInstructions;
|
||||
import static com.sap.sailing.shared.persistence.impl.MongoObjectFactoryImpl.storeDeviceId;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
@@ -98,7 +101,6 @@ import com.sap.sailing.domain.base.configuration.RegattaConfiguration;
|
||||
import com.sap.sailing.domain.base.impl.FleetImpl;
|
||||
import com.sap.sailing.domain.common.DeviceIdentifier;
|
||||
import com.sap.sailing.domain.common.MaxPointsReason;
|
||||
import com.sap.sailing.domain.common.PassingInstruction;
|
||||
import com.sap.sailing.domain.common.Positioned;
|
||||
import com.sap.sailing.domain.common.RaceIdentifier;
|
||||
import com.sap.sailing.domain.common.SpeedWithBearing;
|
||||
@@ -116,8 +118,6 @@ import com.sap.sailing.domain.leaderboard.ResultDiscardingRule;
|
||||
import com.sap.sailing.domain.leaderboard.SettableScoreCorrection;
|
||||
import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule;
|
||||
import com.sap.sailing.domain.persistence.MongoObjectFactory;
|
||||
import com.sap.sailing.domain.persistence.racelog.tracking.DeviceIdentifierMongoHandler;
|
||||
import com.sap.sailing.domain.persistence.racelog.tracking.impl.PlaceHolderDeviceIdentifierMongoHandler;
|
||||
import com.sap.sailing.domain.racelog.RaceLogIdentifier;
|
||||
import com.sap.sailing.domain.regattalike.RegattaLikeIdentifier;
|
||||
import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParameters;
|
||||
@@ -134,6 +134,8 @@ import com.sap.sailing.server.gateway.serialization.impl.DeviceConfigurationJson
|
||||
import com.sap.sailing.server.gateway.serialization.impl.RegattaConfigurationJsonSerializer;
|
||||
import com.sap.sailing.server.gateway.serialization.racelog.impl.ImpliedWindSourceSerializer;
|
||||
import com.sap.sailing.server.gateway.serialization.racelog.impl.ORCCertificateJsonSerializer;
|
||||
import com.sap.sailing.shared.persistence.device.DeviceIdentifierMongoHandler;
|
||||
import com.sap.sailing.shared.persistence.device.impl.PlaceHolderDeviceIdentifierMongoHandler;
|
||||
import com.sap.sse.common.Bearing;
|
||||
import com.sap.sse.common.Duration;
|
||||
import com.sap.sse.common.NoCorrespondingServiceRegisteredException;
|
||||
@@ -258,9 +260,9 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
|
||||
dropIndexSafe(gpsFixCollection, "DEVICE_ID_1_GPSFIX.TIME_AS_MILLIS_1");
|
||||
Document index = new Document();
|
||||
index.put(FieldNames.TIME_AS_MILLIS.name(), 1);
|
||||
index.put(FieldNames.DEVICE_ID.name()+"."+FieldNames.DEVICE_TYPE.name(), 1);
|
||||
index.put(FieldNames.DEVICE_ID.name()+"."+FieldNames.DEVICE_TYPE_SPECIFIC_ID.name(), 1);
|
||||
index.put(FieldNames.DEVICE_ID.name()+"."+FieldNames.DEVICE_STRING_REPRESENTATION.name(), 1);
|
||||
index.put(FieldNames.DEVICE_ID.name()+"."+com.sap.sailing.shared.persistence.impl.FieldNames.DEVICE_TYPE.name(), 1);
|
||||
index.put(FieldNames.DEVICE_ID.name()+"."+com.sap.sailing.shared.persistence.impl.FieldNames.DEVICE_TYPE_SPECIFIC_ID.name(), 1);
|
||||
index.put(FieldNames.DEVICE_ID.name()+"."+com.sap.sailing.shared.persistence.impl.FieldNames.DEVICE_STRING_REPRESENTATION.name(), 1);
|
||||
gpsFixCollection.createIndex(index, new IndexOptions().name("fixbytimeanddev"));
|
||||
return gpsFixCollection;
|
||||
}
|
||||
@@ -995,7 +997,7 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
|
||||
result.put(FieldNames.RACE_LOG_EVENT.name(), storeRaceLogSuppressedMarkPassingsEvent(event));
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
public Document storeRaceLogEntry(RaceLogIdentifier raceLogIdentifier, RaceLogORCLegDataEvent event) {
|
||||
Document result = new Document();
|
||||
storeRaceLogIdentifier(raceLogIdentifier, result);
|
||||
@@ -1290,9 +1292,23 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
|
||||
storeRaceLogEventProperties(courseDesignChangedEvent, result);
|
||||
result.put(FieldNames.RACE_LOG_EVENT_CLASS.name(), RaceLogCourseDesignChangedEvent.class.getSimpleName());
|
||||
result.put(FieldNames.RACE_LOG_COURSE_DESIGN_NAME.name(), courseDesignChangedEvent.getCourseDesign().getName());
|
||||
result.put(FieldNames.RACE_LOG_COURSE_ORIGINATING_TEMPLATE_ID.name(),
|
||||
courseDesignChangedEvent.getCourseDesign().getOriginatingCourseTemplateIdOrNull());
|
||||
result.put(FieldNames.RACE_LOG_COURSE_DESIGNER_MODE.name(),
|
||||
courseDesignChangedEvent.getCourseDesignerMode() == null ? null : courseDesignChangedEvent.getCourseDesignerMode().name());
|
||||
result.put(FieldNames.RACE_LOG_COURSE_DESIGN.name(), storeCourseBase(courseDesignChangedEvent.getCourseDesign()));
|
||||
|
||||
final BasicDBList roleMap = new BasicDBList();
|
||||
courseDesignChangedEvent.getCourseDesign().getAssociatedRoles().forEach((mark, role) -> {
|
||||
final Document mapping = new Document();
|
||||
mapping.put(FieldNames.RACE_LOG_COURSE_ASSOCIATED_ROLES_MARK_ID.name(), mark.getId().toString());
|
||||
mapping.put(FieldNames.RACE_LOG_COURSE_ASSOCIATED_ROLES_ROLE_ID.name(), role.toString());
|
||||
roleMap.add(mapping);
|
||||
});
|
||||
|
||||
if (!roleMap.isEmpty()) {
|
||||
result.put(FieldNames.RACE_LOG_COURSE_ASSOCIATED_ROLES.name(), roleMap);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1301,6 +1317,7 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
|
||||
storeRaceLogEventProperties(finishPositioningListChangedEvent, result);
|
||||
result.put(FieldNames.RACE_LOG_EVENT_CLASS.name(), RaceLogFinishPositioningListChangedEvent.class.getSimpleName());
|
||||
result.put(FieldNames.RACE_LOG_POSITIONED_COMPETITORS.name(), storePositionedCompetitors(finishPositioningListChangedEvent.getPositionedCompetitorsIDsNamesMaxPointsReasons()));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1309,6 +1326,7 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
|
||||
storeRaceLogEventProperties(finishPositioningConfirmedEvent, result);
|
||||
result.put(FieldNames.RACE_LOG_EVENT_CLASS.name(), RaceLogFinishPositioningConfirmedEvent.class.getSimpleName());
|
||||
result.put(FieldNames.RACE_LOG_POSITIONED_COMPETITORS.name(), storePositionedCompetitors(finishPositioningConfirmedEvent.getPositionedCompetitorsIDsNamesMaxPointsReasons()));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1379,6 +1397,7 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
|
||||
Document result = new Document();
|
||||
result.put(FieldNames.CONTROLPOINTWITHTWOMARKS_ID.name(), cpwtm.getId());
|
||||
result.put(FieldNames.CONTROLPOINTWITHTWOMARKS_NAME.name(), cpwtm.getName());
|
||||
result.put(FieldNames.CONTROLPOINTWITHTWOMARKS_SHORT_NAME.name(), cpwtm.getShortName());
|
||||
result.put(FieldNames.CONTROLPOINTWITHTWOMARKS_LEFT.name(), storeMark(cpwtm.getLeft()));
|
||||
result.put(FieldNames.CONTROLPOINTWITHTWOMARKS_RIGHT.name(), storeMark(cpwtm.getRight()));
|
||||
return result;
|
||||
@@ -1389,22 +1408,15 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
|
||||
result.put(FieldNames.MARK_ID.name(), mark.getId());
|
||||
result.put(FieldNames.MARK_COLOR.name(), mark.getColor()==null?null:mark.getColor().getAsHtml());
|
||||
result.put(FieldNames.MARK_NAME.name(), mark.getName());
|
||||
result.put(FieldNames.MARK_SHORT_NAME.name(), mark.getShortName());
|
||||
result.put(FieldNames.MARK_PATTERN.name(), mark.getPattern());
|
||||
result.put(FieldNames.MARK_SHAPE.name(), mark.getShape());
|
||||
result.put(FieldNames.MARK_TYPE.name(), mark.getType() == null ? null : mark.getType().name());
|
||||
result.put(FieldNames.MARK_ORIGINATING_MARK_TEMPLATE_ID.name(), mark.getOriginatingMarkTemplateIdOrNull());
|
||||
result.put(FieldNames.MARK_ORIGINATING_MARK_PROPERTIES_ID.name(), mark.getOriginatingMarkPropertiesIdOrNull());
|
||||
return result;
|
||||
}
|
||||
|
||||
private String getPassingInstructions(PassingInstruction passingInstructions) {
|
||||
final String passing;
|
||||
if (passingInstructions != null) {
|
||||
passing = passingInstructions.name();
|
||||
} else {
|
||||
passing = null;
|
||||
}
|
||||
return passing;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void storeCompetitor(Competitor competitor) {
|
||||
if (competitor.hasBoat()) {
|
||||
@@ -1555,20 +1567,6 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
|
||||
Document query = new Document(FieldNames.CONFIGURATION_ID_AS_STRING.name(), id.toString());
|
||||
configurationsCollections.deleteOne(query);
|
||||
}
|
||||
|
||||
public static Document storeDeviceId(
|
||||
TypeBasedServiceFinder<DeviceIdentifierMongoHandler> deviceIdentifierServiceFinder, DeviceIdentifier device)
|
||||
throws TransformationException, NoCorrespondingServiceRegisteredException {
|
||||
String type = device.getIdentifierType();
|
||||
DeviceIdentifierMongoHandler handler = deviceIdentifierServiceFinder.findService(type);
|
||||
com.sap.sse.common.Util.Pair<String, ? extends Object> pair = handler.serialize(device);
|
||||
type = pair.getA();
|
||||
Object deviceTypeSpecificId = pair.getB();
|
||||
return new Document()
|
||||
.append(FieldNames.DEVICE_TYPE.name(), type)
|
||||
.append(FieldNames.DEVICE_TYPE_SPECIFIC_ID.name(), deviceTypeSpecificId)
|
||||
.append(FieldNames.DEVICE_STRING_REPRESENTATION.name(), device.getStringRepresentation());
|
||||
}
|
||||
|
||||
void storeRaceLogEventEvent(Document eventEntry) {
|
||||
getRaceLogCollection().insertOne(eventEntry);
|
||||
@@ -1886,4 +1884,5 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
|
||||
document.append(FieldNames.ORC_CERTIFICATE.name(), createORCCertificateObject(event.getCertificate()));
|
||||
storeRegattaLogEvent(regattaLikeIdentifier, document);
|
||||
}
|
||||
|
||||
}
|
||||
+3
-2
@@ -10,6 +10,7 @@ import com.sap.sailing.domain.persistence.MongoObjectFactory;
|
||||
import com.sap.sailing.domain.persistence.impl.DomainObjectFactoryImpl;
|
||||
import com.sap.sailing.domain.persistence.impl.MongoObjectFactoryImpl;
|
||||
import com.sap.sailing.domain.persistence.racelog.tracking.FixMongoHandler;
|
||||
import static com.sap.sailing.shared.persistence.impl.DomainObjectFactoryImpl.loadPosition;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
|
||||
public class GPSFixMongoHandlerImpl implements FixMongoHandler<GPSFix> {
|
||||
@@ -25,7 +26,7 @@ public class GPSFixMongoHandlerImpl implements FixMongoHandler<GPSFix> {
|
||||
public Document transformForth(GPSFix fix) throws IllegalArgumentException {
|
||||
Document result = new Document();
|
||||
mof.storeTimed(fix, result);
|
||||
mof.storePositioned(fix, result);
|
||||
mof.storePositioned(fix, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -33,7 +34,7 @@ public class GPSFixMongoHandlerImpl implements FixMongoHandler<GPSFix> {
|
||||
public GPSFix transformBack(Document object) {
|
||||
Document dbObject = (Document) object;
|
||||
TimePoint timePoint = dof.loadTimePoint(dbObject);
|
||||
Position position = dof.loadPosition(dbObject);
|
||||
Position position = loadPosition(dbObject);
|
||||
return new GPSFixImpl(position, timePoint);
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -11,6 +11,7 @@ import com.sap.sailing.domain.persistence.MongoObjectFactory;
|
||||
import com.sap.sailing.domain.persistence.impl.DomainObjectFactoryImpl;
|
||||
import com.sap.sailing.domain.persistence.impl.MongoObjectFactoryImpl;
|
||||
import com.sap.sailing.domain.persistence.racelog.tracking.FixMongoHandler;
|
||||
import static com.sap.sailing.shared.persistence.impl.DomainObjectFactoryImpl.loadPosition;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
|
||||
public class GPSFixMovingMongoHandlerImpl implements FixMongoHandler<GPSFixMoving> {
|
||||
@@ -34,7 +35,7 @@ public class GPSFixMovingMongoHandlerImpl implements FixMongoHandler<GPSFixMovin
|
||||
@Override
|
||||
public GPSFixMoving transformBack(Document dbObject) {
|
||||
TimePoint timePoint = dof.loadTimePoint(dbObject);
|
||||
Position position = dof.loadPosition(dbObject);
|
||||
Position position = loadPosition(dbObject);
|
||||
SpeedWithBearing speed = dof.loadSpeedWithBearing(dbObject);
|
||||
return new GPSFixMovingImpl(position, timePoint, speed);
|
||||
}
|
||||
|
||||
+52
-42
@@ -1,6 +1,6 @@
|
||||
package com.sap.sailing.domain.persistence.racelog.tracking.impl;
|
||||
|
||||
import static com.sap.sailing.domain.persistence.impl.MongoObjectFactoryImpl.storeDeviceId;
|
||||
import static com.sap.sailing.shared.persistence.impl.MongoObjectFactoryImpl.storeDeviceId;
|
||||
import static com.sap.sailing.domain.persistence.impl.MongoObjectFactoryImpl.storeTimeRange;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -31,10 +31,10 @@ import com.sap.sailing.domain.persistence.MongoObjectFactory;
|
||||
import com.sap.sailing.domain.persistence.impl.DomainObjectFactoryImpl;
|
||||
import com.sap.sailing.domain.persistence.impl.FieldNames;
|
||||
import com.sap.sailing.domain.persistence.impl.MongoObjectFactoryImpl;
|
||||
import com.sap.sailing.domain.persistence.racelog.tracking.DeviceIdentifierMongoHandler;
|
||||
import com.sap.sailing.domain.persistence.racelog.tracking.FixMongoHandler;
|
||||
import com.sap.sailing.domain.persistence.racelog.tracking.MongoSensorFixStore;
|
||||
import com.sap.sailing.domain.racelog.tracking.FixReceivedListener;
|
||||
import com.sap.sailing.shared.persistence.device.DeviceIdentifierMongoHandler;
|
||||
import com.sap.sse.common.Duration;
|
||||
import com.sap.sse.common.NoCorrespondingServiceRegisteredException;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
@@ -195,37 +195,28 @@ public class MongoSensorFixStoreImpl implements MongoSensorFixStore {
|
||||
final Object dbDeviceId = storeDeviceId(deviceServiceFinder, device);
|
||||
final int nrOfTotalFixes = Util.size(fixes);
|
||||
final ArrayList<Document> dbFixes = new ArrayList<>(nrOfTotalFixes);
|
||||
|
||||
TimePoint newFrom = null;
|
||||
TimePoint newTo = null;
|
||||
final TimeRange oldTimeRange = getTimeRangeCoveredByFixes(device);
|
||||
TimeRange newTimeRange = oldTimeRange;
|
||||
FixT latestFix = null;
|
||||
for (FixT fix : fixes) {
|
||||
String type = fix.getClass().getName();
|
||||
FixMongoHandler<FixT> mongoHandler = findService(type);
|
||||
Object fixObject = mongoHandler.transformForth(fix);
|
||||
Document entry = new Document().append(FieldNames.DEVICE_ID.name(), dbDeviceId)
|
||||
.append(FieldNames.GPSFIX_TYPE.name(), type).append(FieldNames.GPSFIX.name(), fixObject);
|
||||
if (latestFix == null || latestFix.getTimePoint().before(fix.getTimePoint())) {
|
||||
latestFix = fix;
|
||||
}
|
||||
Document entry = new Document().append(FieldNames.DEVICE_ID.name(), dbDeviceId);
|
||||
storeFixToDocument(entry, fix);
|
||||
mongoOF.storeTimed(fix, entry);
|
||||
dbFixes.add(entry);
|
||||
TimePoint fixTP = fix.getTimePoint();
|
||||
if (newFrom == null || newFrom.after(fixTP)) {
|
||||
newFrom = fixTP;
|
||||
}
|
||||
if (newTo == null || newTo.before(fixTP)) {
|
||||
newTo = fixTP;
|
||||
}
|
||||
final TimeRangeImpl fixTimeRange = new TimeRangeImpl(fixTP, fixTP, /* toIsInclusive */ true);
|
||||
newTimeRange = newTimeRange == null ? fixTimeRange : newTimeRange.extend(fixTimeRange);
|
||||
}
|
||||
fixesCollection.withWriteConcern(WriteConcern.UNACKNOWLEDGED).insertMany(dbFixes);
|
||||
final Document updateOperation = new Document();
|
||||
final Document newMetadata = new Document();
|
||||
newMetadata.put(FieldNames.DEVICE_ID.name(), dbDeviceId);
|
||||
|
||||
TimeRange oldTimeRange = getTimeRangeCoveredByFixes(device);
|
||||
if (oldTimeRange != null) {
|
||||
newFrom = oldTimeRange.from().before(newFrom) ? oldTimeRange.from() : newFrom;
|
||||
newTo = oldTimeRange.to().after(newTo) ? oldTimeRange.to() : newTo;
|
||||
if (latestFix != null) {
|
||||
newMetadata.put(FieldNames.LAST_FIX_RECEIVED.name(), storeFixToDocument(new Document(), latestFix));
|
||||
}
|
||||
final TimeRange newTimeRange = new TimeRangeImpl(newFrom, newTo);
|
||||
|
||||
storeTimeRange(newTimeRange, newMetadata, FieldNames.TIMERANGE);
|
||||
updateOperation.append("$set", newMetadata);
|
||||
updateOperation.append("$inc", new Document(FieldNames.NUM_FIXES.name(), nrOfTotalFixes));
|
||||
@@ -239,6 +230,20 @@ public class MongoSensorFixStoreImpl implements MongoSensorFixStore {
|
||||
return maneuverChanged;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the {@code fix} to the {@code entry} document such that {@link #loadFix(Document)} can re-establish
|
||||
* the fix from that document.
|
||||
*
|
||||
* @return for convenience and call chaining, the updated {@code entry} is returned
|
||||
*/
|
||||
private <FixT extends Timed> Document storeFixToDocument(Document entry, FixT fix) throws TransformationException {
|
||||
String type = fix.getClass().getName();
|
||||
FixMongoHandler<FixT> mongoHandler = findService(type);
|
||||
Object fixObject = mongoHandler.transformForth(fix);
|
||||
entry.append(FieldNames.GPSFIX_TYPE.name(), type).append(FieldNames.GPSFIX.name(), fixObject);
|
||||
return entry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <FixT extends Timed> void storeFix(DeviceIdentifier device, FixT fix) {
|
||||
storeFixes(device, Collections.singletonList(fix));
|
||||
@@ -294,21 +299,27 @@ public class MongoSensorFixStoreImpl implements MongoSensorFixStore {
|
||||
@Override
|
||||
public TimeRange getTimeRangeCoveredByFixes(DeviceIdentifier device)
|
||||
throws TransformationException, NoCorrespondingServiceRegisteredException {
|
||||
Document result = findMetadataObject(device);
|
||||
if (result == null) {
|
||||
return null;
|
||||
final Document resultDocument = findMetadataObject(device);
|
||||
final TimeRange result;
|
||||
if (resultDocument == null) {
|
||||
result = null;
|
||||
} else {
|
||||
result = DomainObjectFactoryImpl.loadTimeRange(resultDocument, FieldNames.TIMERANGE);
|
||||
}
|
||||
return DomainObjectFactoryImpl.loadTimeRange(result, FieldNames.TIMERANGE);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getNumberOfFixes(DeviceIdentifier device)
|
||||
throws TransformationException, NoCorrespondingServiceRegisteredException {
|
||||
Document result = findMetadataObject(device);
|
||||
if (result == null) {
|
||||
return 0;
|
||||
final Document resultDocument = findMetadataObject(device);
|
||||
final long result;
|
||||
if (resultDocument == null) {
|
||||
result = 0;
|
||||
} else {
|
||||
result = ((Number) resultDocument.get(FieldNames.NUM_FIXES.name())).longValue();
|
||||
}
|
||||
return ((Number) result.get(FieldNames.NUM_FIXES.name())).longValue();
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -331,20 +342,19 @@ public class MongoSensorFixStoreImpl implements MongoSensorFixStore {
|
||||
}
|
||||
|
||||
@Override
|
||||
public <FixT extends Timed> Map<DeviceIdentifier, FixT> getLastFix(Iterable<DeviceIdentifier> forDevices)
|
||||
public <FixT extends Timed> Map<DeviceIdentifier, FixT> getFixLastReceived(Iterable<DeviceIdentifier> forDevices)
|
||||
throws TransformationException, NoCorrespondingServiceRegisteredException {
|
||||
Map<DeviceIdentifier, FixT> result = new HashMap<>();
|
||||
for (final DeviceIdentifier deviceIdentifier : forDevices) {
|
||||
final Bson deviceQuery = getDeviceQuery(deviceIdentifier);
|
||||
final Document orderBy = new Document(
|
||||
FieldNames.GPSFIX.name() + "." + FieldNames.TIME_AS_MILLIS.name(), -1);
|
||||
FindIterable<Document> lastFixForDeviceCursor = fixesCollection.find(deviceQuery).sort(orderBy).limit(1);
|
||||
if (lastFixForDeviceCursor.iterator().hasNext()) {
|
||||
final Document lastFixForDeviceDbObject = lastFixForDeviceCursor.iterator().next();
|
||||
final Timed lastFixForDevice = loadFix(lastFixForDeviceDbObject);
|
||||
@SuppressWarnings("unchecked")
|
||||
final FixT lastFixForDeviceTyped = (FixT) lastFixForDevice;
|
||||
result.put(deviceIdentifier, lastFixForDeviceTyped);
|
||||
final Document metadataDoc = findMetadataObject(deviceIdentifier);
|
||||
if (metadataDoc != null) {
|
||||
final Document lastFixForDeviceDbObject = (Document) metadataDoc.get(FieldNames.LAST_FIX_RECEIVED.name());
|
||||
if (lastFixForDeviceDbObject != null) {
|
||||
final Timed lastFixForDevice = loadFix(lastFixForDeviceDbObject);
|
||||
@SuppressWarnings("unchecked")
|
||||
final FixT lastFixForDeviceTyped = (FixT) lastFixForDevice;
|
||||
result.put(deviceIdentifier, lastFixForDeviceTyped);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@ import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogDeviceMa
|
||||
import com.sap.sailing.domain.abstractlog.regatta.impl.RegattaLogImpl;
|
||||
import com.sap.sailing.domain.base.Mark;
|
||||
import com.sap.sailing.domain.base.impl.MarkImpl;
|
||||
import com.sap.sailing.domain.racelogtracking.PingDeviceIdentifierImpl;
|
||||
import com.sap.sailing.domain.racelogtracking.impl.PingDeviceIdentifierImpl;
|
||||
import com.sap.sse.common.impl.MillisecondsTimePoint;
|
||||
|
||||
import org.junit.Assert;
|
||||
|
||||
+9
-7
@@ -30,6 +30,7 @@ import com.sap.sailing.domain.base.impl.CourseImpl;
|
||||
import com.sap.sailing.domain.base.impl.RaceDefinitionImpl;
|
||||
import com.sap.sailing.domain.base.impl.WaypointImpl;
|
||||
import com.sap.sailing.domain.common.DeviceIdentifier;
|
||||
import com.sap.sailing.domain.common.impl.DegreePosition;
|
||||
import com.sap.sailing.domain.common.racelog.tracking.TransformationException;
|
||||
import com.sap.sailing.domain.common.tracking.GPSFix;
|
||||
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
|
||||
@@ -543,7 +544,8 @@ public class TrackedRaceLoadsFixesTest extends AbstractGPSFixStoreTest {
|
||||
store.storeFix(device, createFix(100, 10, 20, 30, 40));
|
||||
store.storeFix(device, createFix(200, 10, 20, 30, 40));
|
||||
assertEquals(2, store.getNumberOfFixes(device));
|
||||
assertEquals(TimeRangeImpl.create(100, 200), store.getTimeRangeCoveredByFixes(device));
|
||||
assertEquals(TimeRangeImpl.create(100, 200, /* toIsInclusive */ true), store.getTimeRangeCoveredByFixes(device));
|
||||
assertEquals(new DegreePosition(10, 20), ((GPSFix) store.getFixLastReceived(Collections.singleton(device)).get(device)).getPosition());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -551,28 +553,28 @@ public class TrackedRaceLoadsFixesTest extends AbstractGPSFixStoreTest {
|
||||
store.storeFix(device, createFix(100, 10, 20, 30, 40));
|
||||
store.storeFix(device, createFix(1100, 10, 20, 30, 40));
|
||||
store.storeFix(device, createFix(2100, 10, 20, 30, 40));
|
||||
final Map<DeviceIdentifier, Timed> lastFixes = store.getLastFix(Collections.singleton(device));
|
||||
final Map<DeviceIdentifier, Timed> lastFixes = store.getFixLastReceived(Collections.singleton(device));
|
||||
assertEquals(1, lastFixes.size());
|
||||
Timed lastFix = lastFixes.get(device);
|
||||
assertEquals(2100, lastFix.getTimePoint().asMillis());
|
||||
store.storeFix(device, createFix(2000, 10, 20, 30, 40));
|
||||
final Map<DeviceIdentifier, Timed> lastFixes2 = store.getLastFix(Collections.singleton(device));
|
||||
final Map<DeviceIdentifier, Timed> lastFixes2 = store.getFixLastReceived(Collections.singleton(device));
|
||||
assertEquals(1, lastFixes2.size());
|
||||
Timed lastFix2 = lastFixes2.get(device);
|
||||
assertEquals(2100, lastFix2.getTimePoint().asMillis());
|
||||
assertEquals(2000, lastFix2.getTimePoint().asMillis()); // not the fix with the latest time point but the fix that was last received by the store
|
||||
store.storeFix(device, createFix(2200, 10, 20, 30, 40));
|
||||
final Map<DeviceIdentifier, Timed> lastFixes3 = store.getLastFix(Collections.singleton(device));
|
||||
final Map<DeviceIdentifier, Timed> lastFixes3 = store.getFixLastReceived(Collections.singleton(device));
|
||||
assertEquals(1, lastFixes3.size());
|
||||
Timed lastFix3 = lastFixes3.get(device);
|
||||
assertEquals(2200, lastFix3.getTimePoint().asMillis());
|
||||
final DeviceIdentifier device2 = new SmartphoneImeiIdentifier("b");
|
||||
store.storeFix(device2, createFix(1200, 10, 20, 30, 40));
|
||||
store.storeFix(device2, createFix(1100, 10, 20, 30, 40));
|
||||
final Map<DeviceIdentifier, Timed> lastFixes4 = store.getLastFix(Arrays.asList(device, device2));
|
||||
final Map<DeviceIdentifier, Timed> lastFixes4 = store.getFixLastReceived(Arrays.asList(device, device2));
|
||||
assertEquals(2, lastFixes4.size());
|
||||
Timed lastFix4 = lastFixes4.get(device);
|
||||
assertEquals(2200, lastFix4.getTimePoint().asMillis());
|
||||
Timed lastFixDevice2 = lastFixes4.get(device2);
|
||||
assertEquals(1200, lastFixDevice2.getTimePoint().asMillis());
|
||||
assertEquals(1100, lastFixDevice2.getTimePoint().asMillis()); // not the fix with the latest time point but the fix that was last received by the store
|
||||
}
|
||||
}
|
||||
@@ -20,3 +20,4 @@ Require-Bundle: com.sap.sailing.domain.shared.android,
|
||||
org.mongodb.mongo-java-driver;bundle-version="3.6.4"
|
||||
Export-Package: com.sap.sailing.domain.racelog.tracking.test.mock
|
||||
Automatic-Module-Name: com.sap.sailing.domain.racelogtrackingadapter.testsupport
|
||||
Import-Package: com.sap.sailing.shared.persistence.device
|
||||
+1
-1
@@ -3,10 +3,10 @@ package com.sap.sailing.domain.racelog.tracking.test.mock;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.sap.sailing.domain.persistence.racelog.tracking.DeviceIdentifierMongoHandler;
|
||||
import com.sap.sailing.domain.persistence.racelog.tracking.FixMongoHandler;
|
||||
import com.sap.sailing.server.gateway.serialization.racelog.tracking.DeviceIdentifierJsonHandler;
|
||||
import com.sap.sailing.server.gateway.serialization.racelog.tracking.GPSFixJsonHandler;
|
||||
import com.sap.sailing.shared.persistence.device.DeviceIdentifierMongoHandler;
|
||||
import com.sap.sse.common.TypeBasedServiceFinder;
|
||||
import com.sap.sse.common.TypeBasedServiceFinderFactory;
|
||||
|
||||
|
||||
+1
-1
@@ -3,12 +3,12 @@ package com.sap.sailing.domain.racelog.tracking.test.mock;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.sap.sailing.domain.persistence.racelog.tracking.DeviceIdentifierMongoHandler;
|
||||
import com.sap.sailing.domain.persistence.racelog.tracking.FixMongoHandler;
|
||||
import com.sap.sailing.domain.racelogtracking.impl.SmartphoneUUIDMongoHandler;
|
||||
import com.sap.sailing.server.gateway.serialization.racelog.tracking.DeviceIdentifierJsonHandler;
|
||||
import com.sap.sailing.server.gateway.serialization.racelog.tracking.GPSFixJsonHandler;
|
||||
import com.sap.sailing.server.gateway.serialization.racelog.tracking.impl.SmartphoneUUIDJsonHandler;
|
||||
import com.sap.sailing.shared.persistence.device.DeviceIdentifierMongoHandler;
|
||||
import com.sap.sse.common.TypeBasedServiceFinder;
|
||||
import com.sap.sse.common.TypeBasedServiceFinderFactory;
|
||||
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ package com.sap.sailing.domain.racelog.tracking.test.mock;
|
||||
|
||||
import com.sap.sailing.domain.common.DeviceIdentifier;
|
||||
import com.sap.sailing.domain.common.racelog.tracking.TransformationException;
|
||||
import com.sap.sailing.domain.persistence.racelog.tracking.DeviceIdentifierMongoHandler;
|
||||
import com.sap.sailing.shared.persistence.device.DeviceIdentifierMongoHandler;
|
||||
|
||||
public class SmartphoneImeiMongoHandler extends SmartphoneImeiSerializationHandler
|
||||
implements DeviceIdentifierMongoHandler {
|
||||
|
||||
@@ -19,7 +19,8 @@ Require-Bundle: com.sap.sailing.domain,
|
||||
com.sap.sse,
|
||||
com.sap.sailing.server.gateway.serialization.shared.android,
|
||||
com.sap.sse.mail,
|
||||
org.mongodb.mongo-java-driver;bundle-version="3.6.4"
|
||||
org.mongodb.mongo-java-driver;bundle-version="3.6.4",
|
||||
com.sap.sailing.shared.persistence;bundle-version="1.0.0"
|
||||
Import-Package: com.google.zxing;version="3.1.0",
|
||||
com.mongodb,
|
||||
com.sap.sse.concurrent,
|
||||
|
||||
+1
-2
@@ -13,11 +13,9 @@ import org.osgi.framework.ServiceRegistration;
|
||||
import org.osgi.util.tracker.ServiceTracker;
|
||||
|
||||
import com.sap.sailing.domain.common.tracking.GPSFix;
|
||||
import com.sap.sailing.domain.persistence.racelog.tracking.DeviceIdentifierMongoHandler;
|
||||
import com.sap.sailing.domain.racelog.tracking.SensorFixMapper;
|
||||
import com.sap.sailing.domain.racelogsensortracking.impl.SensorFixMapperFactoryImpl;
|
||||
import com.sap.sailing.domain.racelogtracking.DeviceIdentifierStringSerializationHandler;
|
||||
import com.sap.sailing.domain.racelogtracking.PingDeviceIdentifierImpl;
|
||||
import com.sap.sailing.domain.racelogtracking.RaceLogTrackingAdapterFactory;
|
||||
import com.sap.sailing.domain.racelogtracking.SmartphoneUUIDIdentifier;
|
||||
import com.sap.sailing.domain.racelogtracking.impl.fixtracker.RegattaLogFixTrackerRegattaListener;
|
||||
@@ -35,6 +33,7 @@ import com.sap.sailing.server.gateway.serialization.racelog.tracking.GPSFixJsonH
|
||||
import com.sap.sailing.server.gateway.serialization.racelog.tracking.impl.GPSFixJsonHandlerImpl;
|
||||
import com.sap.sailing.server.gateway.serialization.racelog.tracking.impl.SmartphoneUUIDJsonHandler;
|
||||
import com.sap.sailing.server.interfaces.RacingEventService;
|
||||
import com.sap.sailing.shared.persistence.device.DeviceIdentifierMongoHandler;
|
||||
import com.sap.sse.MasterDataImportClassLoaderService;
|
||||
import com.sap.sse.common.TypeBasedServiceFinder;
|
||||
import com.sap.sse.replication.Replicable;
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ package com.sap.sailing.domain.racelogtracking.impl;
|
||||
|
||||
import com.sap.sailing.domain.common.DeviceIdentifier;
|
||||
import com.sap.sailing.domain.common.racelog.tracking.TransformationException;
|
||||
import com.sap.sailing.domain.persistence.racelog.tracking.DeviceIdentifierMongoHandler;
|
||||
import com.sap.sailing.shared.persistence.device.DeviceIdentifierMongoHandler;
|
||||
|
||||
public class PingDeviceIdentifierMongoHandler extends PingDeviceIdentifierSerializationHandler
|
||||
implements DeviceIdentifierMongoHandler {
|
||||
|
||||
-1
@@ -5,7 +5,6 @@ import java.util.UUID;
|
||||
import com.sap.sailing.domain.common.DeviceIdentifier;
|
||||
import com.sap.sailing.domain.common.racelog.tracking.TransformationException;
|
||||
import com.sap.sailing.domain.racelogtracking.PingDeviceIdentifier;
|
||||
import com.sap.sailing.domain.racelogtracking.PingDeviceIdentifierImpl;
|
||||
import com.sap.sse.common.Util;
|
||||
|
||||
public class PingDeviceIdentifierSerializationHandler {
|
||||
|
||||
+4
-2
@@ -262,7 +262,8 @@ public class RaceLogRaceTracker extends AbstractRaceTrackerBaseImpl {
|
||||
}
|
||||
try {
|
||||
final Course course = trackedRace.getRace().getCourse();
|
||||
course.update(update, params.getDomainFactory());
|
||||
course.update(update, event.getCourseDesign().getAssociatedRoles(),
|
||||
event.getCourseDesign().getOriginatingCourseTemplateIdOrNull(), params.getDomainFactory());
|
||||
if (base.getName() != null) {
|
||||
course.setName(base.getName());
|
||||
}
|
||||
@@ -290,7 +291,8 @@ public class RaceLogRaceTracker extends AbstractRaceTrackerBaseImpl {
|
||||
courseBase = new CourseDataImpl("Default course for " + raceName);
|
||||
logger.log(Level.FINE, "Using empty course in creation of race " + raceName);
|
||||
}
|
||||
final Course course = new CourseImpl(courseBase.getName() == null ? raceName + " course" : courseBase.getName(), courseBase.getWaypoints());
|
||||
final Course course = new CourseImpl(courseBase.getName() == null ? raceName + " course" : courseBase.getName(),
|
||||
courseBase.getWaypoints(), courseBase.getOriginatingCourseTemplateIdOrNull());
|
||||
if (raceColumn.getTrackedRace(fleet) != null) {
|
||||
if (event != null) {
|
||||
try {
|
||||
|
||||
+1
-4
@@ -54,7 +54,6 @@ import com.sap.sailing.domain.common.racelog.tracking.RaceLogTrackingState;
|
||||
import com.sap.sailing.domain.common.tracking.GPSFix;
|
||||
import com.sap.sailing.domain.leaderboard.Leaderboard;
|
||||
import com.sap.sailing.domain.leaderboard.RegattaLeaderboard;
|
||||
import com.sap.sailing.domain.racelogtracking.PingDeviceIdentifierImpl;
|
||||
import com.sap.sailing.domain.racelogtracking.RaceLogTrackingAdapter;
|
||||
import com.sap.sailing.domain.tracking.RaceHandle;
|
||||
import com.sap.sailing.domain.tracking.RaceTrackingHandler;
|
||||
@@ -203,11 +202,10 @@ public class RaceLogTrackingAdapterImpl implements RaceLogTrackingAdapter {
|
||||
if (course != null) {
|
||||
course.getWaypoints().forEach(wp -> Util.addAll(wp.getMarks(), marks));
|
||||
}
|
||||
|
||||
for (RaceLog toRaceLog : toRaceLogs) {
|
||||
if (new RaceLogTrackingStateAnalyzer(toRaceLog).analyze().isForTracking()) {
|
||||
if (course != null) {
|
||||
CourseBase newCourse = new CourseDataImpl(course.getName());
|
||||
CourseBase newCourse = new CourseDataImpl(course.getName(), course.getOriginatingCourseTemplateIdOrNull());
|
||||
TimePoint now = MillisecondsTimePoint.now();
|
||||
int i = 0;
|
||||
for (Waypoint oldWaypoint : course.getWaypoints()) {
|
||||
@@ -220,7 +218,6 @@ public class RaceLogTrackingAdapterImpl implements RaceLogTrackingAdapter {
|
||||
passId, newCourse, CourseDesignerMode.ADMIN_CONSOLE);
|
||||
toRaceLog.add(newCourseEvent);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -2,8 +2,8 @@ package com.sap.sailing.domain.racelogtracking.impl;
|
||||
|
||||
import com.sap.sailing.domain.common.DeviceIdentifier;
|
||||
import com.sap.sailing.domain.common.racelog.tracking.TransformationException;
|
||||
import com.sap.sailing.domain.persistence.racelog.tracking.DeviceIdentifierMongoHandler;
|
||||
import com.sap.sailing.domain.racelogtracking.SmartphoneUUIDSerializationHandler;
|
||||
import com.sap.sailing.shared.persistence.device.DeviceIdentifierMongoHandler;
|
||||
|
||||
public class SmartphoneUUIDMongoHandler extends SmartphoneUUIDSerializationHandler
|
||||
implements DeviceIdentifierMongoHandler {
|
||||
|
||||
+1
-1
@@ -6,9 +6,9 @@ import org.bson.Document;
|
||||
|
||||
import com.sap.sailing.domain.common.DeviceIdentifier;
|
||||
import com.sap.sailing.domain.common.racelog.tracking.TransformationException;
|
||||
import com.sap.sailing.domain.persistence.racelog.tracking.DeviceIdentifierMongoHandler;
|
||||
import com.sap.sailing.domain.trackfiles.TrackFileImportDeviceIdentifier;
|
||||
import com.sap.sailing.domain.trackfiles.TrackFileImportDeviceIdentifierImpl;
|
||||
import com.sap.sailing.shared.persistence.device.DeviceIdentifierMongoHandler;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.common.Util;
|
||||
|
||||
|
||||
+154
@@ -0,0 +1,154 @@
|
||||
package com.sap.sailing.domain.coursetemplate.test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.junit.Assume.assumeNoException;
|
||||
|
||||
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.common.MarkType;
|
||||
import com.sap.sailing.domain.common.PassingInstruction;
|
||||
import com.sap.sailing.domain.coursetemplate.ControlPointTemplate;
|
||||
import com.sap.sailing.domain.coursetemplate.CourseTemplate;
|
||||
import com.sap.sailing.domain.coursetemplate.MarkRole;
|
||||
import com.sap.sailing.domain.coursetemplate.MarkTemplate;
|
||||
import com.sap.sailing.domain.coursetemplate.WaypointTemplate;
|
||||
import com.sap.sailing.domain.coursetemplate.impl.CourseTemplateImpl;
|
||||
import com.sap.sailing.domain.coursetemplate.impl.MarkRoleImpl;
|
||||
import com.sap.sailing.domain.coursetemplate.impl.MarkRolePairImpl;
|
||||
import com.sap.sailing.domain.coursetemplate.impl.MarkTemplateImpl;
|
||||
import com.sap.sailing.domain.coursetemplate.impl.RepeatablePartImpl;
|
||||
import com.sap.sailing.domain.coursetemplate.impl.WaypointTemplateImpl;
|
||||
import com.sap.sse.common.Util;
|
||||
|
||||
public class TestLapConfiguration {
|
||||
private CourseTemplate courseTemplate;
|
||||
private MarkTemplate startBoat;
|
||||
private MarkRole startBoatRole;
|
||||
private MarkTemplate pin;
|
||||
private MarkRole pinRole;
|
||||
private MarkTemplate top;
|
||||
private MarkRole one;
|
||||
private MarkTemplate gateLeft;
|
||||
private MarkRole threeP;
|
||||
private MarkTemplate gateRight;
|
||||
private MarkRole threeS;
|
||||
private ControlPointTemplate startFinish;
|
||||
private ControlPointTemplate gate;
|
||||
private Map<MarkRole, MarkTemplate> defaultMarkTemplatesForMarkRoles;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
startBoat = new MarkTemplateImpl("Start Boat", "SB", /* color */ null, /* shape */ null, /* pattern */ null, MarkType.STARTBOAT);
|
||||
startBoatRole = new MarkRoleImpl(UUID.randomUUID(), "Start Boat", "SB");
|
||||
pin = new MarkTemplateImpl("Pin End", "Pin", /* color */ null, /* shape */ null, /* pattern */ null, MarkType.BUOY);
|
||||
pinRole = new MarkRoleImpl(UUID.randomUUID(), "Pin End", "P");
|
||||
top = new MarkTemplateImpl("Windward Mark", "1", /* color */ null, /* shape */ null, /* pattern */ null, MarkType.BUOY);
|
||||
one = new MarkRoleImpl(UUID.randomUUID(), "Windward Mark", "1");
|
||||
gateLeft = new MarkTemplateImpl("Leeward Gate Port", "4p", /* color */ null, /* shape */ null, /* pattern */ null, MarkType.BUOY);
|
||||
threeP = new MarkRoleImpl(UUID.randomUUID(), "Leeward Gate Port", "3p");
|
||||
gateRight = new MarkTemplateImpl("Leeward Gate Starboard", "4s", /* color */ null, /* shape */ null, /* pattern */ null, MarkType.BUOY);
|
||||
threeS = new MarkRoleImpl(UUID.randomUUID(), "Leeward Gate Starboard", "3s");
|
||||
startFinish = new MarkRolePairImpl("Start/Finish", "S/F", startBoatRole, pinRole);
|
||||
gate = new MarkRolePairImpl("Leeward Gate", "LG", threeP, threeS);
|
||||
defaultMarkTemplatesForMarkRoles = new HashMap<>();
|
||||
defaultMarkTemplatesForMarkRoles.put(startBoatRole, startBoat);
|
||||
defaultMarkTemplatesForMarkRoles.put(pinRole, pin);
|
||||
defaultMarkTemplatesForMarkRoles.put(one, top);
|
||||
defaultMarkTemplatesForMarkRoles.put(threeP, gateLeft);
|
||||
defaultMarkTemplatesForMarkRoles.put(threeS, gateRight);
|
||||
courseTemplate = new CourseTemplateImpl(UUID.randomUUID(), "Windward/Leeward", "L",
|
||||
/* marks */ Arrays.asList(startBoat, pin, top, gateLeft, gateRight),
|
||||
/* waypoints */ Arrays.asList(new WaypointTemplateImpl(startFinish, PassingInstruction.Line),
|
||||
new WaypointTemplateImpl(one, PassingInstruction.Port),
|
||||
new WaypointTemplateImpl(gate, PassingInstruction.Gate),
|
||||
new WaypointTemplateImpl(one, PassingInstruction.Port),
|
||||
new WaypointTemplateImpl(startFinish, PassingInstruction.Line)),
|
||||
defaultMarkTemplatesForMarkRoles,
|
||||
/* associatedRoles */ Collections.emptyMap(),
|
||||
/* optionaImageURL */ null, new RepeatablePartImpl(/* zeroBasedIndexOfRepeatablePartStart */ 1,
|
||||
/* zeroBasedIndexOfRepeatablePartEnd */ 3), /* Default Number of Laps */ 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleCourseWithOneLap() {
|
||||
final Iterable<WaypointTemplate> waypointsOfOneLapper = courseTemplate.getWaypointTemplates(1);
|
||||
final List<ControlPointTemplate> expected = Arrays.asList(startFinish, one, startFinish);
|
||||
verify(waypointsOfOneLapper, expected);
|
||||
}
|
||||
|
||||
private void verify(final Iterable<WaypointTemplate> waypointsOfOneLapper,
|
||||
final List<ControlPointTemplate> expected) {
|
||||
assertEquals(expected.size(), Util.size(waypointsOfOneLapper));
|
||||
final List<ControlPointTemplate> controlPoints = new ArrayList<>();
|
||||
for (final WaypointTemplate waypoint : waypointsOfOneLapper) {
|
||||
controlPoints.add(waypoint.getControlPointTemplate());
|
||||
}
|
||||
assertEquals(expected, controlPoints);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleCourseWithTwoLaps() {
|
||||
final Iterable<WaypointTemplate> waypointsOfOneLapper = courseTemplate.getWaypointTemplates(2);
|
||||
final List<ControlPointTemplate> expected = Arrays.asList(startFinish, one, gate, one, startFinish);
|
||||
verify(waypointsOfOneLapper, expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleCourseWithThreeLaps() {
|
||||
final Iterable<WaypointTemplate> waypointsOfOneLapper = courseTemplate.getWaypointTemplates(3);
|
||||
final List<ControlPointTemplate> expected = Arrays.asList(startFinish, one, gate, one, gate, one, startFinish);
|
||||
verify(waypointsOfOneLapper, expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExceptionInCaseMarkIsMissing() {
|
||||
startBoat = new MarkTemplateImpl("Start Boat", "SB", /* color */ null, /* shape */ null, /* pattern */ null, MarkType.STARTBOAT);
|
||||
pin = new MarkTemplateImpl("Pin End", "Pin", /* color */ null, /* shape */ null, /* pattern */ null, MarkType.BUOY);
|
||||
startFinish = new MarkRolePairImpl("Start/Finish","Start/Finish", startBoatRole, pinRole);
|
||||
try {
|
||||
courseTemplate = new CourseTemplateImpl("Test", "T",
|
||||
/* marks */ Arrays.asList(startBoat),
|
||||
/* waypoints */ Arrays.asList(new WaypointTemplateImpl(startFinish, PassingInstruction.Line)),
|
||||
/* defaultMarkRolesForMarkTemplates */ Collections.emptyMap(),
|
||||
/* defaultMarkTemplatesForRoles */ Collections.emptyMap(), /* optionaImageURL */ null);
|
||||
fail("Expected an IllegalArgumentException due to missing mark <pin> but it wasn't thrown");
|
||||
} catch (IllegalArgumentException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testZeroLapsOkIfNoRepeatablePart() {
|
||||
courseTemplate = new CourseTemplateImpl("Test", "T",
|
||||
/* marks */ Arrays.asList(startBoat, pin),
|
||||
/* waypoints */ Arrays.asList(new WaypointTemplateImpl(startFinish, PassingInstruction.Line)),
|
||||
defaultMarkTemplatesForMarkRoles,
|
||||
/* defaultMarkTemplatesForRoles */ Collections.emptyMap(), /* optionaImageURL */ null);
|
||||
try {
|
||||
courseTemplate.getWaypointTemplates(0);
|
||||
} catch (IllegalArgumentException e) {
|
||||
assumeNoException("No IllegalArgumentException should have been thrown for zero laps because the course has no repeatable part", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIllegalArgumentExceptionForZeroLaps() {
|
||||
try {
|
||||
courseTemplate
|
||||
.getWaypointTemplates(/* illegal to request 0 laps if course template defines repeatable part */ 0);
|
||||
fail("Expected an IllegalArgumentException but none was thrown");
|
||||
} catch (IllegalArgumentException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -50,6 +50,8 @@ Export-Package: com.sap.sailing.domain.abstractlog,
|
||||
com.sap.sailing.domain.base.impl,
|
||||
com.sap.sailing.domain.base.racegroup,
|
||||
com.sap.sailing.domain.base.racegroup.impl,
|
||||
com.sap.sailing.domain.coursetemplate,
|
||||
com.sap.sailing.domain.coursetemplate.impl;
|
||||
com.sap.sailing.domain.racelogtracking,
|
||||
com.sap.sailing.domain.racelogtracking.impl,
|
||||
com.sap.sailing.domain.statistics,
|
||||
|
||||
+2
@@ -4,4 +4,6 @@ import com.sap.sse.common.NamedWithID;
|
||||
|
||||
public interface ControlPoint extends NamedWithID {
|
||||
Iterable<Mark> getMarks();
|
||||
|
||||
String getShortName();
|
||||
}
|
||||
+19
-1
@@ -1,7 +1,10 @@
|
||||
package com.sap.sailing.domain.base;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.sap.sailing.domain.coursetemplate.MarkRole;
|
||||
import com.sap.sse.common.Named;
|
||||
|
||||
/**
|
||||
@@ -47,8 +50,23 @@ public interface CourseBase extends Named {
|
||||
void removeWaypoint(int zeroBasedPosition);
|
||||
|
||||
Leg getFirstLeg();
|
||||
|
||||
/**
|
||||
* @return the id of the course template that was used to create this course. This reference can remain in place
|
||||
* even if the actual course is being manipulated in ways incompatible with the original course template.
|
||||
*/
|
||||
UUID getOriginatingCourseTemplateIdOrNull();
|
||||
|
||||
/**
|
||||
* @return an unmodifiable map with the IDs of the associated roles required for checking course template validity
|
||||
* and when creating a new course template from this course. See also {@link MarkRole}.
|
||||
*/
|
||||
Map<Mark, UUID> getAssociatedRoles();
|
||||
|
||||
void addRoleMapping(Mark mark, UUID role);
|
||||
|
||||
default String internalToString() {
|
||||
StringBuilder result = new StringBuilder(getName());
|
||||
StringBuilder result = new StringBuilder(getName()==null?"":getName());
|
||||
result.append(": ");
|
||||
boolean first = true;
|
||||
for (Waypoint waypoint : getWaypoints()) {
|
||||
|
||||
+7
-7
@@ -1,7 +1,8 @@
|
||||
package com.sap.sailing.domain.base;
|
||||
|
||||
import com.sap.sailing.domain.common.MarkType;
|
||||
import com.sap.sse.common.Color;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.sap.sailing.domain.coursetemplate.CommonMarkProperties;
|
||||
import com.sap.sse.common.IsManagedByCache;
|
||||
|
||||
|
||||
@@ -12,9 +13,8 @@ import com.sap.sse.common.IsManagedByCache;
|
||||
* @author Axel Uhl (D043530)
|
||||
*
|
||||
*/
|
||||
public interface Mark extends ControlPoint, IsManagedByCache<SharedDomainFactory<?>> {
|
||||
public Color getColor();
|
||||
public String getShape();
|
||||
public String getPattern();
|
||||
public MarkType getType();
|
||||
public interface Mark extends CommonMarkProperties, ControlPoint, IsManagedByCache<SharedDomainFactory<?>> {
|
||||
UUID getOriginatingMarkTemplateIdOrNull();
|
||||
|
||||
UUID getOriginatingMarkPropertiesIdOrNull();
|
||||
}
|
||||
+21
-12
@@ -17,7 +17,7 @@ public interface SharedDomainFactory<RLR extends RaceLogResolver> extends Compet
|
||||
Nationality getOrCreateNationality(String threeLetterIOCCode);
|
||||
|
||||
/**
|
||||
* The name will also be used as the mark's ID. If you have a unique ID, use {@link #getOrCreateMark(Serializable, String)} instead.
|
||||
* The name will also be used as the mark's ID. If you have a unique ID, use {@link #getOrCreateMark(Serializable, String, String)} instead.
|
||||
*/
|
||||
Mark getOrCreateMark(String name);
|
||||
|
||||
@@ -30,32 +30,39 @@ public interface SharedDomainFactory<RLR extends RaceLogResolver> extends Compet
|
||||
* <p>
|
||||
*
|
||||
* This method first looks up the actual ID whose string representation is <code>toStringRepresentationOfID</code>
|
||||
* and then calls {@link #getOrCreateMark(Serializable, String)} with the result and the <code>name</code>
|
||||
* and then calls {@link #getOrCreateMark(Serializable, String, String)} with the result and the <code>name</code>
|
||||
* parameter, or with <code>ToStringRepresentationOfID</code> and <code>name</code> in case the string
|
||||
* representation of the ID is not known. So in the latter case, the string is used as the ID for the new mark.
|
||||
* @param shortName TODO
|
||||
*/
|
||||
Mark getOrCreateMark(String toStringRepresentationOfID, String name);
|
||||
Mark getOrCreateMark(String toStringRepresentationOfID, String name, String shortName);
|
||||
|
||||
Mark getOrCreateMark(Serializable id, String name);
|
||||
Mark getOrCreateMark(Serializable id, String name, String shortName);
|
||||
Mark getOrCreateMark(Serializable id, String name, String shortName, UUID originatingMarkTemplateId, UUID originatingMarkPropertiesId);
|
||||
|
||||
/**
|
||||
* If the single mark with ID <code>id</code> already exists, it is returned. Its color may differ from <code>color</code>
|
||||
* in that case. Otherwise, a new {@link Mark} is created with <code>color</code> as its {@link Mark#getColor()}
|
||||
* and <code>shape</code> as its {@link Mark#getShape()}.
|
||||
*/
|
||||
Mark getOrCreateMark(Serializable id, String name, MarkType type, Color color, String shape, String pattern);
|
||||
|
||||
Mark getOrCreateMark(Serializable id, String name, String shortName, MarkType type, Color color, String shape, String pattern);
|
||||
|
||||
Mark getOrCreateMark(Serializable id, String name, String shortName, MarkType type, Color color, String shape,
|
||||
String pattern, UUID originatingMarkTemplateId, UUID originatingMarkPropertiesId);
|
||||
|
||||
/**
|
||||
* @see #getOrCreateMark(String, String)
|
||||
* @param shortName TODO
|
||||
* @see #getOrCreateMark(String, String, String)
|
||||
*/
|
||||
Mark getOrCreateMark(String toStringRepresentationOfID, String name, MarkType type, Color color, String shape, String pattern);
|
||||
Mark getOrCreateMark(String toStringRepresentationOfID, String name, String shortName, MarkType type, Color color, String shape, String pattern);
|
||||
|
||||
/**
|
||||
* @param name also uses the name as the gate's ID; if you have a real ID, use {@link #createControlPointWithTwoMarks(Serializable, Mark, Mark, String)} instead
|
||||
*/
|
||||
ControlPointWithTwoMarks createControlPointWithTwoMarks(Mark left, Mark right, String name);
|
||||
ControlPointWithTwoMarks createControlPointWithTwoMarks(Mark left, Mark right, String name, String shortName);
|
||||
|
||||
ControlPointWithTwoMarks createControlPointWithTwoMarks(Serializable id, Mark left, Mark right, String name);
|
||||
ControlPointWithTwoMarks createControlPointWithTwoMarks(Serializable id, Mark left, Mark right, String name,
|
||||
String shortName);
|
||||
|
||||
/**
|
||||
* The waypoint created is weakly cached so that when requested again by
|
||||
@@ -103,9 +110,11 @@ public interface SharedDomainFactory<RLR extends RaceLogResolver> extends Compet
|
||||
|
||||
Mark getExistingMarkById(Serializable id);
|
||||
|
||||
ControlPointWithTwoMarks getOrCreateControlPointWithTwoMarks(Serializable id, String name, Mark left, Mark right);
|
||||
ControlPointWithTwoMarks getOrCreateControlPointWithTwoMarks(Serializable id, String name, Mark left, Mark right,
|
||||
String shortName);
|
||||
|
||||
ControlPointWithTwoMarks getOrCreateControlPointWithTwoMarks(String id, String name, Mark left, Mark right);
|
||||
ControlPointWithTwoMarks getOrCreateControlPointWithTwoMarks(String id, String name, Mark left, Mark right,
|
||||
String shortName);
|
||||
|
||||
RLR getRaceLogResolver();
|
||||
|
||||
|
||||
+2
-2
@@ -79,11 +79,11 @@ public class DeviceConfigurationImpl extends NamedImpl implements DeviceConfigur
|
||||
|
||||
@Override
|
||||
public QualifiedObjectIdentifier getIdentifier() {
|
||||
return getType().getQualifiedObjectIdentifier(getTypeRelativeObjectIdentifier());
|
||||
return getPermissionType().getQualifiedObjectIdentifier(getTypeRelativeObjectIdentifier());
|
||||
}
|
||||
|
||||
@Override
|
||||
public HasPermissions getType() {
|
||||
public HasPermissions getPermissionType() {
|
||||
return SecuredDomainType.RACE_MANAGER_APP_DEVICE_CONFIGURATION;
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -129,11 +129,11 @@ public class BoatImpl extends RenamableImpl implements DynamicBoat {
|
||||
|
||||
@Override
|
||||
public QualifiedObjectIdentifier getIdentifier() {
|
||||
return getType().getQualifiedObjectIdentifier(getTypeRelativeObjectIdentifier());
|
||||
return getPermissionType().getQualifiedObjectIdentifier(getTypeRelativeObjectIdentifier());
|
||||
}
|
||||
|
||||
@Override
|
||||
public HasPermissions getType() {
|
||||
public HasPermissions getPermissionType() {
|
||||
return SecuredDomainType.BOAT;
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -257,11 +257,11 @@ public class CompetitorImpl implements DynamicCompetitor {
|
||||
|
||||
@Override
|
||||
public QualifiedObjectIdentifier getIdentifier() {
|
||||
return getType().getQualifiedObjectIdentifier(getTypeRelativeObjectIdentifier());
|
||||
return getPermissionType().getQualifiedObjectIdentifier(getTypeRelativeObjectIdentifier());
|
||||
}
|
||||
|
||||
@Override
|
||||
public HasPermissions getType() {
|
||||
public HasPermissions getPermissionType() {
|
||||
return SecuredDomainType.COMPETITOR;
|
||||
}
|
||||
|
||||
|
||||
+22
-3
@@ -8,24 +8,39 @@ import com.sap.sailing.domain.base.ControlPointWithTwoMarks;
|
||||
import com.sap.sailing.domain.base.Mark;
|
||||
|
||||
public class ControlPointWithTwoMarksImpl implements ControlPointWithTwoMarks {
|
||||
private static final long serialVersionUID = 2807354812133070574L;
|
||||
private static final long serialVersionUID = -6407078290366911597L;
|
||||
private final Mark left;
|
||||
private final Mark right;
|
||||
private final String name;
|
||||
private final Serializable id;
|
||||
private final String shortName;
|
||||
|
||||
/**
|
||||
* @param name also used as ID for the mark; if you have a better ID, use {@link GateImpl(Serializable, Mark, Mark, String)} instead.
|
||||
*/
|
||||
|
||||
/** Use {@link #ControlPointWithTwoMarksImpl(Mark, Mark, String, String)} instead. */
|
||||
@Deprecated
|
||||
public ControlPointWithTwoMarksImpl(Mark left, Mark right, String name) {
|
||||
this(/* ID */ name, left, right, name);
|
||||
this(/* ID */ name, left, right, name, name);
|
||||
}
|
||||
|
||||
/** Use {@link #ControlPointWithTwoMarksImpl(Serializable, Mark, Mark, String, String)} instead. */
|
||||
@Deprecated
|
||||
public ControlPointWithTwoMarksImpl(Serializable id, Mark left, Mark right, String name) {
|
||||
this(id, left, right, name, name);
|
||||
}
|
||||
|
||||
public ControlPointWithTwoMarksImpl(Mark left, Mark right, String name, String shortName) {
|
||||
this(/* ID */ name, left, right, name, shortName);
|
||||
}
|
||||
|
||||
public ControlPointWithTwoMarksImpl(Serializable id, Mark left, Mark right, String name) {
|
||||
public ControlPointWithTwoMarksImpl(Serializable id, Mark left, Mark right, String name, String shortName) {
|
||||
this.id = id;
|
||||
this.left = left;
|
||||
this.right = right;
|
||||
this.name = name;
|
||||
this.shortName = shortName;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -60,4 +75,8 @@ public class ControlPointWithTwoMarksImpl implements ControlPointWithTwoMarks {
|
||||
public Serializable getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getShortName() {
|
||||
return shortName;
|
||||
}
|
||||
}
|
||||
+28
-2
@@ -1,23 +1,36 @@
|
||||
package com.sap.sailing.domain.base.impl;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.sap.sailing.domain.base.ControlPoint;
|
||||
import com.sap.sailing.domain.base.CourseBase;
|
||||
import com.sap.sailing.domain.base.Leg;
|
||||
import com.sap.sailing.domain.base.Mark;
|
||||
import com.sap.sailing.domain.base.Waypoint;
|
||||
import com.sap.sse.common.impl.NamedImpl;
|
||||
|
||||
public class CourseDataImpl extends NamedImpl implements CourseBase {
|
||||
|
||||
private static final long serialVersionUID = 1389216430392253253L;
|
||||
|
||||
private static final long serialVersionUID = 2749443048689453078L;
|
||||
|
||||
private final List<Waypoint> waypoints;
|
||||
|
||||
private final UUID originatingCourseTemplateId;
|
||||
|
||||
private final Map<Mark, UUID> associatedRoles = new HashMap<>();
|
||||
|
||||
public CourseDataImpl(String name) {
|
||||
this(name, null);
|
||||
}
|
||||
|
||||
public CourseDataImpl(String name, UUID originatingCourseTemplateId) {
|
||||
super(name);
|
||||
this.waypoints = new ArrayList<Waypoint>();
|
||||
this.originatingCourseTemplateId = originatingCourseTemplateId;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -73,6 +86,19 @@ public class CourseDataImpl extends NamedImpl implements CourseBase {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getOriginatingCourseTemplateIdOrNull() {
|
||||
return originatingCourseTemplateId;
|
||||
}
|
||||
|
||||
public void addRoleMapping(Mark mark, UUID role) {
|
||||
associatedRoles.put(mark, role);
|
||||
}
|
||||
|
||||
public Map<Mark, UUID> getAssociatedRoles() {
|
||||
return associatedRoles;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return internalToString();
|
||||
|
||||
+2
-2
@@ -384,11 +384,11 @@ public abstract class EventBaseImpl implements EventBase {
|
||||
|
||||
@Override
|
||||
public QualifiedObjectIdentifier getIdentifier() {
|
||||
return getType().getQualifiedObjectIdentifier(getTypeRelativeObjectIdentifier());
|
||||
return getPermissionType().getQualifiedObjectIdentifier(getTypeRelativeObjectIdentifier());
|
||||
}
|
||||
|
||||
@Override
|
||||
public HasPermissions getType() {
|
||||
public HasPermissions getPermissionType() {
|
||||
return SecuredDomainType.EVENT;
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -68,7 +68,7 @@ public abstract class LeaderboardGroupBaseImpl implements LeaderboardGroupBase {
|
||||
}
|
||||
|
||||
public QualifiedObjectIdentifier getIdentifier() {
|
||||
return getType().getQualifiedObjectIdentifier(getTypeRelativeObjectIdentifier());
|
||||
return getPermissionType().getQualifiedObjectIdentifier(getTypeRelativeObjectIdentifier());
|
||||
}
|
||||
|
||||
public TypeRelativeObjectIdentifier getTypeRelativeObjectIdentifier() {
|
||||
@@ -80,7 +80,7 @@ public abstract class LeaderboardGroupBaseImpl implements LeaderboardGroupBase {
|
||||
}
|
||||
|
||||
@Override
|
||||
public HasPermissions getType() {
|
||||
public HasPermissions getPermissionType() {
|
||||
return SecuredDomainType.LEADERBOARD_GROUP;
|
||||
}
|
||||
|
||||
|
||||
+37
-2
@@ -2,6 +2,7 @@ package com.sap.sailing.domain.base.impl;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collections;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.sap.sailing.domain.base.Mark;
|
||||
import com.sap.sailing.domain.base.SharedDomainFactory;
|
||||
@@ -10,13 +11,17 @@ import com.sap.sse.common.Color;
|
||||
import com.sap.sse.common.impl.NamedImpl;
|
||||
|
||||
public class MarkImpl extends NamedImpl implements Mark {
|
||||
private static final long serialVersionUID = 1900673146064411979L;
|
||||
|
||||
private static final long serialVersionUID = -4817053057647606900L;
|
||||
|
||||
private final Color color;
|
||||
private final String shape;
|
||||
private final String pattern;
|
||||
private final MarkType type;
|
||||
private final String shortName;
|
||||
private final Serializable id;
|
||||
private final UUID originatingMarkTemplateId;
|
||||
private final UUID originatingMarkPropertiesId;
|
||||
|
||||
public MarkImpl(String name) {
|
||||
this(name, name);
|
||||
@@ -27,12 +32,27 @@ public class MarkImpl extends NamedImpl implements Mark {
|
||||
}
|
||||
|
||||
public MarkImpl(Serializable id, String name, MarkType type, Color color, String shape, String pattern) {
|
||||
this(id, name, /* use name as short name, too, by default */ name, type, color, shape, pattern,
|
||||
/* originatingMarkTemplateId */ null, /* originatingMarkPropertiesId */ null);
|
||||
}
|
||||
|
||||
public MarkImpl(Serializable id, String name, MarkType type, Color color, String shape, String pattern,
|
||||
UUID originatingMarkTemplateId, UUID originatingMarkPropertiesId) {
|
||||
this(id, name, /* use name as short name, too, by default */ name, type, color, shape, pattern,
|
||||
originatingMarkTemplateId, originatingMarkPropertiesId);
|
||||
}
|
||||
|
||||
public MarkImpl(Serializable id, String name, String shortName, MarkType type, Color color, String shape,
|
||||
String pattern, UUID originatingMarkTemplateId, UUID originatingMarkPropertiesId) {
|
||||
super(name);
|
||||
this.shortName = shortName;
|
||||
this.id = id;
|
||||
this.type = type;
|
||||
this.color = color;
|
||||
this.shape = shape;
|
||||
this.pattern = pattern;
|
||||
this.originatingMarkTemplateId = originatingMarkTemplateId;
|
||||
this.originatingMarkPropertiesId = originatingMarkPropertiesId;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -49,7 +69,7 @@ public class MarkImpl extends NamedImpl implements Mark {
|
||||
|
||||
@Override
|
||||
public Mark resolve(SharedDomainFactory<?> domainFactory) {
|
||||
Mark result = domainFactory.getOrCreateMark(getId(), getName(), type, color, shape, pattern);
|
||||
Mark result = domainFactory.getOrCreateMark(getId(), getName(), shortName, type, color, shape, pattern);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -73,6 +93,21 @@ public class MarkImpl extends NamedImpl implements Mark {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getShortName() {
|
||||
return shortName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getOriginatingMarkTemplateIdOrNull() {
|
||||
return originatingMarkTemplateId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getOriginatingMarkPropertiesIdOrNull() {
|
||||
return originatingMarkPropertiesId;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return getId() + " " + (getColor() == null ? "" : (getColor() + " ")) + super.toString();
|
||||
}
|
||||
|
||||
+46
-19
@@ -147,7 +147,7 @@ public class SharedDomainFactoryImpl<RLR extends RaceLogResolver> implements Sha
|
||||
|
||||
@Override
|
||||
public Mark getOrCreateMark(String name) {
|
||||
return getOrCreateMark(name, name);
|
||||
return getOrCreateMark(name, name, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -157,21 +157,29 @@ public class SharedDomainFactoryImpl<RLR extends RaceLogResolver> implements Sha
|
||||
|
||||
@Override
|
||||
public Mark getOrCreateMark(Serializable id, String name, MarkType markType) {
|
||||
return getOrCreateMark(id, name, markType, /* color */ null, /* shape */ null, /* pattern */ null);
|
||||
return getOrCreateMark(id, name, /* no separate short name available here */ name, markType, /* color */ null, /* shape */ null, /* pattern */ null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mark getOrCreateMark(Serializable id, String name) {
|
||||
return getOrCreateMark(id, name, /* type */ null, /* color */ null, /* shape */ null, /* pattern */ null);
|
||||
public Mark getOrCreateMark(Serializable id, String name, String shortName) {
|
||||
return getOrCreateMark(id, name, shortName, /* originatingMarkTemplateId */ null,
|
||||
/* originatingMarkPropertiesId */ null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mark getOrCreateMark(String toStringRepresentationOfID, String name) {
|
||||
return getOrCreateMark(toStringRepresentationOfID, name, /* type */ null, /* color */ null, /* shape */ null, /* pattern */ null);
|
||||
public Mark getOrCreateMark(Serializable id, String name, String shortName, UUID originatingMarkTemplateId,
|
||||
UUID originatingMarkPropertiesId) {
|
||||
return getOrCreateMark(id, name, shortName, /* type */ null, /* color */ null, /* shape */ null,
|
||||
/* pattern */ null, originatingMarkTemplateId, originatingMarkPropertiesId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mark getOrCreateMark(String toStringRepresentationOfID, String name, String shortName) {
|
||||
return getOrCreateMark(toStringRepresentationOfID, name, shortName, /* type */ null, /* color */ null, /* shape */ null, /* pattern */ null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mark getOrCreateMark(Serializable id, String name, MarkType type, Color color, String shape, String pattern) {
|
||||
public Mark getOrCreateMark(Serializable id, String name, String shortName, MarkType type, Color color, String shape, String pattern) {
|
||||
Mark result = markCache.get(id);
|
||||
if (result == null) {
|
||||
result = new MarkImpl(id, name, type, color, shape, pattern);
|
||||
@@ -179,37 +187,51 @@ public class SharedDomainFactoryImpl<RLR extends RaceLogResolver> implements Sha
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Mark getOrCreateMark(String toStringRepresentationOfID, String name, MarkType type,
|
||||
Color color, String shape, String pattern) {
|
||||
public Mark getOrCreateMark(Serializable id, String name, String shortName, MarkType type, Color color,
|
||||
String shape, String pattern, UUID originatingMarkTemplateId,
|
||||
UUID originatingMarkPropertiesId) {
|
||||
Mark result = markCache.get(id);
|
||||
if (result == null) {
|
||||
result = new MarkImpl(id, name, shortName, type, color, shape, pattern, originatingMarkTemplateId,
|
||||
originatingMarkPropertiesId);
|
||||
cacheMark(id, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mark getOrCreateMark(String toStringRepresentationOfID, String name, String shortName,
|
||||
MarkType type, Color color, String shape, String pattern) {
|
||||
Serializable id = toStringRepresentationOfID;
|
||||
if (markIdCache.containsKey(toStringRepresentationOfID)) {
|
||||
id = markIdCache.get(toStringRepresentationOfID);
|
||||
}
|
||||
return getOrCreateMark(id, name, type, color, shape, pattern);
|
||||
return getOrCreateMark(id, name, shortName, type, color, shape, pattern);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ControlPointWithTwoMarks getOrCreateControlPointWithTwoMarks(Serializable id, String name, Mark left, Mark right) {
|
||||
public ControlPointWithTwoMarks getOrCreateControlPointWithTwoMarks(Serializable id, String name, Mark left,
|
||||
Mark right, String shortName) {
|
||||
final ControlPointWithTwoMarks result;
|
||||
final ControlPointWithTwoMarks fromCache = controlPointWithTwoMarksCache.get(id);
|
||||
if (fromCache != null) {
|
||||
result = fromCache;
|
||||
} else {
|
||||
result = createControlPointWithTwoMarks(id, left, right, name);
|
||||
result = createControlPointWithTwoMarks(id, left, right, name, shortName);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ControlPointWithTwoMarks getOrCreateControlPointWithTwoMarks(
|
||||
String toStringRepresentationOfID, String name, Mark left, Mark right) {
|
||||
String toStringRepresentationOfID, String name, Mark left, Mark right, String shortName) {
|
||||
Serializable id = toStringRepresentationOfID;
|
||||
if (controlPointWithTwoMarksIdCache.containsKey(toStringRepresentationOfID)) {
|
||||
id = controlPointWithTwoMarksIdCache.get(toStringRepresentationOfID);
|
||||
}
|
||||
return getOrCreateControlPointWithTwoMarks(id, name, left, right);
|
||||
return getOrCreateControlPointWithTwoMarks(id, name, left, right, shortName);
|
||||
}
|
||||
|
||||
private void cacheMark(Serializable id, Mark result) {
|
||||
@@ -218,13 +240,18 @@ public class SharedDomainFactoryImpl<RLR extends RaceLogResolver> implements Sha
|
||||
}
|
||||
|
||||
@Override
|
||||
public ControlPointWithTwoMarks createControlPointWithTwoMarks(Mark left, Mark right, String name) {
|
||||
return createControlPointWithTwoMarks(name, left, right, name);
|
||||
public ControlPointWithTwoMarks createControlPointWithTwoMarks(Mark left, Mark right, String name,
|
||||
String shortName) {
|
||||
return createControlPointWithTwoMarks(name, left, right, name, shortName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ControlPointWithTwoMarks createControlPointWithTwoMarks(Serializable id, Mark left, Mark right, String name) {
|
||||
ControlPointWithTwoMarks result = new ControlPointWithTwoMarksImpl(id, left, right, name);
|
||||
public ControlPointWithTwoMarks createControlPointWithTwoMarks(Serializable id, Mark left, Mark right, String name,
|
||||
String shortName) {
|
||||
if (shortName == null || shortName.isEmpty()) {
|
||||
shortName = name;
|
||||
}
|
||||
ControlPointWithTwoMarks result = new ControlPointWithTwoMarksImpl(id, left, right, name, shortName);
|
||||
controlPointWithTwoMarksCache.put(id, result);
|
||||
controlPointWithTwoMarksIdCache.put(id.toString(), id);
|
||||
return result;
|
||||
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package com.sap.sailing.domain.coursetemplate;
|
||||
|
||||
import com.sap.sailing.domain.base.Mark;
|
||||
import com.sap.sailing.domain.common.MarkType;
|
||||
import com.sap.sse.common.Color;
|
||||
import com.sap.sse.common.Named;
|
||||
import com.sap.sse.common.Util;
|
||||
|
||||
/**
|
||||
* The properties that a {@link Mark}, a {@link MarkTemplate}, and a {@link MarkProperties}
|
||||
* object have in common.
|
||||
*
|
||||
* @author Axel Uhl (d043530)
|
||||
*
|
||||
*/
|
||||
public interface CommonMarkProperties extends Named {
|
||||
Color getColor();
|
||||
String getShape();
|
||||
String getPattern();
|
||||
MarkType getType();
|
||||
String getShortName();
|
||||
|
||||
default boolean hasEqualAppeareanceWith(CommonMarkProperties properties) {
|
||||
return Util.equalsWithNull(getColor(), properties.getColor())
|
||||
&& Util.equalsWithNull(getShape(), properties.getShape())
|
||||
&& Util.equalsWithNull(getPattern(), properties.getPattern())
|
||||
&& Util.equalsWithNull(getType(), properties.getType())
|
||||
&& Util.equalsWithNull(getShortName(), properties.getShortName());
|
||||
};
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package com.sap.sailing.domain.coursetemplate;
|
||||
|
||||
import com.sap.sailing.domain.base.ControlPoint;
|
||||
import com.sap.sse.common.Named;
|
||||
|
||||
/**
|
||||
* What a {@link WaypointTemplate} references. It is a template for the construction of a {@link ControlPoint} and
|
||||
* references the {@link MarkRole}s that describe the "logical" marks. The {@link MarkTemplate}s from which the mark
|
||||
* properties are to be drawn during construction of the actual {@link ControlPoint} are provided by the
|
||||
* {@link CourseTemplate} (see {@link CourseTemplate#getDefaultMarkTemplateForMarkRole(MarkRole)}).
|
||||
*
|
||||
* @author Axel Uhl (d043530)
|
||||
*
|
||||
*/
|
||||
public interface ControlPointTemplate extends Named {
|
||||
Iterable<MarkRole> getMarkRoles();
|
||||
|
||||
String getShortName();
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
package com.sap.sailing.domain.coursetemplate;
|
||||
|
||||
import com.sap.sailing.domain.base.ControlPoint;
|
||||
import com.sap.sse.common.Named;
|
||||
|
||||
/**
|
||||
* What a {@link WaypointTemplate} references. It is a template for the construction of a {@link ControlPoint} and
|
||||
* references the {@link MarkTemplate}s from which this control point shall be constructed.
|
||||
*
|
||||
* @author Axel Uhl (d043530)
|
||||
*
|
||||
* @param <P>
|
||||
* type of the annotation used to convey positioning-related information on the {@link MarkConfiguration}
|
||||
* objects contains; typical instantiations would, e.g., be with {@link MarkConfigurationRequestAnnotation} and
|
||||
* {@link MarkConfigurationResponseAnnotation}.
|
||||
*/
|
||||
public interface ControlPointWithMarkConfiguration<P> extends Named {
|
||||
Iterable<MarkConfiguration<P>> getMarkConfigurations();
|
||||
|
||||
String getShortName();
|
||||
}
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
package com.sap.sailing.domain.coursetemplate;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.Map;
|
||||
|
||||
import com.sap.sailing.domain.abstractlog.regatta.RegattaLog;
|
||||
import com.sap.sailing.domain.base.Mark;
|
||||
import com.sap.sse.common.Named;
|
||||
|
||||
/**
|
||||
* A course that does not necessarily consist of {@link Mark}s existing in a regatta but instead is defined by
|
||||
* {@link MarkConfiguration}s. This means changes to the model are easily possible without requiring to pollute the
|
||||
* {@link RegattaLog}. The effective Marks will then be created upon Course creation based on the
|
||||
* {@link MarkConfiguration}s.
|
||||
* <p>
|
||||
*
|
||||
* This is the model represented in a course editor which deals with editing the waypoint sequence as well as changing
|
||||
* how marks are mapped to {@link MarkTemplate}s and how marks are generally configured in terms of their base
|
||||
* properties and tracking/positioning information. This configuration can reference a non-{@code null}
|
||||
* {@link CourseTemplate} (returned by {@link #getOptionalCourseTemplate()}). Being an "editor model" this means that
|
||||
* the actual {@link #getWaypoints() waypoint sequence} may be inconsistent with what the course template demands. When
|
||||
* requesting a course update, the waypoint sequence takes precedence over a course template. Likewise, the
|
||||
* {@link #getNumberOfLaps() number of laps} in a request to create a course may be inconsistent with the waypoint
|
||||
* sequence which then should be interpreted such that the request processor shall adjust the waypoint sequence such
|
||||
* that the number of laps requested is reflected in the waypoint sequence consistently.
|
||||
* <p>
|
||||
*
|
||||
* When used in a request to create a course template, the {@link #getRepeatablePart()} will be used as the
|
||||
* new course template's {@link CourseTemplate#getRepeatablePart() repeatable part}.<p>
|
||||
*
|
||||
* In particular, the base {@link #getWaypoints() waypoints sequence}, when returned by a request, has at least one
|
||||
* occurrence of a repeatable part, should the configuration specify a course template, be consistent with it, and
|
||||
* should the course template specify a repeatable part. As a response, this configuration will tell the
|
||||
* {@link #getNumberOfLaps()} found or created during the request.
|
||||
* <p>
|
||||
*
|
||||
* The configuration for all marks are returned by {@link #getAllMarks()} because when pushing this to a Regatta the
|
||||
* marks for all these configurations have to be established.
|
||||
*
|
||||
* @param <P>
|
||||
* the type used to {@link MarkConfiguration#getAnnotationInfo() annotate} the {@link MarkProperties} used in
|
||||
* this course configuration. Different annotations are expected to be used for course configurations used in
|
||||
* requests sent to a service and for responses produced by a service.
|
||||
*/
|
||||
public interface CourseConfiguration<P> extends WithOptionalRepeatablePart, Named {
|
||||
String getShortName();
|
||||
|
||||
/**
|
||||
* If this course configuration has been obtained from a {@link CourseTemplate} without modifying the sequence of
|
||||
* waypoints and their mapping to {@link MarkTemplate}s, this method will return that {@link CourseTemplate}. In all
|
||||
* other cases it will return {@code null}. If not {@code null}, the methods of {@link WithOptionalRepeatablePart}
|
||||
* delegate to the {@link CourseTemplate} returned by this method.
|
||||
*/
|
||||
CourseTemplate getOptionalCourseTemplate();
|
||||
|
||||
URL getOptionalImageURL();
|
||||
|
||||
/**
|
||||
* Returns all marks that are part of this course configuration. This consists of:
|
||||
* <ul>
|
||||
* <li>All {@link MarkConfiguration} that are used by at least one {@link WaypointWithMarkConfiguration}.</li>
|
||||
* <li>Any additional {@link MarkConfiguration} that is intended to be used as a spare mark.</li>
|
||||
* </ul>
|
||||
*/
|
||||
Iterable<MarkConfiguration<P>> getAllMarks();
|
||||
|
||||
/**
|
||||
* The waypoint sequence. If a repeatable part is specified, the sequence returned must contain at least one
|
||||
* occurrence of the repeatable part. All {@link MarkConfiguration} objects referenced are part of the result of
|
||||
* {@link #getAllMarks()}. If you want the waypoint sequence including as many occurrences
|
||||
* of any repeatable part, call {@link #getWaypoints(int) getWaypoints(}{@link #getNumberOfLaps() getNumberOfLaps())}.
|
||||
*/
|
||||
Iterable<WaypointWithMarkConfiguration<P>> getWaypoints();
|
||||
|
||||
/**
|
||||
* Returns all {@link MarkConfiguration MarkConfigurations} that are part of the {@link CourseConfigurationBase} and
|
||||
* have a role explicitly associated. A mark role links back to a {@link CourseTemplate} and makes it possible to
|
||||
* recognize congruence between the course described by this configuration and the course described by a course
|
||||
* template, regardless of the actual marks that are used in certain roles. This enables, for example, to use two
|
||||
* different marks for the same role in different laps and still find the course configuration to be congruent to
|
||||
* its original course template. This information is also useful when creating another course template from this
|
||||
* configuration ("save as...") because then the roles to which the mark configurations refer can be (re-)used in
|
||||
* the new course template constructed from this configuration.
|
||||
*/
|
||||
Map<MarkConfiguration<P>, MarkRole> getAssociatedRoles();
|
||||
|
||||
/**
|
||||
* Returns a map whose key set equals the result of {@link #getAllMarks()} and whose values are the result
|
||||
* of mapping the key through the result of {@link #getAssociatedRoles()}. If a mark configuration does not
|
||||
* have a role assigned, {@code null} will be the associated value in the map returned by this method.
|
||||
*/
|
||||
Map<MarkConfiguration<P>, MarkRole> getAllMarksWithOptionalRoles();
|
||||
|
||||
/**
|
||||
* A {@link CourseConfigurationBase} having a {@link #getOptionalCourseTemplate() course template} can optionally
|
||||
* also tell the number of laps in case this configuration is used to create or update a course. In a request this
|
||||
* number can be used to override the {@link CourseTemplate#getDefaultNumberOfLaps() default number of laps} that
|
||||
* the course template would use. Depending on the use-case this may be ignored (saving a course template) or is
|
||||
* required (creating a regatta course from a configuration with a course template that specifies a repeatable
|
||||
* part).
|
||||
* <p>
|
||||
*
|
||||
* In a response the number of laps is reported as {@code null} if a non-{@code null}
|
||||
* {@link #getOptionalCourseTemplate() course template} is provided but the course is not compatible with that
|
||||
* template; {@code -1} if there is no course template specified, or the course template has no
|
||||
* {@link CourseTemplate#getRepeatablePart() repeatable part}; otherwise the number of laps (number of occurrences
|
||||
* of the repeatable part plus one).
|
||||
*/
|
||||
Integer getNumberOfLaps();
|
||||
|
||||
/**
|
||||
* The waypoint sequence with lap count applied. If this course configuration has no {@link #hasRepeatablePart()
|
||||
* repeatable part}, the result is the same as that of {@link #getWaypoints()}. The result has
|
||||
* {@link MarkConfiguration configuration information} for the marks used. All {@link MarkConfiguration} objects
|
||||
* referenced are part of the result of {@link #getAllMarks()}.
|
||||
*
|
||||
* @param numberOfLaps
|
||||
* the repeatable part will be inserted {@code numberOfLaps-1} times. For example, a two-lap course will
|
||||
* have the repeatable part exactly once. Use {@link #getNumberOfLaps()} to
|
||||
* obtain the waypoint sequence as specified by this course configuration.
|
||||
*/
|
||||
Iterable<WaypointWithMarkConfiguration<P>> getWaypoints(int numberOfLaps);
|
||||
}
|
||||
+188
@@ -0,0 +1,188 @@
|
||||
package com.sap.sailing.domain.coursetemplate;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.sap.sailing.domain.base.Mark;
|
||||
import com.sap.sailing.domain.common.security.SecuredDomainType;
|
||||
import com.sap.sse.common.NamedWithUUID;
|
||||
import com.sap.sse.common.Util;
|
||||
import com.sap.sse.security.shared.HasPermissions;
|
||||
import com.sap.sse.security.shared.QualifiedObjectIdentifier;
|
||||
import com.sap.sse.security.shared.TypeRelativeObjectIdentifier;
|
||||
import com.sap.sse.security.shared.WithQualifiedObjectIdentifier;
|
||||
|
||||
/**
|
||||
* A {@link Course} can be created from this template. The template defines {@link MarkRole}s, {@link MarkTemplate}s,
|
||||
* {@link ControlPointTemplate}s, and {@link WaypointTemplate}s and assembles the latter into a sequence. The sequence
|
||||
* of waypoint templates can optionally have a {@link #getRepeatablePart() repeatable part}. When increasing or reducing
|
||||
* the number of laps, more or, respectively, fewer occurrences of this repeatable part will be inserted into the
|
||||
* {@link Course}.
|
||||
* <p>
|
||||
*
|
||||
* The course template can define additional mark templates that are not part of the waypoint template sequence. This
|
||||
* can be used, e.g., to define spare marks. Those additional {@link MarkTemplate}s can optionally tell a
|
||||
* {@link MarkRole} through the {@link #getDefaultMarkRolesForMarkTemplates()} map as their preferred role for which they
|
||||
* define a spare.
|
||||
* <p>
|
||||
*
|
||||
* All {@link MarkRole}s reachable through the {@link #getWaypointTemplates() waypoint template sequence} must refer to
|
||||
* a corresponding {@link MarkTemplate}. This link is represented by the {@link #getDefaultMarkTemplatesForMarkRoles()}
|
||||
* method. For all {@link MarkTemplate}s connected to a role in this way, {@link #getDefaultMarkRolesForMarkTemplates()}
|
||||
* must contain the reverse link.
|
||||
* <p>
|
||||
*
|
||||
* The course template has a {@link #getId() globally unique ID} and with this can have a life cycle. Its
|
||||
* {@link #getWaypointTemplates() waypoint template sequence} is immutable. If a logical copy is created, the new copy
|
||||
* will have a different ID ("Save as...").
|
||||
* <p>
|
||||
*
|
||||
* The {@link MarkTemplate}s and {@link MarkRole}s also have their globally unique IDs. When creating a variant, the new
|
||||
* copy can reference the <em>same</em> set or a subset of the mark templates that the original references. This is
|
||||
* helpful in case tracking options or bindings to physical marks are created and remembered. For example, a course
|
||||
* template for an "I" (inner loop) course and a course template for an "O" (outer loop) course may share the mark
|
||||
* templates for the start line and the "1" top mark, and when tracker bindings are established they can automatically
|
||||
* be applied to all marks created from the same {@link MarkTemplate}.
|
||||
*
|
||||
* @author Axel Uhl (d043530)
|
||||
*
|
||||
*/
|
||||
public interface CourseTemplate extends WithOptionalRepeatablePart, NamedWithUUID, HasTags, WithQualifiedObjectIdentifier {
|
||||
/**
|
||||
* A short name, as typically used in sailing instructions documents, such as "L" for a Windward/Leeward course template
|
||||
* with leeward finish. When combined with the number of laps, a well-known short name such as "L2" or "L3" can result.
|
||||
*/
|
||||
String getShortName();
|
||||
|
||||
/**
|
||||
* The templates for all the marks that shall be made available in the regatta when applying this template. All
|
||||
* marks required to construct the waypoint sequence must be produced from this set of mark templates. There may be
|
||||
* additional mark templates returned by this method, for constructing marks not immediately required for the
|
||||
* waypoint sequence but, e.g., as proposals for spare or alternative marks. For example, templates for alternative
|
||||
* marks for the windward mark may be returned to quickly accommodate for wind shifts.
|
||||
*/
|
||||
Iterable<MarkTemplate> getMarkTemplates();
|
||||
|
||||
/**
|
||||
* Returns all {@link MarkTemplate}s from {@link #getMarkTemplates()} which are not the default mark template
|
||||
* for {@link #getMarkRoles() any mark role used by this course template}.
|
||||
*/
|
||||
default Iterable<MarkTemplate> getMarkTemplatesNotIncludedInCourse() {
|
||||
final Set<MarkTemplate> result = new HashSet<>();
|
||||
Util.addAll(getMarkTemplates(), result);
|
||||
for (final MarkRole markRole : getMarkRoles()) {
|
||||
result.remove(getDefaultMarkTemplateForMarkRole(markRole));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* The set of {@link MarkRole}s one gets when enumerating all {@link #getWaypointTemplates() waypoint templates},
|
||||
* fetching their {@link WaypointTemplate#getControlPointTemplate() control point template} and from it all
|
||||
* {@link ControlPointTemplate#getMarkRoles() mark roles}.
|
||||
*/
|
||||
default Iterable<MarkRole> getMarkRoles() {
|
||||
final Set<MarkRole> result = new HashSet<>();
|
||||
for (final WaypointTemplate wpt : getWaypointTemplates()) {
|
||||
for (final MarkRole markRole : wpt.getControlPointTemplate().getMarkRoles()) {
|
||||
result.add(markRole);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
MarkTemplate getMarkTemplateByIdIfContainedInCourseTemplate(UUID markTemplateId);
|
||||
|
||||
/**
|
||||
* Returns a sequence of {@link WaypointTemplate}s that can be use to construct a course. If this course template
|
||||
* defines a repeatable waypoint sub-sequence, the {@code numberOfLaps} parameter is used to decide how many times
|
||||
* to repeat this sub-sequence. Typically, the repeatable sub-sequence will be repeated one times fewer than the
|
||||
* {@code numberOfLaps}. For example, in a typical windward-leeward "L" course we would have
|
||||
* {@code Start/Finish, [1, 4p/4s], 1, Start/Finish}. For an "L1" course with only one lap, we'd like to have
|
||||
* {@code Start/Finish, 1, Start/Finish}, so the repeatable sub-sequence, enclosed by the brackets in the example
|
||||
* above, will occur zero times. For an "L2" the repeatable sub-sequence will occur once, and so on. However, an
|
||||
* implementation is free to choose an interpretation of {@code numberOfLaps} that meets callers' expectations.
|
||||
* <p>
|
||||
*
|
||||
* Note that usually a value of {@code 1} for the {@code numberOfLaps} parameter will lead to no occurrence of a
|
||||
* repeatable part in the result. When building a course configuration for this course template, waypoint
|
||||
* configurations for at least one occurrence of the repeatable part are expected to be present, meaning that at
|
||||
* least a lap count of 2 would be required to meet that requirement.
|
||||
* <p>
|
||||
*
|
||||
* Note: this method is mostly intended for testing purposes; a client / UI would normally use a
|
||||
* {@link CourseConfiguration} that refers to a {@link CourseTemplate} and then invoke
|
||||
* {@link CourseConfiguration#getWaypoints(int)}.
|
||||
*
|
||||
* @param numberOfLaps
|
||||
* if the course defines a repeatable part, the number of laps at least needs to be {@code 1} for the
|
||||
* default implementation, and an {@link IllegalArgumentException} shall be thrown in case a value less
|
||||
* than {@code 1} is used if this template specifies a repeatable part. Note again that the number of
|
||||
* repetitions of the repeatable part is usually one less than the number of laps, therefore this
|
||||
* limitation.
|
||||
*/
|
||||
Iterable<WaypointTemplate> getWaypointTemplates(int numberOfLaps);
|
||||
|
||||
/**
|
||||
* The sequence of waypoint templates that are used to define the course. If this course template defines
|
||||
* a {@link #getRepeatablePart() repeatable part} then this repeatable part refers to this waypoint sequence
|
||||
* by indices. It is therefore essential that the repeatable part then occurs in this waypoint template
|
||||
* sequence at least (and usually exactly) once.
|
||||
*/
|
||||
Iterable<WaypointTemplate> getWaypointTemplates();
|
||||
|
||||
/**
|
||||
* @return the value set contains at least all {@link MarkRole}s reachable through the {@link #getWaypointTemplates()
|
||||
* waypoint template sequence}; the keys for those values are what clients get when calling
|
||||
* {@link #getDefaultMarkTemplateForMarkRole(MarkRole)} for the value. Optionally, additional default role
|
||||
* assignment for "spare" marks, such as the "1" role for alternative windward mark templates may be returned
|
||||
* by this method.
|
||||
*/
|
||||
Map<MarkTemplate, MarkRole> getDefaultMarkRolesForMarkTemplates();
|
||||
|
||||
/**
|
||||
* Short for {@link #getDefaultMarkRolesForMarkTemplates()}.{@link Map#get(Object) get(markTemplate)}
|
||||
*/
|
||||
MarkRole getOptionalAssociatedRole(MarkTemplate markTemplate);
|
||||
|
||||
/**
|
||||
* The key set contains exactly those {@link MarkRole}s one gets when enumerating all roles reachable through the
|
||||
* {@link #getWaypointTemplates()} and their {@link WaypointTemplate#getControlPointTemplate()}'s
|
||||
* {@link ControlPointTemplate#getMarkRoles() mark roles}, which is the same as calling {@link #getMarkRoles()}. The
|
||||
* values tell the {@link MarkTemplate} to use when instantiating a course from this template in place of the key
|
||||
* role. All mark templates from the value set are guaranteed to be in {@link #getMarkTemplates()}, but
|
||||
* {@link #getMarkTemplates()} may contain more elements, e.g., for spare marks.
|
||||
*/
|
||||
Map<MarkRole, MarkTemplate> getDefaultMarkTemplatesForMarkRoles();
|
||||
|
||||
/**
|
||||
* Obtains the {@link MarkTemplate} that, when instantiating a course from this template, shall be used
|
||||
* to create the {@link Mark} that acts in the role identified by {@code markRole}. Short for
|
||||
* {@link #getDefaultMarkTemplatesForMarkRoles()}.{@link Map#get(Object) get(markRole)}. For all {@link MarkRole}
|
||||
* objects from {@link #getMarkRoles()} this has to return a valid non-{@code null} result.
|
||||
*/
|
||||
MarkTemplate getDefaultMarkTemplateForMarkRole(MarkRole markRole);
|
||||
|
||||
MarkRole getMarkRoleByIdIfContainedInCourseTemplate(UUID markRoleId);
|
||||
|
||||
URL getOptionalImageURL();
|
||||
|
||||
Integer getDefaultNumberOfLaps();
|
||||
|
||||
public static TypeRelativeObjectIdentifier getTypeRelativeObjectIdentifier(UUID courseTemplateUUID) {
|
||||
return new TypeRelativeObjectIdentifier(courseTemplateUUID.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
default QualifiedObjectIdentifier getIdentifier() {
|
||||
return getPermissionType().getQualifiedObjectIdentifier(getTypeRelativeObjectIdentifier(getId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
default HasPermissions getPermissionType() {
|
||||
return SecuredDomainType.COURSE_TEMPLATE;
|
||||
}
|
||||
}
|
||||
+116
@@ -0,0 +1,116 @@
|
||||
package com.sap.sailing.domain.coursetemplate;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
import com.sap.sailing.domain.base.CourseBase;
|
||||
import com.sap.sse.common.Util;
|
||||
|
||||
/**
|
||||
* Checks compatibility of a course with a {@link CourseTemplate}. As a generic type, subclasses
|
||||
* can specify what to use as a course, simply by defining how to get to the waypoint sequence of
|
||||
* the course and from the waypoints to the marks and from the marks to the {@link MarkRole}s.
|
||||
* This way, the core method {@link #isCourseInstanceOfCourseTemplate()} becomes re-usable across
|
||||
* types as different as {@link CourseBase} or {@link CourseConfiguration}.
|
||||
*
|
||||
* @param <C>
|
||||
* the type of the actual course to check for compatibility with a {@link CourseTemplate}
|
||||
* @param <M>
|
||||
* type type of the marks used by the course of type {@code C}
|
||||
* @param <W>
|
||||
* type of the waypoints produced by the course of type {@code C}
|
||||
*/
|
||||
public abstract class CourseTemplateCompatibilityChecker<C, M, W> {
|
||||
private final C course;
|
||||
private final CourseTemplate courseTemplate;
|
||||
|
||||
public CourseTemplateCompatibilityChecker(C course, CourseTemplate courseTemplate) {
|
||||
super();
|
||||
assert course != null;
|
||||
assert courseTemplate != null;
|
||||
this.course = course;
|
||||
this.courseTemplate = courseTemplate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a {@code course} and a {@link CourseTemplate} and checks whether the course is a valid "instance" of the
|
||||
* {@link CourseTemplate}. A course is a valid instance of a course template if for all marks in the course (as
|
||||
* defined by the {@link #getMarks(Object)} method) there is a role associated with the mark (as defined by the
|
||||
* {@link #getMarkRole(Object)} method), and that role is consistent with the role at the corresponding place in the
|
||||
* course template. Furthermore, the waypoint sequence of the course (as defined by the
|
||||
* {@link #getWaypoints(Object)} method) needs to conform to the course template's waypoint sequence, modulo the
|
||||
* number of laps. The result is the number of laps if the {@link CourseTemplate} has a
|
||||
* {@link CourseTemplate#getRepeatablePart() repeatable part}, or {@code -1} if the course does not have a
|
||||
* repeatable part, and {@code null} if the course is not a valid instance of the course template.
|
||||
* <p>
|
||||
*
|
||||
* This means in particular that the course can use different marks for the same role in different laps and still
|
||||
* conform to the course template.
|
||||
* <p>
|
||||
*
|
||||
* @return the number of laps if the {@link CourseTemplate} has a {@link CourseTemplate#getRepeatablePart()
|
||||
* repeatable part}, or {@code -1} if the course does not have a repeatable part, and {@code null} if the
|
||||
* course is not a valid instance of the course template
|
||||
*/
|
||||
public Integer isCourseInstanceOfCourseTemplate() {
|
||||
int numberOfLaps = -1;
|
||||
boolean validCourseTemplateUsage = true;
|
||||
final Iterable<WaypointTemplate> effectiveCourseSequence;
|
||||
if (courseTemplate.hasRepeatablePart()) {
|
||||
final RepeatablePart optionalRepeatablePart = courseTemplate.getRepeatablePart();
|
||||
final int numberOfWaypointsInTemplate = Util.size(courseTemplate.getWaypointTemplates());
|
||||
final int numberOfWaypointsInCourse = Util.size(getWaypoints(course));
|
||||
final int lengthOfRepeatablePart = optionalRepeatablePart.getZeroBasedIndexOfRepeatablePartEnd()
|
||||
- optionalRepeatablePart.getZeroBasedIndexOfRepeatablePartStart();
|
||||
final int lengthOfNonRepeatablePart = numberOfWaypointsInTemplate - lengthOfRepeatablePart;
|
||||
final int lengthOfRepetitions = numberOfWaypointsInCourse - lengthOfNonRepeatablePart;
|
||||
if (lengthOfRepetitions % lengthOfRepeatablePart == 0) {
|
||||
numberOfLaps = lengthOfRepetitions / lengthOfRepeatablePart + 1;
|
||||
effectiveCourseSequence = courseTemplate.getWaypointTemplates(numberOfLaps);
|
||||
} else {
|
||||
validCourseTemplateUsage = false;
|
||||
effectiveCourseSequence = courseTemplate.getWaypointTemplates();
|
||||
}
|
||||
} else {
|
||||
effectiveCourseSequence = courseTemplate.getWaypointTemplates();
|
||||
}
|
||||
if (validCourseTemplateUsage) {
|
||||
final Iterator<WaypointTemplate> waypointTemplateIterator = effectiveCourseSequence.iterator();
|
||||
final Iterator<W> waypointIterator = getWaypoints(course).iterator();
|
||||
while (waypointTemplateIterator.hasNext() && validCourseTemplateUsage) {
|
||||
final WaypointTemplate waypointTemplate = waypointTemplateIterator.next();
|
||||
final Iterable<MarkRole> markRolesOfControlPoint = waypointTemplate.getControlPointTemplate().getMarkRoles();
|
||||
final W waypoint = waypointIterator.next();
|
||||
final Iterable<M> marksOfControlPoint = getMarks(waypoint);
|
||||
if (Util.size(markRolesOfControlPoint) != Util.size(marksOfControlPoint)) {
|
||||
validCourseTemplateUsage = false;
|
||||
} else {
|
||||
final Iterator<MarkRole> markRoleIterator = markRolesOfControlPoint.iterator();
|
||||
final Iterator<M> markIterator = marksOfControlPoint.iterator();
|
||||
while (markRoleIterator.hasNext()) {
|
||||
final MarkRole markRoleFromCourseTemplate = markRoleIterator.next();
|
||||
final M markFromRegatta = markIterator.next();
|
||||
final MarkRole roleForMarkOrNull = getMarkRole(markFromRegatta);
|
||||
if (!Util.equalsWithNull(markRoleFromCourseTemplate, roleForMarkOrNull)) {
|
||||
validCourseTemplateUsage = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return validCourseTemplateUsage ? numberOfLaps : null;
|
||||
}
|
||||
|
||||
protected C getCourse() {
|
||||
return course;
|
||||
}
|
||||
|
||||
protected CourseTemplate getCourseTemplate() {
|
||||
return courseTemplate;
|
||||
}
|
||||
|
||||
protected abstract MarkRole getMarkRole(M markFromCourse);
|
||||
|
||||
protected abstract Iterable<M> getMarks(W waypoint);
|
||||
|
||||
protected abstract Iterable<W> getWaypoints(C course);
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
package com.sap.sailing.domain.coursetemplate;
|
||||
|
||||
import com.sap.sailing.domain.common.Position;
|
||||
|
||||
public interface FixedPositioning extends Positioning {
|
||||
Position getFixedPosition();
|
||||
default <T> T accept(PositioningVisitor<T> visitor) {
|
||||
return visitor.visit(this);
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package com.sap.sailing.domain.coursetemplate;
|
||||
|
||||
/**
|
||||
* Describes a configuration with an optional {@link #getOptionalMarkTemplate() mark template} that delivers
|
||||
* explicit {@link CommonMarkPropertiesWithOptionalPositioning}. The object can be marked for
|
||||
* adding to the inventory when applied. The {@link #getFreestyleProperties() freestyle properties}
|
||||
* take precedence.
|
||||
*
|
||||
* @author Axel Uhl (D043530)
|
||||
*
|
||||
*/
|
||||
public interface FreestyleMarkConfiguration<P> extends MarkConfiguration<P> {
|
||||
CommonMarkProperties getFreestyleProperties();
|
||||
|
||||
/**
|
||||
* @return a non-{@code null} object if this mark configuration originated from a {@link MarkProperties} object
|
||||
* where the {@link #getFreestyleProperties() freestyle properties} override one or more of the
|
||||
* {@link MarkProperties}' attributes. If {@link #isStoreToInventory()} is {@code true}, this means
|
||||
* that the {@link MarkProperties} object returned by this method shall be updated accordingly to
|
||||
* reflect the {@link #getFreestyleProperties() freestyle properties} after the update.
|
||||
*/
|
||||
@Override
|
||||
MarkProperties getOptionalMarkProperties();
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
package com.sap.sailing.domain.coursetemplate;
|
||||
|
||||
public interface HasTags {
|
||||
|
||||
public Iterable<String> getTags();
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
package com.sap.sailing.domain.coursetemplate;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import com.sap.sailing.domain.base.Mark;
|
||||
|
||||
/**
|
||||
* Describes how a {@link Mark} in a regatta shall be configured, in terms of its basic attributes as well as regarding
|
||||
* its positioning / tracking / location information.
|
||||
* <p>
|
||||
*
|
||||
* The information may come from one or more sources:
|
||||
* <ul>
|
||||
* <li>A {@link MarkTemplate} can provide {@link CommonMarkProperties}</li>
|
||||
* <li>An existing {@link Mark} from the regatta may be selected; the mark may or may not be used to fill the role
|
||||
* defined by the {@link #getOptionalMarkTemplate() mark template}. Regarding the {@link #getEffectiveProperties()
|
||||
* effective properties}, the {@link Mark}'s properties take precedence over the {@link MarkTemplate}'s properties.</li>
|
||||
* <li>A {@link MarkProperties} object may be selected from the inventory or may have been
|
||||
* {@link #getOptionalMarkProperties() identified} as having been used while creating the {@link #getMark() mark} in the
|
||||
* regatta, to provide optional base configuration ({@link CommonMarkProperties}) as well as tracking/location/device
|
||||
* information. All of those are optional in a {@link MarkProperties} object. Those present will take precedence and
|
||||
* will be merged with the properties obtained from the {@link #getOptionalMarkTemplate() mark template} and the
|
||||
* properties obtained from an existing {@link #getMark() mark}.</li>
|
||||
* </ul>
|
||||
*
|
||||
* The combined set of properties merged according to the rules above can be obtained by calling
|
||||
* {@link #getEffectiveProperties()}.
|
||||
* <p>
|
||||
*
|
||||
* The {@link #isStoreToInventory()} method tells whether, when applying this configuration to create or use a
|
||||
* {@link Mark}, the {@link #getEffectiveProperties() effective mark properties} shall be recorded as a new
|
||||
* {@link MarkProperties} object in the inventory.
|
||||
*
|
||||
* @param <P>
|
||||
* type of the annotation used to convey positioning-related information; typical instantiations would, e.g.,
|
||||
* be with {@link MarkConfigurationRequestAnnotation} and {@link MarkConfigurationResponseAnnotation}.
|
||||
*
|
||||
* @author Axel Uhl (D043530)
|
||||
*
|
||||
*/
|
||||
public interface MarkConfiguration<P> extends ControlPointWithMarkConfiguration<P> {
|
||||
<T> T accept(MarkConfigurationVisitor<T, P> visitor);
|
||||
|
||||
MarkTemplate getOptionalMarkTemplate();
|
||||
|
||||
MarkProperties getOptionalMarkProperties();
|
||||
|
||||
CommonMarkProperties getEffectiveProperties();
|
||||
|
||||
P getAnnotationInfo();
|
||||
|
||||
@Override
|
||||
default Iterable<MarkConfiguration<P>> getMarkConfigurations() {
|
||||
final Iterable<MarkConfiguration<P>> result = (Iterable<MarkConfiguration<P>>) Collections.singleton(this);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
package com.sap.sailing.domain.coursetemplate;
|
||||
|
||||
import com.sap.sse.common.Util;
|
||||
|
||||
/**
|
||||
* Additional data for a {@link MarkConfiguration} when using it in a request, e.g., to create a course. It may provide
|
||||
* {@link #getOptionalPositioning() positioning information} that the receiver of this object in expected to implement.
|
||||
* In contrast, this object does not contain information about the actual positioning situation as the server would see
|
||||
* it.
|
||||
*
|
||||
* @author Axel Uhl (D043530)
|
||||
*
|
||||
*/
|
||||
public interface MarkConfigurationRequestAnnotation {
|
||||
boolean isStoreToInventory();
|
||||
|
||||
Positioning getOptionalPositioning();
|
||||
|
||||
/**
|
||||
* Can be used to provide the name/short name for a {@link MarkRole} to use for this mark when a
|
||||
* {@link CourseTemplate} is created from the {@link CourseConfiguration} containing the {@link MarkConfiguration}
|
||||
* that is annotated with {@code this} object.<p>
|
||||
*
|
||||
* An existing {@link MarkRole} object would be referenced by the {@link CourseConfiguration}'s
|
||||
* {@link CourseConfiguration#getAssociatedRoles()} map and therefore wouldn't need any mark role
|
||||
* creation request as part of this request annotation.
|
||||
*/
|
||||
MarkRoleCreationRequest getOptionalMarkRoleCreationRequest();
|
||||
|
||||
/**
|
||||
* Describes a request to create a {@link MarkRole} in the context of a {@link CourseTemplate}. If several
|
||||
* {@link MarkConfiguration}s request the creation of {@link MarkRole}s using a {@link MarkRoleCreationRequest} then
|
||||
* those requests will be merged based on name and short name equality during {@link CourseTemplate} creation. Both,
|
||||
* name and short name, have to be {@link Util#equalsWithNull(Object, Object) equal} in order to cause a merge.
|
||||
*
|
||||
* @author Axel Uhl (d043530)
|
||||
*
|
||||
*/
|
||||
interface MarkRoleCreationRequest {
|
||||
String getMarkRoleName();
|
||||
String getMarkRoleShortName();
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package com.sap.sailing.domain.coursetemplate;
|
||||
|
||||
import com.sap.sailing.domain.common.DeviceIdentifier;
|
||||
import com.sap.sailing.domain.common.tracking.GPSFix;
|
||||
import com.sap.sse.common.TimeRange;
|
||||
import com.sap.sse.common.Util.Triple;
|
||||
|
||||
/**
|
||||
* Used on a {@link MarkConfiguration} as a response to a client, providing a result-oriented view on a mark
|
||||
* configuration. In particular, positioning data may be contained that informs clients about the current tracking and
|
||||
* positioning state of the mark in question.
|
||||
*
|
||||
* @author Axel Uhl (D043530)
|
||||
*
|
||||
*/
|
||||
public interface MarkConfigurationResponseAnnotation {
|
||||
GPSFix getLastKnownPosition();
|
||||
Iterable<Triple<DeviceIdentifier, TimeRange, GPSFix>> getDeviceMappings();
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
package com.sap.sailing.domain.coursetemplate;
|
||||
|
||||
public interface MarkConfigurationVisitor<T, P> {
|
||||
T visit(FreestyleMarkConfiguration<P> markConfiguration);
|
||||
T visit(MarkPropertiesBasedMarkConfiguration<P> markConfiguration);
|
||||
T visit(MarkTemplateBasedMarkConfiguration<P> markConfiguration);
|
||||
T visit(RegattaMarkConfiguration<P> markConfiguration);
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package com.sap.sailing.domain.coursetemplate;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import com.sap.sailing.domain.base.ControlPointWithTwoMarks;
|
||||
|
||||
/**
|
||||
* A template that can be used to construct a {@link ControlPointWithTwoMarks}.
|
||||
*
|
||||
* @author Axel Uhl (d043530)
|
||||
*
|
||||
* @param <P>
|
||||
* type of the annotation used to convey positioning-related information; typical instantiations would, e.g.,
|
||||
* be with {@link MarkConfigurationRequestAnnotation} and {@link MarkConfigurationResponseAnnotation}.
|
||||
*/
|
||||
public interface MarkPairWithConfiguration<P> extends ControlPointWithMarkConfiguration<P> {
|
||||
MarkConfiguration<P> getLeft();
|
||||
|
||||
MarkConfiguration<P> getRight();
|
||||
|
||||
@Override
|
||||
default Iterable<MarkConfiguration<P>> getMarkConfigurations() {
|
||||
return Arrays.asList(getLeft(), getRight());
|
||||
}
|
||||
}
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
package com.sap.sailing.domain.coursetemplate;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.sap.sailing.domain.base.Mark;
|
||||
import com.sap.sailing.domain.common.MarkType;
|
||||
import com.sap.sailing.domain.common.security.SecuredDomainType;
|
||||
import com.sap.sse.common.Color;
|
||||
import com.sap.sse.common.NamedWithUUID;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.security.shared.HasPermissions;
|
||||
import com.sap.sse.security.shared.QualifiedObjectIdentifier;
|
||||
import com.sap.sse.security.shared.TypeRelativeObjectIdentifier;
|
||||
import com.sap.sse.security.shared.WithQualifiedObjectIdentifier;
|
||||
|
||||
/**
|
||||
* Stores properties that can be applied to a mark in the context of an event or a regatta, including the mark's own
|
||||
* attributes as well as tracking-related information such as a reference to the tracking device used to track the mark,
|
||||
* or a fixed mark position, such as for a land mark, an official lateral or cardinal buoy, a regatta mark in a fixed
|
||||
* position or a lighthouse.
|
||||
* <p>
|
||||
*
|
||||
* Such a properties object can be linked to zero or more {@link MarkTemplate}s. When the user creates a course from a
|
||||
* {@link CourseTemplate}, linked {@link MarkProperties} can be offered to configure the {@link Mark} created from the
|
||||
* {@link CourseTemplate}'s {@link MarkTemplate}s.
|
||||
* <p>
|
||||
*
|
||||
* {@link MarkProperties} objects can be tagged, and the tags may be used to search and filter a library of such
|
||||
* {@link MarkProperties}. The tags can, e.g., express a venue name or a course area name or the name of a set of
|
||||
* tracking devices typically used to track a course. A user may have more than one {@link MarkProperties} object
|
||||
* available that is linked to a particular {@link MarkTemplate}. One of those could be selected depending on
|
||||
* {@link #getPreviousUsage() when it was used last} for the {@link MarkTemplate}, or the user may be presented with the
|
||||
* set of {@link MarkProperties} available and can select, search and filter.<p>
|
||||
*
|
||||
* These properties objects are entities and can be updated. For example, if the device used to track the mark
|
||||
* shall be replaced permanently, also for future uses, the device ID could be updated.
|
||||
*
|
||||
* @author Axel Uhl (d043530)
|
||||
*
|
||||
*/
|
||||
public interface MarkProperties extends CommonMarkProperties, NamedWithUUID, HasTags, WithQualifiedObjectIdentifier {
|
||||
void setColor(Color color);
|
||||
|
||||
void setShape(String shape);
|
||||
|
||||
void setPattern(String pattern);
|
||||
|
||||
void setType(MarkType type);
|
||||
|
||||
void setShortName(String shortName);
|
||||
|
||||
/**
|
||||
* {@code null} or a specification as to how to obtain position data for the mark; could, e.g., be
|
||||
* a fixed position specification or the specification of a tracking device identifier. See the
|
||||
* specializations of {@link Positioning} for an overview.
|
||||
*/
|
||||
Positioning getPositioningInformation();
|
||||
|
||||
/**
|
||||
* Updates this properties object such that the next call to {@link #getPositioningInformation()} returns the
|
||||
* {@code positioningInformation} object provided to this call.
|
||||
*/
|
||||
void setPositioningInformation(Positioning positioningInformation);
|
||||
|
||||
/**
|
||||
* Returns an immutable map that tells all {@link #addMarkTemplateUsage(MarkTemplate, TimePoint) recorded}
|
||||
* usages of this {@link MarkProperties} object for a {@link MarkTemplate}.
|
||||
*/
|
||||
Map<MarkTemplate, TimePoint> getLastUsedMarkTemplate();
|
||||
|
||||
/**
|
||||
* Records a usage of this {@link MarkProperties} object in conjunction with the
|
||||
* {@code markTemplate} provided. If this usage is later than that in
|
||||
* {@link #getLastUsedMarkTemplate()}, it will replace the previously last usage.
|
||||
*/
|
||||
void addMarkTemplateUsage(MarkTemplate markTemplate, TimePoint usageTimePoint);
|
||||
|
||||
/**
|
||||
* Returns an immutable map that tells all {@link #addMarkRoleUsage(MarkRole, TimePoint) recorded}
|
||||
* usages of this {@link MarkProperties} object for a {@link MarkRole}.
|
||||
*/
|
||||
Map<MarkRole, TimePoint> getLastUsedMarkRole();
|
||||
|
||||
/**
|
||||
* Records a usage of this {@link MarkProperties} object in conjunction with the
|
||||
* {@code markRole} provided. If this usage is later than that in
|
||||
* {@link #getLastUsedMarkRole()}, it will replace the previously last usage.
|
||||
*/
|
||||
void addMarkRoleUsage(MarkRole markRole, TimePoint usageTmePoint);
|
||||
|
||||
public static TypeRelativeObjectIdentifier getTypeRelativeObjectIdentifier(UUID markPropertiesUUID) {
|
||||
return new TypeRelativeObjectIdentifier(markPropertiesUUID.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
default QualifiedObjectIdentifier getIdentifier() {
|
||||
return getPermissionType().getQualifiedObjectIdentifier(getTypeRelativeObjectIdentifier(getId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
default HasPermissions getPermissionType() {
|
||||
return SecuredDomainType.MARK_PROPERTIES;
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package com.sap.sailing.domain.coursetemplate;
|
||||
|
||||
/**
|
||||
* Configures a mark based on a {@link MarkProperties} object returned from {@link #getOptionalMarkProperties()}.
|
||||
* Optionally, a non-{@code null} {@link MarkTemplate} may be returned from {@link #getOptionalMarkTemplate()} which
|
||||
* then means that the mark to create and configure assumes the "role" defined by the mark template. The
|
||||
* {@link MarkProperties} attributes, if present, take precedence over their respective {@link MarkTemplate}
|
||||
* counterparts.
|
||||
*
|
||||
* @author Axel Uhl (D043530)
|
||||
*
|
||||
*/
|
||||
public interface MarkPropertiesBasedMarkConfiguration<P> extends MarkConfiguration<P> {
|
||||
/**
|
||||
* The "optional" {@link MarkProperties} for this type of mark configuration are the {@link MarkProperties}
|
||||
* referenced by this object and hence never {@code null}.
|
||||
*/
|
||||
@Override
|
||||
MarkProperties getOptionalMarkProperties();
|
||||
}
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
package com.sap.sailing.domain.coursetemplate;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.sap.sailing.domain.common.DeviceIdentifier;
|
||||
import com.sap.sailing.domain.common.MarkType;
|
||||
import com.sap.sailing.domain.common.Position;
|
||||
import com.sap.sailing.domain.coursetemplate.impl.FixedPositioningImpl;
|
||||
import com.sap.sailing.domain.coursetemplate.impl.MarkPropertiesImpl;
|
||||
import com.sap.sailing.domain.coursetemplate.impl.TrackingDeviceBasedPositioningImpl;
|
||||
import com.sap.sse.common.Color;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
|
||||
public class MarkPropertiesBuilder {
|
||||
private final UUID id;
|
||||
private final String name;
|
||||
private final String shortName;
|
||||
private final Color color;
|
||||
private final String shape;
|
||||
private final String pattern;
|
||||
private final MarkType type;
|
||||
private Iterable<String> tags;
|
||||
private Positioning positioningInformation;
|
||||
private Map<MarkTemplate, TimePoint> lastUsedTemplate;
|
||||
private Map<MarkRole, TimePoint> lastUsedRole;
|
||||
|
||||
public MarkPropertiesBuilder(UUID id, String name, String shortName, Color color, String shape, String pattern,
|
||||
MarkType type) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.shortName = shortName;
|
||||
this.color = color;
|
||||
this.shape = shape;
|
||||
this.pattern = pattern;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public MarkPropertiesBuilder withTags(Iterable<String> tags) {
|
||||
this.tags = tags;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MarkPropertiesBuilder withDeviceId(DeviceIdentifier deviceId) {
|
||||
this.positioningInformation = new TrackingDeviceBasedPositioningImpl(deviceId);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MarkPropertiesBuilder withFixedPosition(Position position) {
|
||||
this.positioningInformation = new FixedPositioningImpl(position);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MarkProperties build() {
|
||||
MarkPropertiesImpl impl = new MarkPropertiesImpl(id, name, shortName, color, shape, pattern, type);
|
||||
if (tags != null) {
|
||||
impl.setTags(tags);
|
||||
}
|
||||
if (positioningInformation != null) {
|
||||
impl.setPositioningInformation(positioningInformation);
|
||||
}
|
||||
if (lastUsedTemplate != null) {
|
||||
impl.setLastUsedMarkTemplate(lastUsedTemplate);
|
||||
}
|
||||
if (lastUsedRole != null) {
|
||||
impl.setLastUsedMarkRole(lastUsedRole);
|
||||
}
|
||||
return impl;
|
||||
}
|
||||
|
||||
public void withLastUsedTemplate(Map<MarkTemplate, TimePoint> lastUsedTemplate) {
|
||||
this.lastUsedTemplate = lastUsedTemplate;
|
||||
}
|
||||
|
||||
public void withLastUsedRole(Map<MarkRole, TimePoint> lastUsedRole) {
|
||||
this.lastUsedRole = lastUsedRole;
|
||||
}
|
||||
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
package com.sap.sailing.domain.coursetemplate;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.sap.sailing.domain.common.security.SecuredDomainType;
|
||||
import com.sap.sse.common.Named;
|
||||
import com.sap.sse.common.NamedWithUUID;
|
||||
import com.sap.sse.security.shared.HasPermissions;
|
||||
import com.sap.sse.security.shared.QualifiedObjectIdentifier;
|
||||
import com.sap.sse.security.shared.TypeRelativeObjectIdentifier;
|
||||
import com.sap.sse.security.shared.WithQualifiedObjectIdentifier;
|
||||
|
||||
/**
|
||||
* A mark role defines the purpose of a mark used in the waypoint sequences of a regatta course or course template and
|
||||
* allows users to swap out marks or mark templates without changing the the effective waypoint sequence. Having this, a
|
||||
* course template and regatta course may define a compatible waypoint sequence while being based on different mark
|
||||
* definitions.<p>
|
||||
*
|
||||
* An example of a role would be "1", representing the windward mark in a course sequence diagram.
|
||||
*
|
||||
* TODO after removing IsMarkRole, all needs to revert to MarkRole. The original idea
|
||||
* was to represent mark role "proxies" by objects of a type compatible to an
|
||||
* interface that MarkRole and the proxy type have in common, leading to
|
||||
* MarkRoleName[Impl]. However, I think it would be better to represent the
|
||||
* request for the creation of a role with a specific name during the creation
|
||||
* of a course template from a CourseConfiguration by annotating the MarkConfiguration
|
||||
* not only with storeToInventory and an optional Positioning object but also
|
||||
* by an optional mark role creation specification. This can be part of the
|
||||
* annotation type used for MarkConfiguration objects during the "request phase"
|
||||
* as implemented on branch bug5168 and already merged to bug5085. The merge
|
||||
* of those changes into branch bug5165 currently is causing conflicts that should
|
||||
* be solved.
|
||||
|
||||
*/
|
||||
public interface MarkRole extends Named, NamedWithUUID, WithQualifiedObjectIdentifier, ControlPointTemplate {
|
||||
public static TypeRelativeObjectIdentifier getTypeRelativeObjectIdentifier(UUID markPropertiesUUID) {
|
||||
return new TypeRelativeObjectIdentifier(markPropertiesUUID.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
default Iterable<MarkRole> getMarkRoles() {
|
||||
return Collections.singleton(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
default QualifiedObjectIdentifier getIdentifier() {
|
||||
return getPermissionType().getQualifiedObjectIdentifier(getTypeRelativeObjectIdentifier(getId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
default HasPermissions getPermissionType() {
|
||||
return SecuredDomainType.MARK_ROLE;
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
package com.sap.sailing.domain.coursetemplate;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.sap.sailing.domain.base.ControlPointWithTwoMarks;
|
||||
import com.sap.sailing.domain.coursetemplate.impl.MarkRolePairImpl;
|
||||
|
||||
/**
|
||||
* A template that can be used to construct a {@link ControlPointWithTwoMarks}.
|
||||
*
|
||||
* @author Axel Uhl (d043530)
|
||||
*
|
||||
*/
|
||||
public interface MarkRolePair extends ControlPointTemplate {
|
||||
MarkRole getLeft();
|
||||
|
||||
MarkRole getRight();
|
||||
|
||||
/**
|
||||
* In the context of one {@link CourseTemplate}, this helps to reuse {@link MarkRolePair} instances that are equal
|
||||
* to each other. The factory maintains a cache of {@link MarkRolePair} objects, and a lookup is performed if a pair
|
||||
* is {@link #create(String, String, MarkRole, MarkRole) requested}; if an equal mark role pair already exists in
|
||||
* the cache, it is returned. Otherwise, a new pair is created, entered into the cache and then returned.
|
||||
*/
|
||||
public class MarkRolePairFactory {
|
||||
private final Map<MarkRolePair, MarkRolePair> markPairs = new HashMap<>();
|
||||
|
||||
public MarkRolePair create(String name, String shortName, MarkRole left, MarkRole right) {
|
||||
// use name as short name if none is provided
|
||||
final MarkRolePair markRolePair = new MarkRolePairImpl(name,
|
||||
shortName != null && !shortName.isEmpty() ? shortName : name, left, right);
|
||||
// usage of computeIfAbsent ensures recycling of identical MarkPairTemplate instances in the CourseTemplate
|
||||
return markPairs.computeIfAbsent(markRolePair, mrp -> mrp);
|
||||
}
|
||||
}
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
package com.sap.sailing.domain.coursetemplate;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import com.sap.sailing.domain.base.Mark;
|
||||
import com.sap.sailing.domain.common.security.SecuredDomainType;
|
||||
import com.sap.sailing.domain.coursetemplate.MarkTemplate.MarkTemplateResolver;
|
||||
import com.sap.sse.common.IsManagedByCache;
|
||||
import com.sap.sse.common.NamedWithUUID;
|
||||
import com.sap.sse.security.shared.HasPermissions;
|
||||
import com.sap.sse.security.shared.QualifiedObjectIdentifier;
|
||||
import com.sap.sse.security.shared.TypeRelativeObjectIdentifier;
|
||||
import com.sap.sse.security.shared.WithQualifiedObjectIdentifier;
|
||||
|
||||
/**
|
||||
* A template for creating a {@link Mark}. It has a globally unique ID and can be used in zero or more
|
||||
* {@link CourseTemplate}s. It is immutable and contains all {@link CommonMarkProperties common properties for a mark}.
|
||||
* In the context of a {@link CourseTemplate} it can be {@link CourseTemplate#getDefaultMarkRolesForMarkTemplates() linked} to a {@link MarkRole}
|
||||
* that it wants to assume by default.
|
||||
*
|
||||
* @author Axel Uhl (d043530)
|
||||
*
|
||||
*/
|
||||
public interface MarkTemplate extends NamedWithUUID, CommonMarkProperties,
|
||||
WithQualifiedObjectIdentifier, IsManagedByCache<MarkTemplateResolver> {
|
||||
public interface MarkTemplateResolver {
|
||||
MarkTemplate getOrCreate(MarkTemplate markTemplate);
|
||||
}
|
||||
|
||||
@Override
|
||||
default IsManagedByCache<MarkTemplateResolver> resolve(MarkTemplateResolver resolver) {
|
||||
return resolver.getOrCreate(this);
|
||||
}
|
||||
|
||||
public static TypeRelativeObjectIdentifier getTypeRelativeObjectIdentifier(UUID markTemplateUUID) {
|
||||
return new TypeRelativeObjectIdentifier(markTemplateUUID.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
default QualifiedObjectIdentifier getIdentifier() {
|
||||
return getPermissionType().getQualifiedObjectIdentifier(getTypeRelativeObjectIdentifier(getId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
default HasPermissions getPermissionType() {
|
||||
return SecuredDomainType.MARK_TEMPLATE;
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
package com.sap.sailing.domain.coursetemplate;
|
||||
|
||||
/**
|
||||
* The {@link #getOptionalMarkTemplate()} method will always return a non-{@code null} {@link MarkTemplate}
|
||||
* which is the only source of the properties returned by {@link #getEffectiveProperties()}.
|
||||
*
|
||||
* @author Axel Uhl (D043530)
|
||||
*
|
||||
*/
|
||||
public interface MarkTemplateBasedMarkConfiguration<P> extends MarkConfiguration<P> {
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package com.sap.sailing.domain.coursetemplate;
|
||||
|
||||
import com.sap.sailing.domain.common.DeviceIdentifier;
|
||||
|
||||
/**
|
||||
* A specification how a mark's position shall be obtained. Typical cases can be a fixed position,
|
||||
* or a {@link DeviceIdentifier} telling which device to use to track the object.
|
||||
*
|
||||
* @author Axel Uhl (D043530)
|
||||
*
|
||||
*/
|
||||
public interface Positioning {
|
||||
<T> T accept(PositioningVisitor<T> visitor);
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package com.sap.sailing.domain.coursetemplate;
|
||||
|
||||
public interface PositioningVisitor<T> {
|
||||
T visit(FixedPositioning fixedPositioning);
|
||||
|
||||
T visit(TrackingDeviceBasedPositioning trackingDeviceBasedPositioning);
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package com.sap.sailing.domain.coursetemplate;
|
||||
|
||||
import com.sap.sailing.domain.base.Mark;
|
||||
|
||||
/**
|
||||
* Configures a mark solely by referencing an already existing mark. No additional configuration beyond what already
|
||||
* exists in the regatta can be specified. The {@link #getEffectiveProperties() effective properties} are simply all
|
||||
* aspects of the {@link #getMark() mark} in its {@link CommonMarkProperties} role, without any additional
|
||||
* location/position/tracking data.
|
||||
* <p>
|
||||
*
|
||||
* An optional non-{@code null} {@link MarkTemplate} may be returned from {@link #getOptionalMarkTemplate()} which then
|
||||
* leads to recording a usage relation between the regatta {@link Mark} and said {@link MarkTemplate}. This relation can be
|
||||
* requested using {@link Mark#getOriginatingMarkTemplateIdOrNull()}.
|
||||
* {@link #getOptionalMarkProperties()} can reference a {@link MarkProperties} object if the {@link #getMark mark} was
|
||||
* originally created from that {@link MarkProperties} object. Again, the resulting usage relation can be requested using
|
||||
* {@link Mark#getOriginatingMarkPropertiesIdOrNull()}.
|
||||
* <p>
|
||||
*
|
||||
* The {@link #getEffectiveProperties() effective properties} will contain
|
||||
* {@link CommonMarkPropertiesWithOptionalPositioning#getFixedPosition() fixed position data} or a
|
||||
* {@link CommonMarkPropertiesWithOptionalPositioning#getTrackingDeviceIdentifier() tracking device ID} in case the mark
|
||||
* has such information on the server side.
|
||||
*
|
||||
* @author Axel Uhl (D043530)
|
||||
*
|
||||
*/
|
||||
public interface RegattaMarkConfiguration<P> extends MarkConfiguration<P> {
|
||||
Mark getMark();
|
||||
}
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
package com.sap.sailing.domain.coursetemplate;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.sap.sse.common.Util;
|
||||
|
||||
/**
|
||||
* Reusable implementation of the semantics for the repeatable part of a course sequence. The sequence is defined by the
|
||||
* start and end of the zero-based repeatable part of the complete sequence. The following requirements need to be
|
||||
* fulfilled:
|
||||
* <ul>
|
||||
* <li>The {@link #getZeroBasedIndexOfRepeatablePartStart()} and {@link #getZeroBasedIndexOfRepeatablePartEnd()} are
|
||||
* required to be >=0</li>
|
||||
* <li>{@link #getZeroBasedIndexOfRepeatablePartStart()} is truly less than {@link #getZeroBasedIndexOfRepeatablePartEnd()}</li>
|
||||
* </ul>
|
||||
*/
|
||||
public interface RepeatablePart extends Serializable {
|
||||
/**
|
||||
* The index into {@link #waypoints} of the first waypoint that is to be cloned for repetitive laps.
|
||||
*/
|
||||
int getZeroBasedIndexOfRepeatablePartStart();
|
||||
|
||||
/**
|
||||
* The index into {@link #waypoints} of the first waypoint that comes after the sub-sequence to be cloned for
|
||||
* repetitive laps.
|
||||
*/
|
||||
int getZeroBasedIndexOfRepeatablePartEnd();
|
||||
|
||||
default int length() {
|
||||
return getZeroBasedIndexOfRepeatablePartEnd()-getZeroBasedIndexOfRepeatablePartStart();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a sequence of waypoints (T) that can be use to construct a course. If this course template
|
||||
* defines a repeatable waypoint sub-sequence, the {@code numberOfLaps} parameter is used to decide how many times
|
||||
* to repeat this sub-sequence. Typically, the repeatable sub-sequence will be repeated one times fewer than the
|
||||
* {@code numberOfLaps}. For example, in a typical windward-leeward "L" course we would have
|
||||
* {@code Start/Finish, [1, 4p/4s], 1, Start/Finish}. For an "L1" course with only one lap, we'd like to have
|
||||
* {@code Start/Finish, 1, Start/Finish}, so the repeatable sub-sequence, enclosed by the brackets in the example
|
||||
* above, will occur zero times. For an "L2" the repeatable sub-sequence will occur once, and so on.
|
||||
*
|
||||
* @throws IllegalArgumentException
|
||||
* in case the given {@code sequenceWithRepeatablePart} contains less or equal elements to
|
||||
* {@link #getZeroBasedIndexOfRepeatablePartEnd()}.
|
||||
*/
|
||||
default <T> Iterable<T> createSequence(int laps, Iterable<T> sequenceWithRepeatablePart) {
|
||||
validateRepeatablePartForSequence(sequenceWithRepeatablePart);
|
||||
final int zeroBasedIndexOfRepeatablePartStart = getZeroBasedIndexOfRepeatablePartStart();
|
||||
final int zeroBasedIndexOfRepeatablePartEnd = getZeroBasedIndexOfRepeatablePartEnd();
|
||||
final List<T> sequenceWithRepeatablePartAsList = new ArrayList<>();
|
||||
Util.addAll(sequenceWithRepeatablePart, sequenceWithRepeatablePartAsList);
|
||||
final int sequenceLength = sequenceWithRepeatablePartAsList.size();
|
||||
final List<T> result = new ArrayList<>();
|
||||
// Non-repeatable start of the sequence
|
||||
for (int i = 0; i < zeroBasedIndexOfRepeatablePartStart; i++) {
|
||||
result.add(sequenceWithRepeatablePartAsList.get(i));
|
||||
}
|
||||
// Repeatable part of the sequence
|
||||
for (int lap = 1; lap < laps; lap++) {
|
||||
for (int i = zeroBasedIndexOfRepeatablePartStart; i < zeroBasedIndexOfRepeatablePartEnd; i++) {
|
||||
result.add(sequenceWithRepeatablePartAsList.get(i));
|
||||
}
|
||||
}
|
||||
// Non-repeatable end of the sequence
|
||||
for (int i = zeroBasedIndexOfRepeatablePartEnd; i < sequenceLength; i++) {
|
||||
result.add(sequenceWithRepeatablePartAsList.get(i));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that this repeatable part is compatible with the given {@code sequenceWithRepeatablePart} and causes an
|
||||
* exception in case it isn't.
|
||||
*
|
||||
* @throws IllegalArgumentException
|
||||
* in case the given {@code sequenceWithRepeatablePart} contains less or equal elements to
|
||||
* {@link #getZeroBasedIndexOfRepeatablePartEnd()}.
|
||||
*/
|
||||
default <T> void validateRepeatablePartForSequence(Iterable<T> sequenceWithRepeatablePart) {
|
||||
final int zeroBasedIndexOfRepeatablePartEnd = getZeroBasedIndexOfRepeatablePartEnd();
|
||||
final int sequenceLength = Util.size(sequenceWithRepeatablePart);
|
||||
if (sequenceLength < zeroBasedIndexOfRepeatablePartEnd) {
|
||||
throw new IllegalArgumentException("Repeatable part (" + getZeroBasedIndexOfRepeatablePartStart() + ", "
|
||||
+ zeroBasedIndexOfRepeatablePartEnd + ") is out of range for sequence of length " + sequenceLength);
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
package com.sap.sailing.domain.coursetemplate;
|
||||
|
||||
import com.sap.sailing.domain.common.DeviceIdentifier;
|
||||
|
||||
public interface TrackingDeviceBasedPositioning extends Positioning {
|
||||
DeviceIdentifier getDeviceIdentifier();
|
||||
default <T> T accept(PositioningVisitor<T> visitor) {
|
||||
return visitor.visit(this);
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
package com.sap.sailing.domain.coursetemplate;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import com.sap.sailing.domain.common.PassingInstruction;
|
||||
|
||||
public interface WaypointTemplate extends Serializable {
|
||||
ControlPointTemplate getControlPointTemplate();
|
||||
PassingInstruction getPassingInstruction();
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
package com.sap.sailing.domain.coursetemplate;
|
||||
|
||||
import com.sap.sailing.domain.common.PassingInstruction;
|
||||
|
||||
public interface WaypointWithMarkConfiguration<P> {
|
||||
ControlPointWithMarkConfiguration<P> getControlPoint();
|
||||
PassingInstruction getPassingInstruction();
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
package com.sap.sailing.domain.coursetemplate;
|
||||
|
||||
public interface WithOptionalRepeatablePart {
|
||||
default boolean hasRepeatablePart() {
|
||||
return getRepeatablePart() != null;
|
||||
}
|
||||
|
||||
RepeatablePart getRepeatablePart();
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
package com.sap.sailing.domain.coursetemplate.impl;
|
||||
|
||||
import com.sap.sailing.domain.common.MarkType;
|
||||
import com.sap.sailing.domain.coursetemplate.CommonMarkProperties;
|
||||
import com.sap.sse.common.Color;
|
||||
import com.sap.sse.common.impl.RenamableImpl;
|
||||
|
||||
public class CommonMarkPropertiesImpl extends RenamableImpl implements CommonMarkProperties {
|
||||
private static final long serialVersionUID = 5599464471737104833L;
|
||||
protected String shortName;
|
||||
protected Color color;
|
||||
protected String shape;
|
||||
protected String pattern;
|
||||
protected MarkType type;
|
||||
|
||||
public CommonMarkPropertiesImpl(String name, String shortName, Color color, String shape, String pattern, MarkType type) {
|
||||
super(name);
|
||||
this.shortName = shortName;
|
||||
this.color = color;
|
||||
this.shape = shape;
|
||||
this.pattern = pattern;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public CommonMarkPropertiesImpl(final CommonMarkProperties commonMarkProperties) {
|
||||
this(commonMarkProperties.getName(), commonMarkProperties.getShortName(), commonMarkProperties.getColor(),
|
||||
commonMarkProperties.getShape(), commonMarkProperties.getPattern(), commonMarkProperties.getType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Color getColor() {
|
||||
return color;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getShape() {
|
||||
return shape;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPattern() {
|
||||
return pattern;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MarkType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getShortName() {
|
||||
return shortName;
|
||||
}
|
||||
}
|
||||
+212
@@ -0,0 +1,212 @@
|
||||
package com.sap.sailing.domain.coursetemplate.impl;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.sap.sailing.domain.coursetemplate.CourseConfiguration;
|
||||
import com.sap.sailing.domain.coursetemplate.CourseTemplate;
|
||||
import com.sap.sailing.domain.coursetemplate.CourseTemplateCompatibilityChecker;
|
||||
import com.sap.sailing.domain.coursetemplate.MarkConfiguration;
|
||||
import com.sap.sailing.domain.coursetemplate.MarkRole;
|
||||
import com.sap.sailing.domain.coursetemplate.RepeatablePart;
|
||||
import com.sap.sailing.domain.coursetemplate.WaypointWithMarkConfiguration;
|
||||
|
||||
public class CourseConfigurationImpl<P> implements CourseConfiguration<P> {
|
||||
private static final long serialVersionUID = -9189989170055144298L;
|
||||
|
||||
private final String shortName;
|
||||
private final CourseTemplate optionalCourseTemplate;
|
||||
// TODO decide if we should combine markConfigurations and roleMapping to one field
|
||||
private final Iterable<MarkConfiguration<P>> markConfigurations;
|
||||
private final Map<MarkConfiguration<P>, MarkRole> associatedRoles;
|
||||
|
||||
/**
|
||||
* The waypoints sequence, with at least a single occurrence of any {@link #getRepeatablePart() repeatable part},
|
||||
* in order to obtain a sequence for more than only one lap (meaning zero occurrences of the repeatable part).
|
||||
* There may be more occurrences of the repeatable part than only one, in particular to allow subsequent repetitions
|
||||
* to use different mark configurations in their lap. See also {@link #getWaypoints()} and
|
||||
* {@link #getWaypoints(int)}. So, if {@link #numberOfLaps} is less than or equal to {@code 1}, an occurrence
|
||||
* of the repeatable part will be in this sequence although it isn't used in the current configuration.
|
||||
* This is, however, required in order to interpret the index-based repeatable part in case one or more
|
||||
* occurrences of the repeatable part will be requested.<p>
|
||||
*
|
||||
* This sequence may contain more occurrences of the repeatable part than requested through {@link #numberOfLaps}
|
||||
* even if one or more occurrences of the repeatable part are requested this way. These extra occurrences
|
||||
* will then be used if {@link #getWaypoints(int)} is called with more laps than {@link #numberOfLaps}. This way,
|
||||
* a course configuration can prepare for more laps being sailed and prepare a configuration for extra laps
|
||||
* that differ from the configurations used for the laps currently selected.
|
||||
*/
|
||||
private final List<WaypointWithMarkConfiguration<P>> waypoints;
|
||||
|
||||
/**
|
||||
* Refers to a repeatable part in {@link #waypoints}. Other than in a {@link CourseTemplate} that always
|
||||
* has exactly one occurrence of a repeatable part in its waypoint template sequence, here the {@link #waypoints}
|
||||
* sequence must have at least one but may have multiple occurrences of the repeatable part. The special handling
|
||||
* that happens in this class is that when more than one occurrence already exists in {@link #waypoints}, upon
|
||||
* adding more repetitions the <em>last</em> occurrence will be repeated. See also {@link #getWaypoints(int)}.
|
||||
*/
|
||||
private final RepeatablePart optionalRepeatablePart;
|
||||
|
||||
/**
|
||||
* The number of laps this course configuration has, in case there is a {@link #getRepeatablePart() repeatable part}
|
||||
* defined for a valid {@link #optionalCourseTemplate}. In this case, {@link #getWaypoints()} will consider this
|
||||
* number of laps and will return
|
||||
*/
|
||||
private final Integer numberOfLaps;
|
||||
|
||||
private final String name;
|
||||
private final URL optionalImageURL;
|
||||
|
||||
/**
|
||||
* @param numberOfLaps
|
||||
* {@code null} if a non-{@code null} {@code optionalCourseTemplate} is provided but the course is not
|
||||
* compatible with that template; {@code -1} if there is no course template specified, or the course
|
||||
* template has a {@code null} {@link CourseTemplate#getRepeatablePart() repeatable part}; otherwise the
|
||||
* number of laps (number of occurrences of the repeatable part plus one).
|
||||
*/
|
||||
public CourseConfigurationImpl(CourseTemplate optionalCourseTemplate,
|
||||
Iterable<MarkConfiguration<P>> markConfigurations, Map<MarkConfiguration<P>, MarkRole> associatedRoles,
|
||||
List<WaypointWithMarkConfiguration<P>> waypoints,
|
||||
RepeatablePart optionalRepeatablePart, Integer numberOfLaps, String name, String shortName, URL optionalImageURL) {
|
||||
super();
|
||||
assert !associatedRoles.values().contains(null);
|
||||
this.optionalCourseTemplate = optionalCourseTemplate;
|
||||
this.markConfigurations = markConfigurations;
|
||||
this.associatedRoles = associatedRoles;
|
||||
this.waypoints = waypoints;
|
||||
if (optionalRepeatablePart != null && optionalCourseTemplate == null) {
|
||||
throw new IllegalArgumentException("Inconsistent course configuration "+name+": When a repeatable part is defined, a course template must be provided");
|
||||
}
|
||||
this.optionalRepeatablePart = optionalRepeatablePart;
|
||||
this.numberOfLaps = numberOfLaps;
|
||||
this.name = name;
|
||||
this.shortName = shortName;
|
||||
this.optionalImageURL = optionalImageURL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getShortName() {
|
||||
return shortName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RepeatablePart getRepeatablePart() {
|
||||
return optionalRepeatablePart;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CourseTemplate getOptionalCourseTemplate() {
|
||||
return optionalCourseTemplate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<MarkConfiguration<P>> getAllMarks() {
|
||||
return markConfigurations;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<WaypointWithMarkConfiguration<P>> getWaypoints() {
|
||||
return waypoints;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<MarkConfiguration<P>, MarkRole> getAssociatedRoles() {
|
||||
return associatedRoles;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<MarkConfiguration<P>, MarkRole> getAllMarksWithOptionalRoles() {
|
||||
final Map<MarkConfiguration<P>, MarkRole> result = new HashMap<>();
|
||||
for (MarkConfiguration<P> mc : markConfigurations) {
|
||||
result.put(mc, associatedRoles.get(mc));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getNumberOfLaps() {
|
||||
return numberOfLaps;
|
||||
}
|
||||
|
||||
private class CourseTemplateCompatibilityCheckerForCourseConfiguration extends
|
||||
CourseTemplateCompatibilityChecker<CourseConfiguration<P>, MarkConfiguration<P>, WaypointWithMarkConfiguration<P>> {
|
||||
public CourseTemplateCompatibilityCheckerForCourseConfiguration() {
|
||||
super(CourseConfigurationImpl.this, optionalCourseTemplate);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MarkRole getMarkRole(MarkConfiguration<P> markFromCourse) {
|
||||
return getAssociatedRoles().get(markFromCourse);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Iterable<MarkConfiguration<P>> getMarks(WaypointWithMarkConfiguration<P> waypoint) {
|
||||
return waypoint.getControlPoint().getMarkConfigurations();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Iterable<WaypointWithMarkConfiguration<P>> getWaypoints(CourseConfiguration<P> course) {
|
||||
return waypoints;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the number of repeatable part occurrences if the {@link CourseTemplate} has a {@link CourseTemplate#getRepeatablePart()
|
||||
* repeatable part}, or {@code -1} if the course does not have a repeatable part, and {@code null} if the
|
||||
* course is not a valid instance of the course template
|
||||
*/
|
||||
private Integer getNumberOfRepeatablePartOccurrencesInWaypoints() {
|
||||
final Integer numberOfLaps = new CourseTemplateCompatibilityCheckerForCourseConfiguration().isCourseInstanceOfCourseTemplate();
|
||||
return numberOfLaps == null ? null : numberOfLaps == -1 ? -1 : numberOfLaps-1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<WaypointWithMarkConfiguration<P>> getWaypoints(int numberOfLaps) {
|
||||
final Iterable<WaypointWithMarkConfiguration<P>> result;
|
||||
if (hasRepeatablePart()) {
|
||||
if (numberOfLaps < 1) {
|
||||
throw new IllegalArgumentException("The course template "+this+" has a repeatable part, hence the number of laps needs to be at least 1.");
|
||||
}
|
||||
final Integer numberOfRepeatablePartOccurrences = getNumberOfRepeatablePartOccurrencesInWaypoints();
|
||||
assert numberOfRepeatablePartOccurrences != null; // expected to be enforced by a check in the constructor
|
||||
final List<WaypointWithMarkConfiguration<P>> waypointsWithCorrectNumberOfLaps = new LinkedList<>();
|
||||
// Copy the prefix of the repeatable part without modifications:
|
||||
for (int i=0; i<getRepeatablePart().getZeroBasedIndexOfRepeatablePartStart(); i++) {
|
||||
waypointsWithCorrectNumberOfLaps.add(waypoints.get(i));
|
||||
}
|
||||
// Copy as many occurrences of repeatable parts as required (numberbOfLaps-1) and keep repeating
|
||||
// the last occurrence from waypoints if more are requested than are present there:
|
||||
for (int repetitions=0; repetitions<numberOfLaps-1; repetitions++) {
|
||||
final int occurrenceInWaypointsToUse = Math.min(repetitions, numberOfRepeatablePartOccurrences-1);
|
||||
for (int i=getRepeatablePart().getZeroBasedIndexOfRepeatablePartStart(); i<getRepeatablePart().getZeroBasedIndexOfRepeatablePartEnd(); i++) {
|
||||
waypointsWithCorrectNumberOfLaps.add(waypoints.get(
|
||||
getRepeatablePart().length()*occurrenceInWaypointsToUse + i));
|
||||
}
|
||||
}
|
||||
// Add the postfix from after the last repeatable part occurrence in waypoints:
|
||||
for (int i=getRepeatablePart().getZeroBasedIndexOfRepeatablePartStart()+
|
||||
numberOfRepeatablePartOccurrences*getRepeatablePart().length(); i<waypoints.size(); i++) {
|
||||
waypointsWithCorrectNumberOfLaps.add(waypoints.get(i));
|
||||
}
|
||||
assert waypointsWithCorrectNumberOfLaps.size() == waypoints.size() +
|
||||
(numberOfLaps-1-numberOfRepeatablePartOccurrences)*getRepeatablePart().length();
|
||||
result = waypointsWithCorrectNumberOfLaps;
|
||||
} else {
|
||||
result = waypoints;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL getOptionalImageURL() {
|
||||
return optionalImageURL;
|
||||
}
|
||||
}
|
||||
+242
@@ -0,0 +1,242 @@
|
||||
package com.sap.sailing.domain.coursetemplate.impl;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.sap.sailing.domain.coursetemplate.CourseTemplate;
|
||||
import com.sap.sailing.domain.coursetemplate.MarkRole;
|
||||
import com.sap.sailing.domain.coursetemplate.MarkTemplate;
|
||||
import com.sap.sailing.domain.coursetemplate.RepeatablePart;
|
||||
import com.sap.sailing.domain.coursetemplate.WaypointTemplate;
|
||||
import com.sap.sse.common.Util;
|
||||
import com.sap.sse.common.impl.NamedWithUUIDImpl;
|
||||
|
||||
public class CourseTemplateImpl extends NamedWithUUIDImpl implements CourseTemplate {
|
||||
private static final long serialVersionUID = -183875832585632806L;
|
||||
|
||||
private final String shortName;
|
||||
|
||||
private final Set<MarkTemplate> marks;
|
||||
|
||||
private final ArrayList<WaypointTemplate> waypoints;
|
||||
|
||||
private final Map<MarkTemplate, MarkRole> defaultMarkRolesForMarkTemplates;
|
||||
|
||||
private final Map<MarkRole, MarkTemplate> defaultMarkTemplatesForMarkRoles;
|
||||
|
||||
private Iterable<String> tags = new ArrayList<>();
|
||||
|
||||
private final URL optionalImageURL;
|
||||
|
||||
private final RepeatablePart optionalRepeatablePart;
|
||||
|
||||
private final Integer defaultNumberOfLaps;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a course template with a random UUID and no repeatable part.
|
||||
* @param marks
|
||||
* all mark templates made available in this course template
|
||||
* @param waypoints
|
||||
* may refer only to {@link MarkTemplate}s provided in the {@code marks} parameter
|
||||
* @param defaultMarkTemplatesForMarkRoles
|
||||
* all {@link MarkRole}s reachable through {@code waypoints} must appear as keys in this map's key set;
|
||||
* the values represent the {@link MarkTemplate} to use when instantiating this course template for the
|
||||
* key role
|
||||
* @param defaultMarkRolesForMarkTemplates
|
||||
* for those mark templates referenced as values in {@code defaultMarkTemplatesForRoles}, the value in
|
||||
* this map has to be the key in {@code defaultMarkTemplatesForRoles}; for all other
|
||||
* {@link MarkTemplates}s from {@code marks} it is optional whether a mapping to a default
|
||||
* {@link MarkRole} is provided in this map
|
||||
*/
|
||||
public CourseTemplateImpl(String name, String shortName, Iterable<MarkTemplate> marks,
|
||||
Iterable<WaypointTemplate> waypoints, Map<MarkRole, MarkTemplate> defaultMarkTemplatesForMarkRoles,
|
||||
Map<MarkTemplate, MarkRole> defaultMarkRolesForMarkTemplates, URL optionalImageURL) {
|
||||
this(UUID.randomUUID(), name, shortName, marks, waypoints, defaultMarkTemplatesForMarkRoles,
|
||||
defaultMarkRolesForMarkTemplates, optionalImageURL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a course with the given UUID and no repeatable part.
|
||||
*/
|
||||
public CourseTemplateImpl(UUID id, String name, String shortName, Iterable<MarkTemplate> marks,
|
||||
Iterable<WaypointTemplate> waypoints, Map<MarkRole, MarkTemplate> defaultMarkTemplatesForMarkRoles,
|
||||
Map<MarkTemplate, MarkRole> defaultMarkRolesForMarkTemplates, URL optionalImageURL) {
|
||||
this(id, name, shortName, marks, waypoints, defaultMarkTemplatesForMarkRoles, defaultMarkRolesForMarkTemplates,
|
||||
optionalImageURL, /* optionalRepeatablePart */ null, /* default number of laps */ null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param defaultMarkRolesForMarkTemplates
|
||||
* will be extended according to and checked for consistency against {@code defaultMarkTemplatesForMarkRoles}
|
||||
*/
|
||||
public CourseTemplateImpl(UUID id, String name, String shortName, Iterable<MarkTemplate> marks,
|
||||
Iterable<WaypointTemplate> waypoints, Map<MarkRole, MarkTemplate> defaultMarkTemplatesForMarkRoles,
|
||||
Map<MarkTemplate, MarkRole> defaultMarkRolesForMarkTemplates, URL optionalImageURL,
|
||||
RepeatablePart optionalRepeatablePart, Integer defaultNumberOfLaps) {
|
||||
super(name, id);
|
||||
this.shortName = shortName;
|
||||
this.defaultNumberOfLaps = defaultNumberOfLaps;
|
||||
if (optionalRepeatablePart != null) {
|
||||
optionalRepeatablePart.validateRepeatablePartForSequence(waypoints);
|
||||
}
|
||||
this.waypoints = new ArrayList<>();
|
||||
Util.addAll(waypoints, this.waypoints);
|
||||
this.marks = new HashSet<>();
|
||||
Util.addAll(marks, this.marks);
|
||||
this.optionalImageURL = optionalImageURL;
|
||||
this.optionalRepeatablePart = optionalRepeatablePart;
|
||||
this.defaultMarkRolesForMarkTemplates = new HashMap<>(defaultMarkRolesForMarkTemplates);
|
||||
this.defaultMarkTemplatesForMarkRoles = new HashMap<>(defaultMarkTemplatesForMarkRoles);
|
||||
for (final Entry<MarkRole, MarkTemplate> e : defaultMarkTemplatesForMarkRoles.entrySet()) {
|
||||
final MarkRole previousDefaultMarkRole = this.defaultMarkRolesForMarkTemplates.put(e.getValue(), e.getKey());
|
||||
if (previousDefaultMarkRole != null && previousDefaultMarkRole != e.getKey()) {
|
||||
throw new IllegalArgumentException("Inconsistent default mark role assignment: mark template "+e.getValue()+
|
||||
" was assigned to mark role "+previousDefaultMarkRole+" but now shall default to mark role "+e.getKey());
|
||||
}
|
||||
}
|
||||
validateWaypointsAgainstRolesAndMappingSymmetry();
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws an {@link IllegalArgumentException} in case a waypoint from {@link #waypoints} uses a mark role that uses
|
||||
* a default mark template that is not in {@link #marks}; or if a mark role used in the waypoint sequence does not
|
||||
* define a default mark template; or if the mark template used as the default for a mark role does not refer back
|
||||
* to that same mark role as its default.
|
||||
*/
|
||||
private void validateWaypointsAgainstRolesAndMappingSymmetry() {
|
||||
for (final WaypointTemplate waypoint : waypoints) {
|
||||
for (final MarkRole markRole : waypoint.getControlPointTemplate().getMarkRoles()) {
|
||||
if (!defaultMarkTemplatesForMarkRoles.containsKey(markRole)) {
|
||||
throw new IllegalArgumentException("Mark role "+markRole+" used by waypoint template "+
|
||||
waypoint+" in course template "+this+" is not providing a default mark template");
|
||||
}
|
||||
if (!Util.contains(marks, defaultMarkTemplatesForMarkRoles.get(markRole))) {
|
||||
throw new IllegalArgumentException("Mark template " + defaultMarkTemplatesForMarkRoles.get(markRole)
|
||||
+ " used by role " + markRole + " used by waypoint template " + waypoint
|
||||
+ " in course template " + this + " is not provided in the collection of marks");
|
||||
}
|
||||
if (!Util.equalsWithNull(defaultMarkRolesForMarkTemplates.get(defaultMarkTemplatesForMarkRoles.get(markRole)), markRole)) {
|
||||
throw new IllegalArgumentException("Mark template " + defaultMarkTemplatesForMarkRoles.get(markRole)
|
||||
+ " used as default for mark role " + markRole
|
||||
+ " does not use that same mark role as its default but instead refers to "
|
||||
+ defaultMarkRolesForMarkTemplates.get(defaultMarkTemplatesForMarkRoles.get(markRole))
|
||||
+ " as its default role");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getShortName() {
|
||||
return shortName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MarkTemplate getDefaultMarkTemplateForMarkRole(MarkRole markRole) {
|
||||
return defaultMarkTemplatesForMarkRoles.get(markRole);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<MarkRole, MarkTemplate> getDefaultMarkTemplatesForMarkRoles() {
|
||||
return defaultMarkTemplatesForMarkRoles;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<MarkTemplate> getMarkTemplates() {
|
||||
return marks;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MarkTemplate getMarkTemplateByIdIfContainedInCourseTemplate(UUID markTemplateId) {
|
||||
MarkTemplate result = null;
|
||||
for (MarkTemplate markTemplate : marks) {
|
||||
if (markTemplate.getId().equals(markTemplateId)) {
|
||||
result = markTemplate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<WaypointTemplate> getWaypointTemplates() {
|
||||
return waypoints;
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar code can be found in {@link CourseConfigurationImpl#getWaypoints(int)}; both methods
|
||||
* rely on {@link RepeatablePart#createSequence(int, Iterable)} to do the actual work.
|
||||
*/
|
||||
@Override
|
||||
public Iterable<WaypointTemplate> getWaypointTemplates(int numberOfLaps) {
|
||||
final Iterable<WaypointTemplate> result;
|
||||
if (hasRepeatablePart()) {
|
||||
if (numberOfLaps < 1) {
|
||||
throw new IllegalArgumentException("The course template "+this+" has a repeatable part, hence the number of laps needs to be at least 1.");
|
||||
}
|
||||
result = optionalRepeatablePart.createSequence(numberOfLaps, waypoints);
|
||||
} else {
|
||||
result = waypoints;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RepeatablePart getRepeatablePart() {
|
||||
return optionalRepeatablePart;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Map<MarkTemplate, MarkRole> getDefaultMarkRolesForMarkTemplates() {
|
||||
return defaultMarkRolesForMarkTemplates;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MarkRole getOptionalAssociatedRole(MarkTemplate markTemplate) {
|
||||
return defaultMarkRolesForMarkTemplates.get(markTemplate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MarkRole getMarkRoleByIdIfContainedInCourseTemplate(UUID markRoleId) {
|
||||
MarkRole result = null;
|
||||
for (MarkRole markRole : defaultMarkRolesForMarkTemplates.values()) {
|
||||
if (markRole.getId().equals(markRoleId)) {
|
||||
result = markRole;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setAssociatedRoles(Map<MarkTemplate, MarkRole> associatedRoles) {
|
||||
this.defaultMarkRolesForMarkTemplates.clear();
|
||||
this.defaultMarkRolesForMarkTemplates.putAll(associatedRoles);
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL getOptionalImageURL() {
|
||||
return optionalImageURL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<String> getTags() {
|
||||
return this.tags;
|
||||
}
|
||||
|
||||
public void setTags(Iterable<String> tags) {
|
||||
this.tags = tags;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getDefaultNumberOfLaps() {
|
||||
return defaultNumberOfLaps;
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package com.sap.sailing.domain.coursetemplate.impl;
|
||||
|
||||
import com.sap.sailing.domain.common.Position;
|
||||
import com.sap.sailing.domain.coursetemplate.FixedPositioning;
|
||||
|
||||
public class FixedPositioningImpl implements FixedPositioning {
|
||||
private final Position fixedPosition;
|
||||
|
||||
public FixedPositioningImpl(Position fixedPosition) {
|
||||
super();
|
||||
this.fixedPosition = fixedPosition;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Position getFixedPosition() {
|
||||
return fixedPosition;
|
||||
}
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
package com.sap.sailing.domain.coursetemplate.impl;
|
||||
|
||||
import com.sap.sailing.domain.coursetemplate.CommonMarkProperties;
|
||||
import com.sap.sailing.domain.coursetemplate.FreestyleMarkConfiguration;
|
||||
import com.sap.sailing.domain.coursetemplate.MarkConfigurationVisitor;
|
||||
import com.sap.sailing.domain.coursetemplate.MarkProperties;
|
||||
import com.sap.sailing.domain.coursetemplate.MarkTemplate;
|
||||
|
||||
public class FreestyleMarkConfigurationImpl<P> extends MarkConfigurationImpl<P> implements FreestyleMarkConfiguration<P> {
|
||||
private static final long serialVersionUID = 8167307943855869321L;
|
||||
|
||||
private final MarkProperties optionalMarkProperties;
|
||||
private final CommonMarkProperties freestyleProperties;
|
||||
|
||||
public FreestyleMarkConfigurationImpl(MarkTemplate optionalMarkTemplate, MarkProperties optionalMarkProperties,
|
||||
CommonMarkProperties freestyleProperties, P annotation) {
|
||||
super(optionalMarkTemplate, annotation);
|
||||
this.optionalMarkProperties = optionalMarkProperties;
|
||||
this.freestyleProperties = freestyleProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T accept(MarkConfigurationVisitor<T, P> visitor) {
|
||||
return visitor.visit(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getShortName() {
|
||||
return freestyleProperties.getShortName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return freestyleProperties.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonMarkProperties getFreestyleProperties() {
|
||||
return freestyleProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonMarkProperties getEffectiveProperties() {
|
||||
return freestyleProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MarkProperties getOptionalMarkProperties() {
|
||||
return optionalMarkProperties;
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package com.sap.sailing.domain.coursetemplate.impl;
|
||||
|
||||
import com.sap.sailing.domain.coursetemplate.MarkConfiguration;
|
||||
import com.sap.sailing.domain.coursetemplate.MarkTemplate;
|
||||
|
||||
public abstract class MarkConfigurationImpl<P> implements MarkConfiguration<P> {
|
||||
private static final long serialVersionUID = -1130451024516101231L;
|
||||
private final MarkTemplate optionalMarkTemplate;
|
||||
private final P annotationInfo;
|
||||
|
||||
public MarkConfigurationImpl(MarkTemplate optionalMarkTemplate, P annotationInfo) {
|
||||
super();
|
||||
this.optionalMarkTemplate = optionalMarkTemplate;
|
||||
this.annotationInfo = annotationInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MarkTemplate getOptionalMarkTemplate() {
|
||||
return optionalMarkTemplate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public P getAnnotationInfo() {
|
||||
return annotationInfo;
|
||||
}
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
package com.sap.sailing.domain.coursetemplate.impl;
|
||||
|
||||
import com.sap.sailing.domain.coursetemplate.MarkConfigurationRequestAnnotation;
|
||||
import com.sap.sailing.domain.coursetemplate.Positioning;
|
||||
|
||||
public class MarkConfigurationRequestAnnotationImpl implements MarkConfigurationRequestAnnotation {
|
||||
private final boolean storeToInventory;
|
||||
private final Positioning optionalPositioning;
|
||||
private final MarkRoleCreationRequest optionalMarkRoleCreationRequest;
|
||||
|
||||
public static class MarkRoleCreationRequestImpl implements MarkRoleCreationRequest {
|
||||
private final String markRoleName;
|
||||
private final String markRoleShortName;
|
||||
public MarkRoleCreationRequestImpl(String markRoleName, String markRoleShortName) {
|
||||
super();
|
||||
this.markRoleName = markRoleName;
|
||||
this.markRoleShortName = markRoleShortName;
|
||||
}
|
||||
@Override
|
||||
public String getMarkRoleName() {
|
||||
return markRoleName;
|
||||
}
|
||||
@Override
|
||||
public String getMarkRoleShortName() {
|
||||
return markRoleShortName;
|
||||
}
|
||||
}
|
||||
|
||||
public MarkConfigurationRequestAnnotationImpl(boolean storeToInventory, Positioning optionalPositioning, MarkRoleCreationRequest optionalMarkRoleCreationRequest) {
|
||||
super();
|
||||
this.storeToInventory = storeToInventory;
|
||||
this.optionalPositioning = optionalPositioning;
|
||||
this.optionalMarkRoleCreationRequest = optionalMarkRoleCreationRequest;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isStoreToInventory() {
|
||||
return storeToInventory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Positioning getOptionalPositioning() {
|
||||
return optionalPositioning;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MarkRoleCreationRequest getOptionalMarkRoleCreationRequest() {
|
||||
return optionalMarkRoleCreationRequest;
|
||||
}
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
package com.sap.sailing.domain.coursetemplate.impl;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import com.sap.sailing.domain.common.DeviceIdentifier;
|
||||
import com.sap.sailing.domain.common.tracking.GPSFix;
|
||||
import com.sap.sailing.domain.coursetemplate.MarkConfigurationResponseAnnotation;
|
||||
import com.sap.sse.common.TimeRange;
|
||||
import com.sap.sse.common.Util.Triple;
|
||||
import com.sap.sse.common.impl.TimeRangeImpl;
|
||||
|
||||
public class MarkConfigurationResponseAnnotationImpl implements MarkConfigurationResponseAnnotation {
|
||||
private final GPSFix lastKnownPosition;
|
||||
private final Iterable<Triple<DeviceIdentifier, TimeRange, GPSFix>> deviceMappings;
|
||||
|
||||
public MarkConfigurationResponseAnnotationImpl(GPSFix lastKnownPosition) {
|
||||
this(lastKnownPosition, Collections.emptySet());
|
||||
}
|
||||
|
||||
public MarkConfigurationResponseAnnotationImpl(GPSFix lastKnownPosition, DeviceIdentifier currentTrackingDevice) {
|
||||
this(lastKnownPosition, Collections.singleton(new Triple<>(currentTrackingDevice, new TimeRangeImpl(null, null), null)));
|
||||
}
|
||||
|
||||
public MarkConfigurationResponseAnnotationImpl(GPSFix lastKnownPosition,
|
||||
Iterable<Triple<DeviceIdentifier, TimeRange, GPSFix>> deviceMappings) {
|
||||
super();
|
||||
this.lastKnownPosition = lastKnownPosition;
|
||||
this.deviceMappings = deviceMappings;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GPSFix getLastKnownPosition() {
|
||||
return lastKnownPosition;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<Triple<DeviceIdentifier, TimeRange, GPSFix>> getDeviceMappings() {
|
||||
return deviceMappings;
|
||||
}
|
||||
}
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
package com.sap.sailing.domain.coursetemplate.impl;
|
||||
|
||||
import com.sap.sailing.domain.coursetemplate.MarkConfiguration;
|
||||
import com.sap.sailing.domain.coursetemplate.MarkPairWithConfiguration;
|
||||
import com.sap.sse.common.impl.NamedImpl;
|
||||
|
||||
public class MarkPairWithConfigurationImpl<P>
|
||||
extends NamedImpl implements MarkPairWithConfiguration<P> {
|
||||
private static final long serialVersionUID = 159552983009183991L;
|
||||
|
||||
private final MarkConfiguration<P> right;
|
||||
private final MarkConfiguration<P> left;
|
||||
|
||||
private final String shortName;
|
||||
|
||||
public MarkPairWithConfigurationImpl(String name, MarkConfiguration<P> left, MarkConfiguration<P> right,
|
||||
String shortName) {
|
||||
super(name);
|
||||
this.left = left;
|
||||
this.right = right;
|
||||
this.shortName = shortName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getShortName() {
|
||||
return this.shortName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MarkConfiguration<P> getLeft() {
|
||||
return left;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MarkConfiguration<P> getRight() {
|
||||
return right;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((getName() == null) ? 0 : getName().hashCode());
|
||||
result = prime * result + ((left == null) ? 0 : left.hashCode());
|
||||
result = prime * result + ((right == null) ? 0 : right.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
MarkPairWithConfigurationImpl<?> other = (MarkPairWithConfigurationImpl<?>) obj;
|
||||
if (getName() == null) {
|
||||
if (other.getName() != null)
|
||||
return false;
|
||||
} else if (!getName().equals(other.getName()))
|
||||
return false;
|
||||
if (left == null) {
|
||||
if (other.left != null)
|
||||
return false;
|
||||
} else if (!left.equals(other.left))
|
||||
return false;
|
||||
if (right == null) {
|
||||
if (other.right != null)
|
||||
return false;
|
||||
} else if (!right.equals(other.right))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
package com.sap.sailing.domain.coursetemplate.impl;
|
||||
|
||||
import com.sap.sailing.domain.coursetemplate.CommonMarkProperties;
|
||||
import com.sap.sailing.domain.coursetemplate.MarkConfigurationVisitor;
|
||||
import com.sap.sailing.domain.coursetemplate.MarkProperties;
|
||||
import com.sap.sailing.domain.coursetemplate.MarkPropertiesBasedMarkConfiguration;
|
||||
import com.sap.sailing.domain.coursetemplate.MarkTemplate;
|
||||
|
||||
public class MarkPropertiesBasedMarkConfigurationImpl<P> extends MarkConfigurationImpl<P>
|
||||
implements MarkPropertiesBasedMarkConfiguration<P> {
|
||||
private static final long serialVersionUID = -1371019872646970791L;
|
||||
|
||||
private final MarkProperties markProperties;
|
||||
|
||||
public MarkPropertiesBasedMarkConfigurationImpl(MarkProperties markProperties, MarkTemplate optionalMarkTemplate, P annotation) {
|
||||
super(optionalMarkTemplate, annotation);
|
||||
this.markProperties = markProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T accept(MarkConfigurationVisitor<T, P> visitor) {
|
||||
return visitor.visit(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getShortName() {
|
||||
return markProperties.getShortName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return markProperties.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MarkProperties getOptionalMarkProperties() {
|
||||
return markProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonMarkProperties getEffectiveProperties() {
|
||||
return markProperties;
|
||||
}
|
||||
}
|
||||
+129
@@ -0,0 +1,129 @@
|
||||
package com.sap.sailing.domain.coursetemplate.impl;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import com.sap.sailing.domain.common.MarkType;
|
||||
import com.sap.sailing.domain.coursetemplate.CommonMarkProperties;
|
||||
import com.sap.sailing.domain.coursetemplate.MarkProperties;
|
||||
import com.sap.sailing.domain.coursetemplate.MarkRole;
|
||||
import com.sap.sailing.domain.coursetemplate.MarkTemplate;
|
||||
import com.sap.sailing.domain.coursetemplate.Positioning;
|
||||
import com.sap.sse.common.Color;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
|
||||
public class MarkPropertiesImpl extends CommonMarkPropertiesImpl implements MarkProperties {
|
||||
private static final long serialVersionUID = -5588202720707030502L;
|
||||
private Positioning positioningInformation;
|
||||
private Iterable<String> tags;
|
||||
private UUID id;
|
||||
|
||||
private final ConcurrentHashMap<MarkTemplate, TimePoint> lastUsedMarkTemplate = new ConcurrentHashMap<>();
|
||||
private final ConcurrentHashMap<MarkRole, TimePoint> lastUsedMarkRole = new ConcurrentHashMap<>();
|
||||
|
||||
public MarkPropertiesImpl(String name, String shortName, Color color, String shape, String pattern, MarkType type) {
|
||||
this(UUID.randomUUID(), name, shortName, color, shape, pattern, type);
|
||||
}
|
||||
|
||||
public MarkPropertiesImpl(UUID id, String name, String shortName, Color color, String shape, String pattern,
|
||||
MarkType type) {
|
||||
super(name, shortName, color, shape, pattern, type);
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public MarkPropertiesImpl(UUID id, final CommonMarkProperties commonMarkProperties) {
|
||||
super(commonMarkProperties);
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setColor(Color color) {
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setShape(String shape) {
|
||||
this.shape = shape;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPattern(String pattern) {
|
||||
this.pattern = pattern;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setType(MarkType type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setShortName(String shortName) {
|
||||
this.shortName = shortName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Positioning getPositioningInformation() {
|
||||
return positioningInformation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPositioningInformation(Positioning positioningInformation) {
|
||||
this.positioningInformation = positioningInformation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<String> getTags() {
|
||||
return this.tags;
|
||||
}
|
||||
|
||||
public void setTags(Iterable<String> tags) {
|
||||
this.tags = tags;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<MarkTemplate, TimePoint> getLastUsedMarkTemplate() {
|
||||
return Collections.unmodifiableMap(lastUsedMarkTemplate);
|
||||
}
|
||||
|
||||
private <T> void recordUsageTimePoint(T object, TimePoint usageTimePoint, Map<T, TimePoint> lastUsedMap) {
|
||||
if (usageTimePoint == null) {
|
||||
throw new IllegalArgumentException("Usage time point must not be null");
|
||||
}
|
||||
if (!lastUsedMap.containsKey(object) || usageTimePoint.after(lastUsedMap.get(object))) {
|
||||
lastUsedMap.put(object, usageTimePoint);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addMarkTemplateUsage(MarkTemplate markTemplate, TimePoint usageTimePoint) {
|
||||
recordUsageTimePoint(markTemplate, usageTimePoint, lastUsedMarkTemplate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<MarkRole, TimePoint> getLastUsedMarkRole() {
|
||||
return Collections.unmodifiableMap(lastUsedMarkRole);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addMarkRoleUsage(MarkRole markRole, TimePoint usageTimePoint) {
|
||||
recordUsageTimePoint(markRole, usageTimePoint, lastUsedMarkRole);
|
||||
}
|
||||
|
||||
public void setLastUsedMarkRole(Map<MarkRole, TimePoint> lastUsedRole) {
|
||||
this.lastUsedMarkRole.clear();
|
||||
this.lastUsedMarkRole.putAll(lastUsedRole);
|
||||
}
|
||||
|
||||
public void setLastUsedMarkTemplate(Map<MarkTemplate, TimePoint> lastUsedTemplate) {
|
||||
this.lastUsedMarkTemplate.clear();
|
||||
this.lastUsedMarkTemplate.putAll(lastUsedTemplate);
|
||||
}
|
||||
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
package com.sap.sailing.domain.coursetemplate.impl;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import com.sap.sailing.domain.coursetemplate.MarkRole;
|
||||
import com.sap.sse.common.impl.NamedWithUUIDImpl;
|
||||
|
||||
public class MarkRoleImpl extends NamedWithUUIDImpl implements MarkRole {
|
||||
private static final long serialVersionUID = 3661478108460413196L;
|
||||
private final String shortName;
|
||||
|
||||
public MarkRoleImpl(UUID id, String name, String shortName) {
|
||||
super(name, id);
|
||||
this.shortName = shortName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getShortName() {
|
||||
return this.shortName;
|
||||
}
|
||||
}
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
package com.sap.sailing.domain.coursetemplate.impl;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import com.sap.sailing.domain.coursetemplate.MarkRole;
|
||||
import com.sap.sailing.domain.coursetemplate.MarkRolePair;
|
||||
import com.sap.sse.common.impl.NamedImpl;
|
||||
|
||||
public class MarkRolePairImpl extends NamedImpl implements MarkRolePair {
|
||||
private static final long serialVersionUID = -4966456947099578789L;
|
||||
private final MarkRole left;
|
||||
private final MarkRole right;
|
||||
private final String shortName;
|
||||
|
||||
public MarkRolePairImpl(String name, String shortName, MarkRole left, MarkRole right) {
|
||||
super(name);
|
||||
this.shortName = shortName;
|
||||
this.left = left;
|
||||
this.right = right;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<MarkRole> getMarkRoles() {
|
||||
return Arrays.asList(getLeft(), getRight());
|
||||
}
|
||||
|
||||
@Override
|
||||
public MarkRole getLeft() {
|
||||
return left;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MarkRole getRight() {
|
||||
return right;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getShortName() {
|
||||
return shortName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((getName() == null) ? 0 : getName().hashCode());
|
||||
result = prime * result + ((getShortName() == null) ? 0 : getShortName().hashCode());
|
||||
result = prime * result + ((left == null) ? 0 : left.hashCode());
|
||||
result = prime * result + ((right == null) ? 0 : right.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
MarkRolePairImpl other = (MarkRolePairImpl) obj;
|
||||
if (getName() == null) {
|
||||
if (other.getName() != null)
|
||||
return false;
|
||||
} else if (!getName().equals(other.getName()))
|
||||
return false;
|
||||
if (getShortName() == null) {
|
||||
if (other.getShortName() != null)
|
||||
return false;
|
||||
} else if (!getShortName().equals(other.getShortName()))
|
||||
return false;
|
||||
if (left == null) {
|
||||
if (other.left != null)
|
||||
return false;
|
||||
} else if (!left.equals(other.left))
|
||||
return false;
|
||||
if (right == null) {
|
||||
if (other.right != null)
|
||||
return false;
|
||||
} else if (!right.equals(other.right))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
package com.sap.sailing.domain.coursetemplate.impl;
|
||||
|
||||
import com.sap.sailing.domain.coursetemplate.CommonMarkProperties;
|
||||
import com.sap.sailing.domain.coursetemplate.MarkConfigurationVisitor;
|
||||
import com.sap.sailing.domain.coursetemplate.MarkProperties;
|
||||
import com.sap.sailing.domain.coursetemplate.MarkPropertiesBasedMarkConfiguration;
|
||||
import com.sap.sailing.domain.coursetemplate.MarkTemplate;
|
||||
import com.sap.sailing.domain.coursetemplate.MarkTemplateBasedMarkConfiguration;
|
||||
|
||||
public class MarkTemplateBasedMarkConfigurationImpl<P> extends MarkConfigurationImpl<P>
|
||||
implements MarkTemplateBasedMarkConfiguration<P> {
|
||||
private static final long serialVersionUID = -6683111363486434715L;
|
||||
|
||||
public MarkTemplateBasedMarkConfigurationImpl(MarkTemplate markTemplate, P annotation) {
|
||||
super(markTemplate, annotation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T accept(MarkConfigurationVisitor<T, P> visitor) {
|
||||
return visitor.visit(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonMarkProperties getEffectiveProperties() {
|
||||
return getOptionalMarkTemplate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return getOptionalMarkTemplate().getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getShortName() {
|
||||
return getOptionalMarkTemplate().getShortName();
|
||||
}
|
||||
|
||||
/**
|
||||
* When configuring a mark from a {@link MarkTemplate} using an instance of this type, no
|
||||
* {@link MarkProperties} can be specified, and therefore this method always returns {@code null}.
|
||||
* To provide {@link MarkProperties} that configure a mark and connect it with other properties
|
||||
* that are based on a {@link MarkTemplate}, use {@link MarkPropertiesBasedMarkConfiguration}
|
||||
* with a non-{@code null} {@link MarkPropertiesBasedMarkConfiguration#getOptionalMarkTemplate()}
|
||||
* return value.
|
||||
*/
|
||||
@Override
|
||||
public MarkProperties getOptionalMarkProperties() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
package com.sap.sailing.domain.coursetemplate.impl;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import com.sap.sailing.domain.common.MarkType;
|
||||
import com.sap.sailing.domain.coursetemplate.CommonMarkProperties;
|
||||
import com.sap.sailing.domain.coursetemplate.MarkTemplate;
|
||||
import com.sap.sse.common.Color;
|
||||
import com.sap.sse.common.impl.NamedWithUUIDImpl;
|
||||
|
||||
public class MarkTemplateImpl extends NamedWithUUIDImpl implements MarkTemplate {
|
||||
private static final long serialVersionUID = 620307743421809258L;
|
||||
private final String shortName;
|
||||
private final Color color;
|
||||
private final String shape;
|
||||
private final String pattern;
|
||||
private final MarkType type;
|
||||
|
||||
public MarkTemplateImpl(String name, String shortName, Color color, String shape, String pattern, MarkType type) {
|
||||
super(name);
|
||||
this.shortName = shortName;
|
||||
this.color = color;
|
||||
this.shape = shape;
|
||||
this.pattern = pattern;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public MarkTemplateImpl(UUID id, String name, String shortName, Color color, String shape, String pattern, MarkType type) {
|
||||
super(name, id);
|
||||
this.shortName = shortName;
|
||||
this.color = color;
|
||||
this.shape = shape;
|
||||
this.pattern = pattern;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public MarkTemplateImpl(UUID id, CommonMarkProperties commonMarkProperties) {
|
||||
super(commonMarkProperties.getName(), id);
|
||||
this.shortName = commonMarkProperties.getShortName();
|
||||
this.color = commonMarkProperties.getColor();
|
||||
this.shape = commonMarkProperties.getShape();
|
||||
this.pattern = commonMarkProperties.getPattern();
|
||||
this.type = commonMarkProperties.getType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Color getColor() {
|
||||
return color;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getShape() {
|
||||
return shape;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPattern() {
|
||||
return pattern;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MarkType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getShortName() {
|
||||
return shortName;
|
||||
}
|
||||
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
package com.sap.sailing.domain.coursetemplate.impl;
|
||||
|
||||
import com.sap.sailing.domain.base.Mark;
|
||||
import com.sap.sailing.domain.coursetemplate.CommonMarkProperties;
|
||||
import com.sap.sailing.domain.coursetemplate.MarkConfigurationVisitor;
|
||||
import com.sap.sailing.domain.coursetemplate.MarkProperties;
|
||||
import com.sap.sailing.domain.coursetemplate.MarkTemplate;
|
||||
import com.sap.sailing.domain.coursetemplate.RegattaMarkConfiguration;
|
||||
|
||||
public class RegattaMarkConfigurationImpl<P> extends MarkConfigurationImpl<P> implements RegattaMarkConfiguration<P> {
|
||||
private static final long serialVersionUID = 329929810377510675L;
|
||||
|
||||
private final Mark mark;
|
||||
private final MarkProperties optionalMarkProperties;
|
||||
|
||||
public RegattaMarkConfigurationImpl(Mark mark, P additionalInfo, MarkTemplate optionalMarkTemplate,
|
||||
MarkProperties optionalMarkProperties) {
|
||||
super(optionalMarkTemplate, additionalInfo);
|
||||
this.mark = mark;
|
||||
this.optionalMarkProperties = optionalMarkProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T accept(MarkConfigurationVisitor<T, P> visitor) {
|
||||
return visitor.visit(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getShortName() {
|
||||
return mark.getShortName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return mark.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mark getMark() {
|
||||
return mark;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonMarkProperties getEffectiveProperties() {
|
||||
return mark;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MarkProperties getOptionalMarkProperties() {
|
||||
return optionalMarkProperties;
|
||||
}
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
package com.sap.sailing.domain.coursetemplate.impl;
|
||||
|
||||
import com.sap.sailing.domain.coursetemplate.RepeatablePart;
|
||||
|
||||
public class RepeatablePartImpl implements RepeatablePart {
|
||||
private static final long serialVersionUID = -7432656529810302123L;
|
||||
|
||||
private final int zeroBasedIndexOfRepeatablePartStart;
|
||||
|
||||
private final int zeroBasedIndexOfRepeatablePartEnd;
|
||||
|
||||
public RepeatablePartImpl(int zeroBasedIndexOfRepeatablePartStart, int zeroBasedIndexOfRepeatablePartEnd) {
|
||||
super();
|
||||
if (zeroBasedIndexOfRepeatablePartStart < 0 || zeroBasedIndexOfRepeatablePartEnd < 0) {
|
||||
throw new IllegalArgumentException("No negative indices are possible for the repeatable part.");
|
||||
}
|
||||
if (zeroBasedIndexOfRepeatablePartStart >= zeroBasedIndexOfRepeatablePartEnd) {
|
||||
throw new IllegalArgumentException(
|
||||
"The start of the repeatable part needs to be less than the end such that the repeatable part has at least one element.");
|
||||
}
|
||||
this.zeroBasedIndexOfRepeatablePartStart = zeroBasedIndexOfRepeatablePartStart;
|
||||
this.zeroBasedIndexOfRepeatablePartEnd = zeroBasedIndexOfRepeatablePartEnd;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getZeroBasedIndexOfRepeatablePartStart() {
|
||||
return zeroBasedIndexOfRepeatablePartStart;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getZeroBasedIndexOfRepeatablePartEnd() {
|
||||
return zeroBasedIndexOfRepeatablePartEnd;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "["+getZeroBasedIndexOfRepeatablePartStart()+".."+getZeroBasedIndexOfRepeatablePartEnd()+"]";
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package com.sap.sailing.domain.coursetemplate.impl;
|
||||
|
||||
import com.sap.sailing.domain.common.DeviceIdentifier;
|
||||
import com.sap.sailing.domain.coursetemplate.TrackingDeviceBasedPositioning;
|
||||
|
||||
public class TrackingDeviceBasedPositioningImpl implements TrackingDeviceBasedPositioning {
|
||||
private final DeviceIdentifier deviceIdentifier;
|
||||
|
||||
public TrackingDeviceBasedPositioningImpl(DeviceIdentifier deviceIdentifier) {
|
||||
super();
|
||||
this.deviceIdentifier = deviceIdentifier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeviceIdentifier getDeviceIdentifier() {
|
||||
return deviceIdentifier;
|
||||
}
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
package com.sap.sailing.domain.coursetemplate.impl;
|
||||
|
||||
import com.sap.sailing.domain.common.PassingInstruction;
|
||||
import com.sap.sailing.domain.coursetemplate.ControlPointTemplate;
|
||||
import com.sap.sailing.domain.coursetemplate.WaypointTemplate;
|
||||
|
||||
public class WaypointTemplateImpl implements WaypointTemplate {
|
||||
private static final long serialVersionUID = -4708322032365234982L;
|
||||
|
||||
private final PassingInstruction passingInstruction;
|
||||
private final ControlPointTemplate controlPointTemplate;
|
||||
|
||||
public WaypointTemplateImpl(ControlPointTemplate controlPoint, PassingInstruction passingInstruction) {
|
||||
this.passingInstruction = passingInstruction;
|
||||
this.controlPointTemplate = controlPoint;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ControlPointTemplate getControlPointTemplate() {
|
||||
return controlPointTemplate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PassingInstruction getPassingInstruction() {
|
||||
return passingInstruction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "WaypointTemplateImpl [passingInstruction=" + passingInstruction + ", controlPointTemplate="
|
||||
+ controlPointTemplate + "]";
|
||||
}
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
package com.sap.sailing.domain.coursetemplate.impl;
|
||||
|
||||
import com.sap.sailing.domain.common.PassingInstruction;
|
||||
import com.sap.sailing.domain.coursetemplate.ControlPointWithMarkConfiguration;
|
||||
import com.sap.sailing.domain.coursetemplate.WaypointWithMarkConfiguration;
|
||||
|
||||
public class WaypointWithMarkConfigurationImpl<P> implements WaypointWithMarkConfiguration<P> {
|
||||
private final ControlPointWithMarkConfiguration<P> controlPoint;
|
||||
private final PassingInstruction passingInstruction;
|
||||
|
||||
public WaypointWithMarkConfigurationImpl(ControlPointWithMarkConfiguration<P> controlPoint,
|
||||
PassingInstruction passingInstruction) {
|
||||
super();
|
||||
this.controlPoint = controlPoint;
|
||||
this.passingInstruction = passingInstruction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ControlPointWithMarkConfiguration<P> getControlPoint() {
|
||||
return controlPoint;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PassingInstruction getPassingInstruction() {
|
||||
return passingInstruction;
|
||||
}
|
||||
|
||||
}
|
||||
File renamed without changes.
+3
-1
@@ -1,7 +1,9 @@
|
||||
package com.sap.sailing.domain.racelogtracking;
|
||||
package com.sap.sailing.domain.racelogtracking.impl;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import com.sap.sailing.domain.racelogtracking.PingDeviceIdentifier;
|
||||
|
||||
public class PingDeviceIdentifierImpl implements PingDeviceIdentifier {
|
||||
private static final long serialVersionUID = -4049961972156611640L;
|
||||
private final UUID id;
|
||||
+2
-1
@@ -79,7 +79,8 @@ public interface DomainFactory {
|
||||
long delayToLiveInMillis, SwissTimingFactory swissTimingFactory, DomainFactory domainFactory,
|
||||
RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, boolean useInternalMarkPassingAlgorithm, boolean trackWind, boolean correctWindDirectionByMagneticDeclination, String updateURL, String updateUsername, String updatePassword);
|
||||
|
||||
ControlPoint getOrCreateControlPoint(String description, Iterable<Serializable> deviceIds, MarkType markType);
|
||||
ControlPoint getOrCreateControlPoint(String description, Iterable<Serializable> deviceIds, MarkType markType,
|
||||
String shortNameOfGate);
|
||||
|
||||
RaceDefinition createRaceDefinition(Regatta regatta, String swissTimingRaceID, Map<Competitor, Boat> competitorsAndBoats,
|
||||
List<ControlPoint> courseDefinition, String raceName, String raceIdForRaceDefinition,
|
||||
|
||||
Loaded 100 of 415 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user