added a first set of domain model implementation classes for course templates

Change-Id: I51cdb6fc588bfc055fff652b3b4616fc3b88796a
This commit is contained in:
Axel Uhl committed 2019-07-04 17:29:55 +02:00
1 parent 9f4f153329
commit 47650fda72
12 files changed
+542 -35

No files matched your search

@@ -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.
*/
@@ -28,7 +28,24 @@ import com.sap.sse.common.NamedWithID;
*
*/
public interface CourseTemplate extends NamedWithID {
/**
* 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> getMarks();
/**
* 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.
*/
Iterable<WaypointTemplate> getWaypoints(int numberOfLaps);
}
@@ -1,35 +1,92 @@
package com.sap.sailing.domain.coursetemplate;
import java.util.Map;
import com.sap.sailing.domain.base.Mark;
import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sailing.domain.common.MarkType;
import com.sap.sailing.domain.common.Position;
import com.sap.sse.common.Color;
import com.sap.sse.common.NamedWithID;
import com.sap.sse.common.TimePoint;
/**
* 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>
* 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>
* 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.
*
* {@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 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.
*
* @author Axel Uhl (d043530)
*
*/
public interface MarkProperties extends NamedWithID {
Color getColor();
String getShape();
String getPattern();
MarkType getType();
String getShortName();
public interface MarkProperties extends CommonMarkProperties, NamedWithID {
void setColor(Color color);
void setShape(String shape);
void setPattern(String pattern);
void setType(MarkType type);
void setShortName(String shortName);
/**
* If not {@code null} then a device identifier that can be used to create a device mapping
* in the scope of a regatta such that the tracking device with the ID returned will be used
* to track the mark to which these properties are applied. No timing for any device mapping is
* provided here. It is up to the process of creating and configuring the regatta marks to decide
* about device mapping time intervals.
*/
DeviceIdentifier getTrackingDeviceIdentifier();
/**
* Updates this properties object such that the next call to {@link #getTrackingDeviceIdentifier()} returns the
* {@code deviceIdentifier} provided to this call. The {@code deviceIdentifier} may be {@code null}, meaning that no
* default tracking device mapping is desired for marks configured with these properties. It is an error, and an
* {@link IllegalStateException} will be thrown, if a non-{@code null} {@link #getFixedPosition() fixed position} is
* currently defined by these properties because a mark cannot have a fixed position and be tracked by a device at
* the same time.
*/
void setTrackingDeviceIdentifier(DeviceIdentifier deviceIdentifier);
/**
* Returns a fixed position to be used to "ping" the mark to which these properties are applied; or {@code null} in
* case the mark is not at a fixed position or no position is known. In particular, it is considered an error to
* provide a non-{@code null} fixed position when a non-{@code null} {@link #getTrackingDeviceIdentifier() tracking
* device identifier} has been provided for these mark properties.
*/
Position getFixedPosition();
/**
* Provides a fixed position to be set by means of a "ping" when these properties are applied to a mark. The
* {@code fixedPosition} parameter can be {@code null}, meaning that no fixed position is known (anymore). It is an
* error, and an {@link IllegalStateException} will be thrown, if a non-{@code null}
* {@link #getTrackingDeviceIdentifier() tracking device identifier} is currently defined for these mark properties
* because a mark cannot have a fixed position and be tracked by a device at the same time.
*/
void setFixedPosition(Position fixedPosition);
/**
* Whenever these properties have been applied to configure a mark that was created from a {@link MarkTemplate}, the
* application of this object is remembered. With this method, previous applications can be looked up. In addition
* to the {@link MarkTemplate}s to which these properties were applied, the time point of applying these properties
* is recorded to allow clients to make default proposals based on a "most recently used" basis.
*/
Map<MarkTemplate, TimePoint> getPreviousUsage();
}
@@ -1,8 +1,6 @@
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;
/**
* A template for creating a {@link Mark}. It has a globally unique ID and can be used in zero or more
@@ -13,10 +11,5 @@ import com.sap.sse.common.Color;
* @author Axel Uhl (d043530)
*
*/
public interface MarkTemplate extends ControlPointTemplate {
Color getColor();
String getShape();
String getPattern();
MarkType getType();
String getShortName();
public interface MarkTemplate extends ControlPointTemplate, CommonMarkProperties {
}
@@ -0,0 +1,60 @@
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.sse.common.Color;
import com.sap.sse.common.impl.NamedWithIDImpl;
public abstract class CommonMarkPropertiesImpl extends NamedWithIDImpl 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(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;
}
@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;
}
}
@@ -0,0 +1,27 @@
package com.sap.sailing.domain.coursetemplate.impl;
import java.util.HashSet;
import java.util.Set;
import com.sap.sailing.domain.coursetemplate.ControlPointTemplate;
import com.sap.sailing.domain.coursetemplate.MarkTemplate;
import com.sap.sse.common.Util;
import com.sap.sse.common.impl.NamedWithIDImpl;
public class ControlPointTemplateImpl extends NamedWithIDImpl implements ControlPointTemplate {
private static final long serialVersionUID = 5150679935881533985L;
private final Iterable<MarkTemplate> marks;
public ControlPointTemplateImpl(String name, Iterable<MarkTemplate> marks) {
super(name);
final Set<MarkTemplate> theMarks = new HashSet<>();
Util.addAll(marks, theMarks);
this.marks = theMarks;
}
@Override
public Iterable<MarkTemplate> getMarks() {
return marks;
}
}
@@ -0,0 +1,107 @@
package com.sap.sailing.domain.coursetemplate.impl;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import com.sap.sailing.domain.coursetemplate.CourseTemplate;
import com.sap.sailing.domain.coursetemplate.MarkTemplate;
import com.sap.sailing.domain.coursetemplate.WaypointTemplate;
import com.sap.sse.common.Util;
import com.sap.sse.common.impl.NamedWithIDImpl;
public class CourseTemplateImpl extends NamedWithIDImpl implements CourseTemplate {
private static final long serialVersionUID = -183875832585632806L;
private final Iterable<MarkTemplate> marks;
private final ArrayList<WaypointTemplate> waypoints;
/**
* The index into {@link #waypoints} of the first waypoint that is to be cloned for repetitive laps.
* -1 means no repeatable part.
*/
private final int zeroBasedIndexOfRepeatablePartStart;
/**
* The index into {@link #waypoints} of the first waypoint that comes after the sub-sequence to be cloned for
* repetitive laps. -1 means no repeatable part.
*/
private final int zeroBasedIndexOfRepeatablePartEnd;
public CourseTemplateImpl(String name, Iterable<MarkTemplate> marks, Iterable<WaypointTemplate> waypoints) {
this(name, marks, waypoints, -1, -1); // no repeatable part
}
public CourseTemplateImpl(String name, Iterable<MarkTemplate> marks, Iterable<WaypointTemplate> waypoints,
int zeroBasedIndexOfRepeatablePartStart, int zeroBasedIndexOfRepeatablePartEnd) {
super(name);
if ((zeroBasedIndexOfRepeatablePartEnd == -1) != (zeroBasedIndexOfRepeatablePartStart == -1)) {
throw new IllegalArgumentException("Either both, start and end of repeatable sub-sequence indices must be -1 or none: "+
zeroBasedIndexOfRepeatablePartStart+".."+zeroBasedIndexOfRepeatablePartEnd);
}
if (zeroBasedIndexOfRepeatablePartStart > zeroBasedIndexOfRepeatablePartEnd) {
throw new IllegalArgumentException("A repeatable part must span a positive number of waypoints. It doesn't: "+
zeroBasedIndexOfRepeatablePartStart+".."+zeroBasedIndexOfRepeatablePartEnd);
}
final Set<MarkTemplate> theMarks = new HashSet<>();
Util.addAll(marks, theMarks);
this.waypoints = new ArrayList<>();
Util.addAll(waypoints, this.waypoints);
this.marks = theMarks;
this.zeroBasedIndexOfRepeatablePartStart = zeroBasedIndexOfRepeatablePartStart;
this.zeroBasedIndexOfRepeatablePartEnd = zeroBasedIndexOfRepeatablePartEnd;
validateWaypointsAgainstMarks();
}
/**
* Throws an {@link IllegalArgumentException} in case a waypoint from {@link #waypoints} uses a mark template that
* is not in {@link #marks}.
*/
private void validateWaypointsAgainstMarks() {
for (final WaypointTemplate waypoint : waypoints) {
for (final MarkTemplate mark : waypoint.getControlPoint().getMarks()) {
if (!Util.contains(marks, mark)) {
throw new IllegalArgumentException("Mark "+mark+" used by waypoint template "+
waypoint+" in course template "+this+" is not provided in the collection of marks");
}
}
}
}
@Override
public Iterable<MarkTemplate> getMarks() {
return marks;
}
@Override
public Iterable<WaypointTemplate> getWaypoints(int numberOfLaps) {
final Iterable<WaypointTemplate> result;
if (hasRepeatablePart()) {
final List<WaypointTemplate> resultList = new LinkedList<>();
for (int i=0; i<waypoints.size(); i++) {
if (i == zeroBasedIndexOfRepeatablePartStart) {
for (int lap=1; lap<numberOfLaps; lap++) {
for (i=zeroBasedIndexOfRepeatablePartStart; i<zeroBasedIndexOfRepeatablePartEnd; i++) {
resultList.add(waypoints.get(i));
}
}
}
// special case: repeatable part ends at end of waypoint list:
if (i<waypoints.size()) {
resultList.add(waypoints.get(i));
}
}
result = resultList;
} else {
result = waypoints;
}
return result;
}
private boolean hasRepeatablePart() {
return zeroBasedIndexOfRepeatablePartEnd == -1;
}
}
@@ -0,0 +1,42 @@
package com.sap.sailing.domain.coursetemplate.impl;
import java.util.Arrays;
import java.util.UUID;
import com.sap.sailing.domain.coursetemplate.MarkPairTemplate;
import com.sap.sailing.domain.coursetemplate.MarkTemplate;
import com.sap.sse.common.impl.NamedWithIDImpl;
public class MarkPairTemplateImpl extends NamedWithIDImpl implements MarkPairTemplate {
private static final long serialVersionUID = -4966456947099578789L;
private final MarkTemplate left;
private final MarkTemplate right;
public MarkPairTemplateImpl(String name, MarkTemplate left, MarkTemplate right) {
super(name);
this.left = left;
this.right = right;
}
public MarkPairTemplateImpl(UUID id, String name, MarkTemplate left, MarkTemplate right) {
super(name, id);
this.left = left;
this.right = right;
}
@Override
public Iterable<MarkTemplate> getMarks() {
return Arrays.asList(getLeft(), getRight());
}
@Override
public MarkTemplate getLeft() {
return left;
}
@Override
public MarkTemplate getRight() {
return right;
}
}
@@ -0,0 +1,81 @@
package com.sap.sailing.domain.coursetemplate.impl;
import java.util.Collections;
import java.util.HashMap;
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.MarkProperties;
import com.sap.sailing.domain.coursetemplate.MarkTemplate;
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 DeviceIdentifier trackingDeviceIdentifier;
private Position fixedPosition;
private final Map<MarkTemplate, TimePoint> previousUsage = new HashMap<MarkTemplate, TimePoint>();
public MarkPropertiesImpl(String name, String shortName, Color color, String shape, String pattern, MarkType type) {
super(name, shortName, color, shape, pattern, type);
}
public MarkPropertiesImpl(UUID id, String name, String shortName, Color color, String shape, String pattern,
MarkType type) {
super(id, name, shortName, color, shape, pattern, type);
}
@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 DeviceIdentifier getTrackingDeviceIdentifier() {
return trackingDeviceIdentifier;
}
@Override
public void setTrackingDeviceIdentifier(DeviceIdentifier trackingDeviceIdentifier) {
this.trackingDeviceIdentifier = trackingDeviceIdentifier;
}
@Override
public Position getFixedPosition() {
return fixedPosition;
}
@Override
public void setFixedPosition(Position fixedPosition) {
this.fixedPosition = fixedPosition;
}
@Override
public Map<MarkTemplate, TimePoint> getPreviousUsage() {
return Collections.unmodifiableMap(previousUsage);
}
}
@@ -0,0 +1,67 @@
package com.sap.sailing.domain.coursetemplate.impl;
import java.util.Collections;
import java.util.UUID;
import com.sap.sailing.domain.common.MarkType;
import com.sap.sailing.domain.coursetemplate.MarkTemplate;
import com.sap.sse.common.Color;
import com.sap.sse.common.impl.NamedWithIDImpl;
public class MarkTemplateImpl extends NamedWithIDImpl 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;
}
@Override
public Iterable<MarkTemplate> getMarks() {
return Collections.singleton(this);
}
@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;
}
}
@@ -0,0 +1,29 @@
package com.sap.sailing.domain.coursetemplate.impl;
import java.io.Serializable;
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 {
@Override
public Serializable getId() {
// TODO Auto-generated method stub
return null;
}
@Override
public ControlPointTemplate getControlPoint() {
// TODO Auto-generated method stub
return null;
}
@Override
public PassingInstruction getPassingInstruction() {
// TODO Auto-generated method stub
return null;
}
}
@@ -0,0 +1,28 @@
package com.sap.sse.common.impl;
import java.io.Serializable;
import java.util.UUID;
import com.sap.sse.common.NamedWithID;
public class NamedWithIDImpl extends NamedImpl implements NamedWithID {
private static final long serialVersionUID = 8259043761754615072L;
private final Serializable id;
/**
* Constructs a new random {@link UUID} as this object's {@link #getId() ID}.
*/
public NamedWithIDImpl(String name) {
this(name, UUID.randomUUID());
}
public NamedWithIDImpl(String name, Serializable id) {
super(name);
this.id = id;
}
@Override
public Serializable getId() {
return id;
}
}