Merge remote-tracking branch 'origin/master' into bug3591_datamining_legtype

This commit is contained in:
fmittag
2016-04-23 14:30:02 +02:00
44 changed files with 219 additions and 89 deletions
@@ -9,15 +9,23 @@ import java.util.List;
* When modifying these values also check res/preferences.xml of racecommittee.app! * When modifying these values also check res/preferences.xml of racecommittee.app!
*/ */
public enum CourseDesignerMode { public enum CourseDesignerMode {
UNKNOWN("Unknown"), UNKNOWN("Unknown", /* waypointSequenceValid */ false),
BY_NAME("By-Name Course Designer"), // O2, O3, I2,... BY_NAME("By-Name Course Designer", /* waypointSequenceValid */ false), // O2, O3, I2,...
BY_MAP("By-Map Course Designer"), // Map-based Course Designer BY_MAP("By-Map Course Designer", /* waypointSequenceValid */ true), // Map-based Course Designer
BY_MARKS("By-Marks Course Designer"); // ESS BY_MARKS("By-Marks Course Designer", /* waypointSequenceValid */ true), // ESS
ADMIN_CONSOLE("Administration Console", /* waypointSequenceValid */ true);
private String displayName; private final String displayName;
/**
* Some course designers may only be able to tell, say, a name for the new course but not
* necessarily a valid waypoint sequence. This flag tells.
*/
private final boolean waypointSequenceValid;
private CourseDesignerMode(String displayName) { private CourseDesignerMode(String displayName, boolean waypointSequenceValid) {
this.displayName = displayName; this.displayName = displayName;
this.waypointSequenceValid = waypointSequenceValid;
} }
@Override @Override
@@ -25,6 +33,10 @@ public enum CourseDesignerMode {
return displayName; return displayName;
} }
public boolean isWaypointSequenceValid() {
return waypointSequenceValid;
}
public static CourseDesignerMode[] validValues() { public static CourseDesignerMode[] validValues() {
List<CourseDesignerMode> validValues = new ArrayList<CourseDesignerMode>(); List<CourseDesignerMode> validValues = new ArrayList<CourseDesignerMode>();
for (CourseDesignerMode type : values()) { for (CourseDesignerMode type : values()) {
@@ -143,6 +143,7 @@ import com.sap.sailing.domain.base.impl.SailingServerConfigurationImpl;
import com.sap.sailing.domain.base.impl.SeriesImpl; import com.sap.sailing.domain.base.impl.SeriesImpl;
import com.sap.sailing.domain.base.impl.VenueImpl; import com.sap.sailing.domain.base.impl.VenueImpl;
import com.sap.sailing.domain.base.impl.WaypointImpl; import com.sap.sailing.domain.base.impl.WaypointImpl;
import com.sap.sailing.domain.common.CourseDesignerMode;
import com.sap.sailing.domain.common.MarkType; import com.sap.sailing.domain.common.MarkType;
import com.sap.sailing.domain.common.MaxPointsReason; import com.sap.sailing.domain.common.MaxPointsReason;
import com.sap.sailing.domain.common.PassingInstruction; import com.sap.sailing.domain.common.PassingInstruction;
@@ -1498,7 +1499,9 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
private RaceLogCourseDesignChangedEvent loadRaceLogCourseDesignChangedEvent(TimePoint createdAt, AbstractLogEventAuthor author, TimePoint logicalTimePoint, Serializable id, Integer passId, List<Competitor> competitors, DBObject dbObject) { private RaceLogCourseDesignChangedEvent loadRaceLogCourseDesignChangedEvent(TimePoint createdAt, AbstractLogEventAuthor author, TimePoint logicalTimePoint, Serializable id, Integer passId, List<Competitor> competitors, DBObject dbObject) {
String courseName = (String) dbObject.get(FieldNames.RACE_LOG_COURSE_DESIGN_NAME.name()); String courseName = (String) dbObject.get(FieldNames.RACE_LOG_COURSE_DESIGN_NAME.name());
CourseBase courseData = loadCourseData((BasicDBList) dbObject.get(FieldNames.RACE_LOG_COURSE_DESIGN.name()), courseName); CourseBase courseData = loadCourseData((BasicDBList) dbObject.get(FieldNames.RACE_LOG_COURSE_DESIGN.name()), courseName);
return new RaceLogCourseDesignChangedEventImpl(createdAt, logicalTimePoint, author, id, passId, courseData); final String courseDesignerModeName = (String) dbObject.get(FieldNames.RACE_LOG_COURSE_DESIGNER_MODE.name());
final CourseDesignerMode courseDesignerMode = courseDesignerModeName == null ? null : CourseDesignerMode.valueOf(courseDesignerModeName);
return new RaceLogCourseDesignChangedEventImpl(createdAt, logicalTimePoint, author, id, passId, courseData, courseDesignerMode);
} }
private RaceLogCourseAreaChangedEvent loadRaceLogCourseAreaChangedEvent(TimePoint createdAt, AbstractLogEventAuthor author, TimePoint logicalTimePoint, Serializable id, Integer passId, List<Competitor> competitors, DBObject dbObject) { private RaceLogCourseAreaChangedEvent loadRaceLogCourseAreaChangedEvent(TimePoint createdAt, AbstractLogEventAuthor author, TimePoint logicalTimePoint, Serializable id, Integer passId, List<Competitor> competitors, DBObject dbObject) {
@@ -88,7 +88,7 @@ public enum FieldNames {
// race log // race log
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_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_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, WAYPOINT_ID, WAYPOINT_NAME, 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, 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, CONTROLPOINT_VALUE, CONTROLPOINTWITHTWOMARKS_ID, CONTROLPOINTWITHTWOMARKS_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_POSITIONED_COMPETITORS, RACE_LOG_PATHFINDER_ID, RACE_LOG_GATE_LINE_OPENING_TIME, RACE_LOG_GOLF_DOWN_TIME, RACE_LOG_START_PROCEDURE_TYPE,
@@ -1116,6 +1116,8 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
storeRaceLogEventProperties(courseDesignChangedEvent, result); storeRaceLogEventProperties(courseDesignChangedEvent, result);
result.put(FieldNames.RACE_LOG_EVENT_CLASS.name(), RaceLogCourseDesignChangedEvent.class.getSimpleName()); 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_DESIGN_NAME.name(), courseDesignChangedEvent.getCourseDesign().getName());
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())); result.put(FieldNames.RACE_LOG_COURSE_DESIGN.name(), storeCourseBase(courseDesignChangedEvent.getCourseDesign()));
return result; return result;
} }
@@ -436,7 +436,7 @@ public class RaceLogRaceTracker implements RaceTracker, GPSFixReceivedListener {
private void onCourseDesignChangedEvent(RaceLogCourseDesignChangedEvent event) { private void onCourseDesignChangedEvent(RaceLogCourseDesignChangedEvent event) {
if (trackedRace != null) { if (trackedRace != null) {
CourseBase base = new LastPublishedCourseDesignFinder(params.getRaceLog()).analyze(); CourseBase base = new LastPublishedCourseDesignFinder(params.getRaceLog(), /* onlyCoursesWithValidWaypointList */ true).analyze();
List<Util.Pair<ControlPoint, PassingInstruction>> update = new ArrayList<>(); List<Util.Pair<ControlPoint, PassingInstruction>> update = new ArrayList<>();
for (Waypoint waypoint : base.getWaypoints()) { for (Waypoint waypoint : base.getWaypoints()) {
update.add(new Util.Pair<>(waypoint.getControlPoint(), waypoint.getPassingInstructions())); update.add(new Util.Pair<>(waypoint.getControlPoint(), waypoint.getPassingInstructions()));
@@ -462,7 +462,7 @@ public class RaceLogRaceTracker implements RaceTracker, GPSFixReceivedListener {
} }
BoatClass boatClass = denoteEvent.getBoatClass(); BoatClass boatClass = denoteEvent.getBoatClass();
String raceName = denoteEvent.getRaceName(); String raceName = denoteEvent.getRaceName();
CourseBase courseBase = new LastPublishedCourseDesignFinder(raceLog).analyze(); CourseBase courseBase = new LastPublishedCourseDesignFinder(raceLog, /* onlyCoursesWithValidWaypointList */ true).analyze();
if (courseBase == null) { if (courseBase == null) {
courseBase = new CourseDataImpl("Default course for " + raceName); courseBase = new CourseDataImpl("Default course for " + raceName);
logger.log(Level.FINE, "Using empty course in creation of race " + raceName); logger.log(Level.FINE, "Using empty course in creation of race " + raceName);
@@ -47,6 +47,7 @@ import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.base.SharedDomainFactory; import com.sap.sailing.domain.base.SharedDomainFactory;
import com.sap.sailing.domain.base.Waypoint; import com.sap.sailing.domain.base.Waypoint;
import com.sap.sailing.domain.base.impl.CourseDataImpl; import com.sap.sailing.domain.base.impl.CourseDataImpl;
import com.sap.sailing.domain.common.CourseDesignerMode;
import com.sap.sailing.domain.common.RegattaIdentifier; import com.sap.sailing.domain.common.RegattaIdentifier;
import com.sap.sailing.domain.common.abstractlog.NotRevokableException; import com.sap.sailing.domain.common.abstractlog.NotRevokableException;
import com.sap.sailing.domain.common.racelog.tracking.CompetitorRegistrationOnRaceLogDisabledException; import com.sap.sailing.domain.common.racelog.tracking.CompetitorRegistrationOnRaceLogDisabledException;
@@ -194,7 +195,7 @@ public class RaceLogTrackingAdapterImpl implements RaceLogTrackingAdapter {
@Override @Override
public void copyCourse(RaceLog fromRaceLog, Set<RaceLog> toRaceLogs, SharedDomainFactory baseDomainFactory, public void copyCourse(RaceLog fromRaceLog, Set<RaceLog> toRaceLogs, SharedDomainFactory baseDomainFactory,
RacingEventService service) { RacingEventService service) {
CourseBase course = new LastPublishedCourseDesignFinder(fromRaceLog).analyze(); CourseBase course = new LastPublishedCourseDesignFinder(fromRaceLog, /* onlyCoursesWithValidWaypointList */ true).analyze();
final Set<Mark> marks = new HashSet<>(); final Set<Mark> marks = new HashSet<>();
if (course != null) { if (course != null) {
course.getWaypoints().forEach(wp -> Util.addAll(wp.getMarks(), marks)); course.getWaypoints().forEach(wp -> Util.addAll(wp.getMarks(), marks));
@@ -212,7 +213,7 @@ public class RaceLogTrackingAdapterImpl implements RaceLogTrackingAdapter {
int passId = toRaceLog.getCurrentPassId(); int passId = toRaceLog.getCurrentPassId();
RaceLogEvent newCourseEvent = new RaceLogCourseDesignChangedEventImpl(now, RaceLogEvent newCourseEvent = new RaceLogCourseDesignChangedEventImpl(now,
service.getServerAuthor(), passId, newCourse); service.getServerAuthor(), passId, newCourse, CourseDesignerMode.ADMIN_CONSOLE);
toRaceLog.add(newCourseEvent); toRaceLog.add(newCourseEvent);
} }
@@ -37,6 +37,7 @@ import com.sap.sailing.domain.base.configuration.RegattaConfiguration;
import com.sap.sailing.domain.base.configuration.impl.EmptyRegattaConfiguration; import com.sap.sailing.domain.base.configuration.impl.EmptyRegattaConfiguration;
import com.sap.sailing.domain.base.configuration.impl.RegattaConfigurationImpl; import com.sap.sailing.domain.base.configuration.impl.RegattaConfigurationImpl;
import com.sap.sailing.domain.base.configuration.procedures.ESSConfiguration; import com.sap.sailing.domain.base.configuration.procedures.ESSConfiguration;
import com.sap.sailing.domain.common.CourseDesignerMode;
import com.sap.sailing.domain.common.racelog.Flags; import com.sap.sailing.domain.common.racelog.Flags;
import com.sap.sailing.domain.common.racelog.RaceLogRaceStatus; import com.sap.sailing.domain.common.racelog.RaceLogRaceStatus;
import com.sap.sailing.domain.common.racelog.RacingProcedureType; import com.sap.sailing.domain.common.racelog.RacingProcedureType;
@@ -177,7 +178,7 @@ public class RaceStateTest {
state.forceNewStartTime(nowMock, new MillisecondsTimePoint(1)); state.forceNewStartTime(nowMock, new MillisecondsTimePoint(1));
Thread.sleep(100); Thread.sleep(100);
state.setFinishedTime(new MillisecondsTimePoint(10)); state.setFinishedTime(new MillisecondsTimePoint(10));
state.setCourseDesign(nowMock, mock(CourseBase.class)); state.setCourseDesign(nowMock, mock(CourseBase.class), CourseDesignerMode.ADMIN_CONSOLE);
state.setAdvancePass(mock(TimePoint.class)); state.setAdvancePass(mock(TimePoint.class));
@@ -1,9 +1,12 @@
package com.sap.sailing.domain.abstractlog.race; package com.sap.sailing.domain.abstractlog.race;
import com.sap.sailing.domain.base.CourseBase; import com.sap.sailing.domain.base.CourseBase;
import com.sap.sailing.domain.common.CourseDesignerMode;
public interface RaceLogCourseDesignChangedEvent extends RaceLogEvent { public interface RaceLogCourseDesignChangedEvent extends RaceLogEvent {
CourseBase getCourseDesign(); CourseBase getCourseDesign();
CourseDesignerMode getCourseDesignerMode();
} }
@@ -4,11 +4,33 @@ import com.sap.sailing.domain.abstractlog.race.RaceLog;
import com.sap.sailing.domain.abstractlog.race.RaceLogCourseDesignChangedEvent; import com.sap.sailing.domain.abstractlog.race.RaceLogCourseDesignChangedEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogEvent; import com.sap.sailing.domain.abstractlog.race.RaceLogEvent;
import com.sap.sailing.domain.base.CourseBase; import com.sap.sailing.domain.base.CourseBase;
import com.sap.sailing.domain.common.CourseDesignerMode;
/**
* A course design can be defined in the {@link RaceLog} using a {@link RaceLogCourseDesignChangedEvent}. These
* events may carry information about the course designer that was used to create the course. Not all of them
* support defining the waypoint sequence that sets the course. See {@link RaceLogCourseDesignChangedEvent#getCourseDesignerMode()}
* and {@link CourseDesignerMode#isWaypointSequenceValid()}. Callers can tell whether they want to obtain {@link CourseBase}
* objects regardless of whether their waypoint sequence is valid or not.
*
* @author Axel Uhl (d043530)
*
*/
public class LastPublishedCourseDesignFinder extends RaceLogAnalyzer<CourseBase> { public class LastPublishedCourseDesignFinder extends RaceLogAnalyzer<CourseBase> {
public LastPublishedCourseDesignFinder(RaceLog raceLog) { private final boolean onlyCoursesWithValidWaypointList;
/**
* @param onlyCoursesWithValidWaypointList
* if {@code true}, only {@link RaceLogCourseDesignChangedEvent} objects will be considered whose
* {@link RaceLogCourseDesignChangedEvent#getCourseDesignerMode() course designer mode}
* {@link CourseDesignerMode#isWaypointSequenceValid() guarantees} that the waypoint list is valid and
* shall be applied to a race; otherwise, all other {@link RaceLogCourseDesignChangedEvent} as considered
* as well.
*/
public LastPublishedCourseDesignFinder(RaceLog raceLog, boolean onlyCoursesWithValidWaypointList) {
super(raceLog); super(raceLog);
this.onlyCoursesWithValidWaypointList = onlyCoursesWithValidWaypointList;
} }
@Override @Override
@@ -16,7 +38,10 @@ public class LastPublishedCourseDesignFinder extends RaceLogAnalyzer<CourseBase>
for (RaceLogEvent event : getAllEventsDescending()) { for (RaceLogEvent event : getAllEventsDescending()) {
if (event instanceof RaceLogCourseDesignChangedEvent) { if (event instanceof RaceLogCourseDesignChangedEvent) {
RaceLogCourseDesignChangedEvent courseDesignEvent = (RaceLogCourseDesignChangedEvent) event; RaceLogCourseDesignChangedEvent courseDesignEvent = (RaceLogCourseDesignChangedEvent) event;
return courseDesignEvent.getCourseDesign(); if (!onlyCoursesWithValidWaypointList || (courseDesignEvent.getCourseDesignerMode() == null ||
courseDesignEvent.getCourseDesignerMode().isWaypointSequenceValid())) {
return courseDesignEvent.getCourseDesign();
}
} }
} }
return null; return null;
@@ -6,23 +6,27 @@ import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor;
import com.sap.sailing.domain.abstractlog.race.RaceLogCourseDesignChangedEvent; import com.sap.sailing.domain.abstractlog.race.RaceLogCourseDesignChangedEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogEventVisitor; import com.sap.sailing.domain.abstractlog.race.RaceLogEventVisitor;
import com.sap.sailing.domain.base.CourseBase; import com.sap.sailing.domain.base.CourseBase;
import com.sap.sailing.domain.common.CourseDesignerMode;
import com.sap.sse.common.TimePoint; import com.sap.sse.common.TimePoint;
public class RaceLogCourseDesignChangedEventImpl extends RaceLogEventImpl implements RaceLogCourseDesignChangedEvent { public class RaceLogCourseDesignChangedEventImpl extends RaceLogEventImpl implements RaceLogCourseDesignChangedEvent {
private static final long serialVersionUID = 1565936170747420547L; private static final long serialVersionUID = 1565936170747420547L;
private final CourseBase courseDesign; private final CourseBase courseDesign;
private final CourseDesignerMode courseDesignerMode;
public RaceLogCourseDesignChangedEventImpl(TimePoint createdAt, TimePoint pTimePoint, public RaceLogCourseDesignChangedEventImpl(TimePoint createdAt, TimePoint pTimePoint,
AbstractLogEventAuthor author, Serializable pId, int pPassId, AbstractLogEventAuthor author, Serializable pId, int pPassId,
CourseBase courseDesign) { CourseBase courseDesign, CourseDesignerMode courseDesignerMode) {
super(createdAt, pTimePoint, author, pId, pPassId); super(createdAt, pTimePoint, author, pId, pPassId);
this.courseDesign = courseDesign; this.courseDesign = courseDesign;
this.courseDesignerMode = courseDesignerMode;
} }
public RaceLogCourseDesignChangedEventImpl(TimePoint logicalTimePoint, AbstractLogEventAuthor author, int pPassId, public RaceLogCourseDesignChangedEventImpl(TimePoint logicalTimePoint, AbstractLogEventAuthor author, int pPassId,
CourseBase courseDesign) { CourseBase courseDesign, CourseDesignerMode courseDesignerMode) {
this(now(), logicalTimePoint, author, randId(), pPassId, courseDesign); this(now(), logicalTimePoint, author, randId(), pPassId, courseDesign, courseDesignerMode);
} }
@Override @Override
@@ -35,8 +39,14 @@ public class RaceLogCourseDesignChangedEventImpl extends RaceLogEventImpl implem
return courseDesign; return courseDesign;
} }
@Override
public CourseDesignerMode getCourseDesignerMode() {
return courseDesignerMode;
}
@Override @Override
public String getShortInfo() { public String getShortInfo() {
return "courseDesign=" + courseDesign != null ? courseDesign.getName() : "null"; return "courseDesign=" + courseDesign != null ? courseDesign.getName() : "null";
} }
}
}
@@ -27,7 +27,7 @@ public class WeakRaceLogChangedVisitor extends AbstractRaceLogChangedVisitor {
private static ReferenceQueue<? super RaceLogChangedListener> queue = new ReferenceQueue<RaceLogChangedListener>(); private static ReferenceQueue<? super RaceLogChangedListener> queue = new ReferenceQueue<RaceLogChangedListener>();
private static Map<Reference<?>, WeakRaceLogChangedVisitor> referenceToVisitor = new ConcurrentHashMap<Reference<?>, WeakRaceLogChangedVisitor>(); private static Map<Reference<?>, WeakRaceLogChangedVisitor> referenceToVisitor = new ConcurrentHashMap<Reference<?>, WeakRaceLogChangedVisitor>();
static { static {
new Thread(WeakRaceLogChangedVisitor.class.getSimpleName() + " weak reference cleaner") { Thread t = new Thread(WeakRaceLogChangedVisitor.class.getSimpleName() + " weak reference cleaner") {
@Override @Override
public void run() { public void run() {
while (true) { while (true) {
@@ -42,7 +42,9 @@ public class WeakRaceLogChangedVisitor extends AbstractRaceLogChangedVisitor {
} }
} }
} }
}.start(); };
t.setDaemon(true);
t.start();
} }
private final WeakReference<RaceLogChangedListener> listenerRef; private final WeakReference<RaceLogChangedListener> listenerRef;
@@ -13,6 +13,7 @@ import com.sap.sailing.domain.abstractlog.race.state.racingprocedure.RacingProce
import com.sap.sailing.domain.abstractlog.race.state.racingprocedure.RacingProcedurePrerequisite.Resolver; import com.sap.sailing.domain.abstractlog.race.state.racingprocedure.RacingProcedurePrerequisite.Resolver;
import com.sap.sailing.domain.abstractlog.race.state.racingprocedure.impl.RacingProcedurePrerequisiteAutoResolver; import com.sap.sailing.domain.abstractlog.race.state.racingprocedure.impl.RacingProcedurePrerequisiteAutoResolver;
import com.sap.sailing.domain.base.CourseBase; import com.sap.sailing.domain.base.CourseBase;
import com.sap.sailing.domain.common.CourseDesignerMode;
import com.sap.sailing.domain.common.Wind; import com.sap.sailing.domain.common.Wind;
import com.sap.sailing.domain.common.racelog.Flags; import com.sap.sailing.domain.common.racelog.Flags;
import com.sap.sailing.domain.common.racelog.RacingProcedureType; import com.sap.sailing.domain.common.racelog.RacingProcedureType;
@@ -172,8 +173,14 @@ public interface RaceState extends ReadonlyRaceState {
/** /**
* Sets a new active course design. * Sets a new active course design.
*
* @param courseDesignerMode
* the type of course designer through which the course was created; this decides about whether the
* waypoint specification will be considered at all. For example, the "By Marks" course designer does not
* produce a valid waypoints list which therefore must be ignored instead of using it to update a
* TrackedRace's course.
*/ */
void setCourseDesign(TimePoint now, CourseBase courseDesign); void setCourseDesign(TimePoint now, CourseBase courseDesign, CourseDesignerMode courseDesignerMode);
/** /**
* Enters a new wind fix for this race. * Enters a new wind fix for this race.
@@ -37,6 +37,7 @@ import com.sap.sailing.domain.base.CourseBase;
import com.sap.sailing.domain.base.configuration.ConfigurationLoader; import com.sap.sailing.domain.base.configuration.ConfigurationLoader;
import com.sap.sailing.domain.base.configuration.RegattaConfiguration; import com.sap.sailing.domain.base.configuration.RegattaConfiguration;
import com.sap.sailing.domain.base.configuration.impl.EmptyRegattaConfiguration; import com.sap.sailing.domain.base.configuration.impl.EmptyRegattaConfiguration;
import com.sap.sailing.domain.common.CourseDesignerMode;
import com.sap.sailing.domain.common.Wind; import com.sap.sailing.domain.common.Wind;
import com.sap.sailing.domain.common.abstractlog.NotRevokableException; import com.sap.sailing.domain.common.abstractlog.NotRevokableException;
import com.sap.sailing.domain.common.racelog.Flags; import com.sap.sailing.domain.common.racelog.Flags;
@@ -201,8 +202,8 @@ public class RaceStateImpl extends ReadonlyRaceStateImpl implements RaceState {
} }
@Override @Override
public void setCourseDesign(TimePoint timePoint, CourseBase courseDesign) { public void setCourseDesign(TimePoint timePoint, CourseBase courseDesign, CourseDesignerMode courseDesignerMode) {
raceLog.add(new RaceLogCourseDesignChangedEventImpl(timePoint, author, raceLog.getCurrentPassId(), courseDesign)); raceLog.add(new RaceLogCourseDesignChangedEventImpl(timePoint, author, raceLog.getCurrentPassId(), courseDesign, courseDesignerMode));
} }
@Override @Override
@@ -186,7 +186,7 @@ public class ReadonlyRaceStateImpl implements ReadonlyRaceState, RaceLogChangedL
this.protestTimeAnalyzer = new ProtestStartTimeFinder(raceLog); this.protestTimeAnalyzer = new ProtestStartTimeFinder(raceLog);
this.finishPositioningListAnalyzer = new FinishPositioningListFinder(raceLog); this.finishPositioningListAnalyzer = new FinishPositioningListFinder(raceLog);
this.confirmedFinishPositioningListAnalyzer = new ConfirmedFinishPositioningListFinder(raceLog); this.confirmedFinishPositioningListAnalyzer = new ConfirmedFinishPositioningListFinder(raceLog);
this.courseDesignerAnalyzer = new LastPublishedCourseDesignFinder(raceLog); this.courseDesignerAnalyzer = new LastPublishedCourseDesignFinder(raceLog, /* onlyCoursesWithValidWaypointList */ false);
this.lastWindFixAnalyzer = new LastWindFixFinder(raceLog); this.lastWindFixAnalyzer = new LastWindFixFinder(raceLog);
this.raceStateToObserveListener = new BaseRaceStateChangedListener() { this.raceStateToObserveListener = new BaseRaceStateChangedListener() {
@@ -339,7 +339,7 @@ public abstract class AbstractRaceColumn extends SimpleAbstractRaceColumn implem
} }
} else { } else {
// if no tracked race is found, use marks from race course if present in racelog // if no tracked race is found, use marks from race course if present in racelog
LastPublishedCourseDesignFinder courseDesginFinder = new LastPublishedCourseDesignFinder(getRaceLog(fleet)); LastPublishedCourseDesignFinder courseDesginFinder = new LastPublishedCourseDesignFinder(getRaceLog(fleet), /* onlyCoursesWithValidWaypointList */ true);
final CourseBase courseBase = courseDesginFinder.analyze(); final CourseBase courseBase = courseDesginFinder.analyze();
if (courseBase != null) { if (courseBase != null) {
courseBase.getWaypoints().forEach((waypoint) -> Util.addAll(waypoint.getMarks(), result)); courseBase.getWaypoints().forEach((waypoint) -> Util.addAll(waypoint.getMarks(), result));
@@ -221,6 +221,7 @@ public class LiveLeaderboardUpdater implements Runnable {
private synchronized void start() { private synchronized void start() {
running = true; running = true;
thread = new Thread(this, "LiveLeaderboardUpdater for leaderboard "+getLeaderboard().getName()); thread = new Thread(this, "LiveLeaderboardUpdater for leaderboard "+getLeaderboard().getName());
thread.setDaemon(true);
thread.start(); thread.start();
} }
@@ -75,7 +75,7 @@ public class DynamicTrackedRaceLogListener extends BaseRaceLogEventVisitor {
} }
private LastPublishedCourseDesignFinder createCourseDesignFinder(RaceLog raceLog) { private LastPublishedCourseDesignFinder createCourseDesignFinder(RaceLog raceLog) {
return new LastPublishedCourseDesignFinder(raceLog); return new LastPublishedCourseDesignFinder(raceLog, /* onlyCoursesWithValidWaypointList */ false); // we also want by-name courses to forward, e.g., to TracTrac
} }
private AbortingFlagFinder createAbortingFlagFinder(RaceLog raceLog) { private AbortingFlagFinder createAbortingFlagFinder(RaceLog raceLog) {
@@ -8,8 +8,10 @@ import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.AcceptsOneWidget; import com.google.gwt.user.client.ui.AcceptsOneWidget;
import com.google.gwt.user.client.ui.RootLayoutPanel; import com.google.gwt.user.client.ui.RootLayoutPanel;
import com.sap.sailing.gwt.ui.raceboard.RaceBoardEntryPoint;
import com.sap.sailing.gwt.ui.raceboard.RaceBoardViewConfiguration; import com.sap.sailing.gwt.ui.raceboard.RaceBoardViewConfiguration;
import com.sap.sailing.gwt.ui.shared.EventDTO; import com.sap.sailing.gwt.ui.shared.EventDTO;
import com.sap.sse.common.Duration;
import com.sap.sse.gwt.client.mvp.ErrorView; import com.sap.sse.gwt.client.mvp.ErrorView;
import com.sap.sse.gwt.client.useragent.UserAgentDetails; import com.sap.sse.gwt.client.useragent.UserAgentDetails;
import com.sap.sse.gwt.shared.GwtHttpRequestUtils; import com.sap.sse.gwt.shared.GwtHttpRequestUtils;
@@ -77,9 +79,11 @@ public class PlayerActivity extends AbstractActivity {
RaceBoardViewConfiguration.PARAM_VIEW_SHOW_COMPETITORSCHART, false /* default */); RaceBoardViewConfiguration.PARAM_VIEW_SHOW_COMPETITORSCHART, false /* default */);
String activeCompetitorsFilterSetName = GwtHttpRequestUtils.getStringParameter(RaceBoardViewConfiguration.PARAM_VIEW_COMPETITOR_FILTER, null /* default*/); String activeCompetitorsFilterSetName = GwtHttpRequestUtils.getStringParameter(RaceBoardViewConfiguration.PARAM_VIEW_COMPETITOR_FILTER, null /* default*/);
final String defaultMedia = GwtHttpRequestUtils.getStringParameter(RaceBoardViewConfiguration.PARAM_DEFAULT_MEDIA, null /* default */); final String defaultMedia = GwtHttpRequestUtils.getStringParameter(RaceBoardViewConfiguration.PARAM_DEFAULT_MEDIA, null /* default */);
final Duration initialDurationAfterRaceStartInReplay = RaceBoardEntryPoint.parseDuration(GwtHttpRequestUtils.getStringParameter(
RaceBoardViewConfiguration.PARAM_TIME_AFTER_RACE_START_AS_HOURS_COLON_MILLIS_COLON_SECONDS, null /* default */));
return new RaceBoardViewConfiguration(activeCompetitorsFilterSetName, showLeaderboard, return new RaceBoardViewConfiguration(activeCompetitorsFilterSetName, showLeaderboard,
showWindChart, showCompetitorsChart, showViewStreamlets, showViewStreamletColors, showViewSimulation, autoSelectMedia, defaultMedia); showWindChart, showCompetitorsChart, showViewStreamlets, showViewStreamletColors, showViewSimulation, autoSelectMedia, defaultMedia, initialDurationAfterRaceStartInReplay);
} }
private void createErrorView(String errorMessage, Throwable errorReason, AcceptsOneWidget panel) { private void createErrorView(String errorMessage, Throwable errorReason, AcceptsOneWidget panel) {
@@ -10,6 +10,7 @@ import com.sap.sailing.domain.common.security.SailingPermissionsForRoleProvider;
import com.sap.sailing.gwt.ui.common.client.DateAndTimeFormatterUtil; import com.sap.sailing.gwt.ui.common.client.DateAndTimeFormatterUtil;
import com.sap.sailing.gwt.ui.shared.MarkPassingTimesDTO; import com.sap.sailing.gwt.ui.shared.MarkPassingTimesDTO;
import com.sap.sailing.gwt.ui.shared.RaceTimesInfoDTO; import com.sap.sailing.gwt.ui.shared.RaceTimesInfoDTO;
import com.sap.sse.common.Duration;
import com.sap.sse.common.Util; import com.sap.sse.common.Util;
import com.sap.sse.gwt.client.player.TimeRangeWithZoomProvider; import com.sap.sse.gwt.client.player.TimeRangeWithZoomProvider;
import com.sap.sse.gwt.client.player.Timer; import com.sap.sse.gwt.client.player.Timer;
@@ -43,16 +44,18 @@ public class RaceTimePanel extends TimePanel<RaceTimePanelSettings> implements R
*/ */
private boolean redrawAllMarkersPendingForMinMaxBeingInitialized; private boolean redrawAllMarkersPendingForMinMaxBeingInitialized;
private final Duration initialTimeAfterRaceStartInReplayMode;
public RaceTimePanel(UserService userService, Timer timer, TimeRangeWithZoomProvider timeRangeProvider, StringMessages stringMessages, public RaceTimePanel(UserService userService, Timer timer, TimeRangeWithZoomProvider timeRangeProvider, StringMessages stringMessages,
RaceTimesInfoProvider raceTimesInfoProvider, boolean canReplayWhileLiveIsPossible, boolean isScreenLargeEnoughToOfferChartSupport, RaceTimesInfoProvider raceTimesInfoProvider, boolean canReplayWhileLiveIsPossible, boolean isScreenLargeEnoughToOfferChartSupport,
RegattaAndRaceIdentifier selectedRaceIdentifier) { RegattaAndRaceIdentifier selectedRaceIdentifier, Duration initialTimeAfterRaceStartInReplayMode) {
super(timer, timeRangeProvider, stringMessages, canReplayWhileLiveIsPossible, isScreenLargeEnoughToOfferChartSupport); super(timer, timeRangeProvider, stringMessages, canReplayWhileLiveIsPossible, isScreenLargeEnoughToOfferChartSupport);
this.userService = userService; this.userService = userService;
this.raceTimesInfoProvider = raceTimesInfoProvider; this.raceTimesInfoProvider = raceTimesInfoProvider;
selectedRace = null; selectedRace = null;
autoAdjustPlayMode = true; autoAdjustPlayMode = true;
selectedRace = selectedRaceIdentifier; selectedRace = selectedRaceIdentifier;
this.initialTimeAfterRaceStartInReplayMode = initialTimeAfterRaceStartInReplayMode;
if (!raceTimesInfoProvider.containsRaceIdentifier(selectedRace)) { if (!raceTimesInfoProvider.containsRaceIdentifier(selectedRace)) {
raceTimesInfoProvider.addRaceIdentifier(selectedRace, true); raceTimesInfoProvider.addRaceIdentifier(selectedRace, true);
} }
@@ -282,7 +285,8 @@ public class RaceTimePanel extends TimePanel<RaceTimePanelSettings> implements R
case Replay: case Replay:
// set time to start of race // set time to start of race
if (newRaceTimesInfo.startOfRace != null) { if (newRaceTimesInfo.startOfRace != null) {
timer.setTime(newRaceTimesInfo.startOfRace.getTime()); timer.setTime(newRaceTimesInfo.startOfRace.getTime() +
(initialTimeAfterRaceStartInReplayMode == null ? 0l : initialTimeAfterRaceStartInReplayMode.asMillis()));
} }
break; break;
} }
@@ -25,6 +25,8 @@ import com.sap.sailing.gwt.ui.client.RaceTimesInfoProvider;
import com.sap.sailing.gwt.ui.client.RemoteServiceMappingConstants; import com.sap.sailing.gwt.ui.client.RemoteServiceMappingConstants;
import com.sap.sailing.gwt.ui.shared.RaceWithCompetitorsDTO; import com.sap.sailing.gwt.ui.shared.RaceWithCompetitorsDTO;
import com.sap.sailing.gwt.ui.shared.RaceboardDataDTO; import com.sap.sailing.gwt.ui.shared.RaceboardDataDTO;
import com.sap.sse.common.Duration;
import com.sap.sse.common.impl.MillisecondsDurationImpl;
import com.sap.sse.gwt.client.EntryPointHelper; import com.sap.sse.gwt.client.EntryPointHelper;
import com.sap.sse.gwt.client.async.AsyncActionsExecutor; import com.sap.sse.gwt.client.async.AsyncActionsExecutor;
import com.sap.sse.gwt.client.player.Timer; import com.sap.sse.gwt.client.player.Timer;
@@ -88,8 +90,10 @@ public class RaceBoardEntryPoint extends AbstractSailingEntryPoint {
final boolean autoSelectMedia = GwtHttpRequestUtils.getBooleanParameter(RaceBoardViewConfiguration.PARAM_AUTOSELECT_MEDIA, true); final boolean autoSelectMedia = GwtHttpRequestUtils.getBooleanParameter(RaceBoardViewConfiguration.PARAM_AUTOSELECT_MEDIA, true);
final String defaultMedia = GwtHttpRequestUtils.getStringParameter(RaceBoardViewConfiguration.PARAM_DEFAULT_MEDIA, null /* default*/); final String defaultMedia = GwtHttpRequestUtils.getStringParameter(RaceBoardViewConfiguration.PARAM_DEFAULT_MEDIA, null /* default*/);
final boolean showMapControls = GwtHttpRequestUtils.getBooleanParameter(RaceBoardViewConfiguration.PARAM_VIEW_SHOW_MAPCONTROLS, true /* default*/); final boolean showMapControls = GwtHttpRequestUtils.getBooleanParameter(RaceBoardViewConfiguration.PARAM_VIEW_SHOW_MAPCONTROLS, true /* default*/);
final Duration initialDurationAfterRaceStartInReplay = parseDuration(GwtHttpRequestUtils.getStringParameter(RaceBoardViewConfiguration.PARAM_TIME_AFTER_RACE_START_AS_HOURS_COLON_MILLIS_COLON_SECONDS, null /* default*/));
raceboardViewConfig = new RaceBoardViewConfiguration(activeCompetitorsFilterSetName, showLeaderboard, raceboardViewConfig = new RaceBoardViewConfiguration(activeCompetitorsFilterSetName, showLeaderboard,
showWindChart, showCompetitorsChart, showViewStreamlets, showViewStreamletColors, showViewSimulation, autoSelectMedia, defaultMedia); showWindChart, showCompetitorsChart, showViewStreamlets, showViewStreamletColors, showViewSimulation, autoSelectMedia, defaultMedia,
initialDurationAfterRaceStartInReplay);
sailingService.getRaceboardData(regattaName, raceName, leaderboardName, leaderboardGroupName, eventId, new AsyncCallback<RaceboardDataDTO>() { sailingService.getRaceboardData(regattaName, raceName, leaderboardName, leaderboardGroupName, eventId, new AsyncCallback<RaceboardDataDTO>() {
@Override @Override
public void onSuccess(RaceboardDataDTO result) { public void onSuccess(RaceboardDataDTO result) {
@@ -107,6 +111,24 @@ public class RaceBoardEntryPoint extends AbstractSailingEntryPoint {
}); });
} }
/**
* Understands [hh:[mm:]]ss and parses into a {@link Duration}. If {@code durationAsString} is {@code null} then
* so is the result.
*/
public static Duration parseDuration(String durationAsString) {
final Duration result;
if (durationAsString == null) {
result = null;
} else {
long seconds = 0;
for (final String hhmmss : durationAsString.split(":")) {
seconds = 60*seconds + Long.valueOf(hhmmss);
}
result = new MillisecondsDurationImpl(1000l * seconds);
}
return result;
}
private void createErrorPage(String message) { private void createErrorPage(String message) {
final DockLayoutPanel vp = new DockLayoutPanel(Unit.PX); final DockLayoutPanel vp = new DockLayoutPanel(Unit.PX);
LogoAndTitlePanel logoAndTitlePanel = new LogoAndTitlePanel(getStringMessages(), this, getUserService()); LogoAndTitlePanel logoAndTitlePanel = new LogoAndTitlePanel(getStringMessages(), this, getUserService());
@@ -234,7 +234,7 @@ public class RaceBoardPanel extends SimplePanel implements LeaderboardUpdateList
} }
} }
timePanel = new RaceTimePanel(userService, timer, timeRangeWithZoomModel, stringMessages, raceTimesInfoProvider, timePanel = new RaceTimePanel(userService, timer, timeRangeWithZoomModel, stringMessages, raceTimesInfoProvider,
false, isScreenLargeEnoughToOfferChartSupport, selectedRaceIdentifier); false, isScreenLargeEnoughToOfferChartSupport, selectedRaceIdentifier, getConfiguration().getInitialDurationAfterRaceStartInReplay());
timeRangeWithZoomModel.addTimeZoomChangeListener(timePanel); timeRangeWithZoomModel.addTimeZoomChangeListener(timePanel);
raceTimesInfoProvider.addRaceTimesInfoProviderListener(timePanel); raceTimesInfoProvider.addRaceTimesInfoProviderListener(timePanel);
} }
@@ -1,5 +1,7 @@
package com.sap.sailing.gwt.ui.raceboard; package com.sap.sailing.gwt.ui.raceboard;
import com.sap.sse.common.Duration;
/** /**
* Represents the parameters for configuring the raceboard view * Represents the parameters for configuring the raceboard view
* @author Frank * @author Frank
@@ -15,6 +17,7 @@ public class RaceBoardViewConfiguration {
private final boolean showViewStreamlets; private final boolean showViewStreamlets;
private final boolean showViewStreamletColors; private final boolean showViewStreamletColors;
private final boolean showViewSimulation; private final boolean showViewSimulation;
private final Duration initialDurationAfterRaceStartInReplay;
public static final String PARAM_VIEW_MODE = "viewMode"; public static final String PARAM_VIEW_MODE = "viewMode";
public static final String PARAM_VIEW_SHOW_LEADERBOARD = "viewShowLeaderboard"; public static final String PARAM_VIEW_SHOW_LEADERBOARD = "viewShowLeaderboard";
@@ -29,16 +32,17 @@ public class RaceBoardViewConfiguration {
public static final String PARAM_CAN_REPLAY_DURING_LIVE_RACES = "canReplayDuringLiveRaces"; public static final String PARAM_CAN_REPLAY_DURING_LIVE_RACES = "canReplayDuringLiveRaces";
public static final String PARAM_AUTOSELECT_MEDIA = "autoSelectMedia"; public static final String PARAM_AUTOSELECT_MEDIA = "autoSelectMedia";
public static final String PARAM_DEFAULT_MEDIA = "defaultMedia"; public static final String PARAM_DEFAULT_MEDIA = "defaultMedia";
public static final String PARAM_TIME_AFTER_RACE_START_AS_HOURS_COLON_MILLIS_COLON_SECONDS = "t";
public RaceBoardViewConfiguration() { public RaceBoardViewConfiguration() {
this(/* activeCompetitorsFilterSetName */ null, /* showLeaderboard */ true, this(/* activeCompetitorsFilterSetName */ null, /* showLeaderboard */ true,
/* showWindChart */ false, /* showCompetitorsChart */ false, /* showViewStreamlets */ false, /* showViewStreamletColors */ false, /* showViewSimulation */ false, /* showWindChart */ false, /* showCompetitorsChart */ false, /* showViewStreamlets */ false, /* showViewStreamletColors */ false, /* showViewSimulation */ false,
/* autoSelectMedia */false, null); /* autoSelectMedia */false, null, /* initialDurationAfterRaceStartInReplay */ null);
} }
public RaceBoardViewConfiguration(String activeCompetitorsFilterSetName, boolean showLeaderboard, public RaceBoardViewConfiguration(String activeCompetitorsFilterSetName, boolean showLeaderboard,
boolean showWindChart, boolean showCompetitorsChart, boolean showViewStreamlets, boolean showViewStreamletColors, boolean showViewSimulation, boolean showWindChart, boolean showCompetitorsChart, boolean showViewStreamlets, boolean showViewStreamletColors, boolean showViewSimulation,
boolean autoSelectMedia, String defaultMedia) { boolean autoSelectMedia, String defaultMedia, Duration initialDurationAfterRaceStartInReplay) {
this.activeCompetitorsFilterSetName = activeCompetitorsFilterSetName; this.activeCompetitorsFilterSetName = activeCompetitorsFilterSetName;
this.showLeaderboard = showLeaderboard; this.showLeaderboard = showLeaderboard;
this.showWindChart = showWindChart; this.showWindChart = showWindChart;
@@ -48,6 +52,7 @@ public class RaceBoardViewConfiguration {
this.showViewSimulation = showViewSimulation; this.showViewSimulation = showViewSimulation;
this.autoSelectMedia = autoSelectMedia; this.autoSelectMedia = autoSelectMedia;
this.defaultMedia = defaultMedia; this.defaultMedia = defaultMedia;
this.initialDurationAfterRaceStartInReplay = initialDurationAfterRaceStartInReplay;
} }
public boolean isShowLeaderboard() { public boolean isShowLeaderboard() {
@@ -86,4 +91,7 @@ public class RaceBoardViewConfiguration {
return defaultMedia; return defaultMedia;
} }
public Duration getInitialDurationAfterRaceStartInReplay() {
return initialDurationAfterRaceStartInReplay;
}
} }
@@ -144,6 +144,7 @@ import com.sap.sailing.domain.base.impl.SailingServerConfigurationImpl;
import com.sap.sailing.domain.base.impl.SeriesImpl; import com.sap.sailing.domain.base.impl.SeriesImpl;
import com.sap.sailing.domain.base.impl.TeamImpl; import com.sap.sailing.domain.base.impl.TeamImpl;
import com.sap.sailing.domain.common.Bearing; import com.sap.sailing.domain.common.Bearing;
import com.sap.sailing.domain.common.CourseDesignerMode;
import com.sap.sailing.domain.common.DataImportProgress; import com.sap.sailing.domain.common.DataImportProgress;
import com.sap.sailing.domain.common.DetailType; import com.sap.sailing.domain.common.DetailType;
import com.sap.sailing.domain.common.Distance; import com.sap.sailing.domain.common.Distance;
@@ -5229,10 +5230,10 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
List<com.sap.sse.common.Util.Pair<ControlPointDTO, PassingInstruction>> courseDTO) throws NotFoundException { List<com.sap.sse.common.Util.Pair<ControlPointDTO, PassingInstruction>> courseDTO) throws NotFoundException {
RaceLog raceLog = getRaceLog(leaderboardName, raceColumnName, fleetName); RaceLog raceLog = getRaceLog(leaderboardName, raceColumnName, fleetName);
String courseName = "Course of " + raceColumnName; String courseName = "Course of " + raceColumnName;
if(!LeaderboardNameConstants.DEFAULT_FLEET_NAME.equals(fleetName)) { if (!LeaderboardNameConstants.DEFAULT_FLEET_NAME.equals(fleetName)) {
courseName += "- " + fleetName; courseName += " - " + fleetName;
} }
CourseBase lastPublishedCourse = new LastPublishedCourseDesignFinder(raceLog).analyze(); CourseBase lastPublishedCourse = new LastPublishedCourseDesignFinder(raceLog, /* onlyCoursesWithValidWaypointList */ false).analyze();
if (lastPublishedCourse == null) { if (lastPublishedCourse == null) {
lastPublishedCourse = new CourseDataImpl(courseName); lastPublishedCourse = new CourseDataImpl(courseName);
} }
@@ -5250,7 +5251,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
} }
RaceLogEvent event = new RaceLogCourseDesignChangedEventImpl(MillisecondsTimePoint.now(), RaceLogEvent event = new RaceLogCourseDesignChangedEventImpl(MillisecondsTimePoint.now(),
getService().getServerAuthor(), raceLog.getCurrentPassId(), course); getService().getServerAuthor(), raceLog.getCurrentPassId(), course, CourseDesignerMode.ADMIN_CONSOLE);
raceLog.add(event); raceLog.add(event);
} }
@@ -5299,7 +5300,8 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
@Override @Override
public RaceCourseDTO getLastCourseDefinitionInRaceLog(final String leaderboardName, String raceColumnName, String fleetName) throws NotFoundException { public RaceCourseDTO getLastCourseDefinitionInRaceLog(final String leaderboardName, String raceColumnName, String fleetName) throws NotFoundException {
final RaceLog raceLog = getRaceLog(leaderboardName, raceColumnName, fleetName); final RaceLog raceLog = getRaceLog(leaderboardName, raceColumnName, fleetName);
CourseBase lastPublishedCourse = new LastPublishedCourseDesignFinder(raceLog).analyze(); // only look for course definitions that really define waypoints; ignore by-name course updates
CourseBase lastPublishedCourse = new LastPublishedCourseDesignFinder(raceLog, /* onlyCoursesWithValidWaypointList */ true).analyze();
if (lastPublishedCourse == null) { if (lastPublishedCourse == null) {
lastPublishedCourse = new CourseDataImpl(""); lastPublishedCourse = new CourseDataImpl("");
} }
@@ -6056,7 +6058,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
for (Fleet fleet : raceColumn.getFleets()) { for (Fleet fleet : raceColumn.getFleets()) {
RaceLog raceLog = raceColumn.getRaceLog(fleet); RaceLog raceLog = raceColumn.getRaceLog(fleet);
if (raceLog != raceLogToIgnore) { if (raceLog != raceLogToIgnore) {
LastPublishedCourseDesignFinder finder = new LastPublishedCourseDesignFinder(raceLog); LastPublishedCourseDesignFinder finder = new LastPublishedCourseDesignFinder(raceLog, /* onlyCoursesWithValidWaypointList */ true);
CourseBase course = finder.analyze(); CourseBase course = finder.analyze();
if (course != null) { if (course != null) {
for (Waypoint waypoint : course.getWaypoints()) { for (Waypoint waypoint : course.getWaypoints()) {
@@ -22,6 +22,16 @@
</li> </li>
</ul> </ul>
<h5 class="articleSubheadline">April 2016</h2>
<ul class="bulletList">
<li>The <tt>/gwt/RaceBoard.html</tt> entry point now accepts an optional parameter "t" in the form
<tt>[[hh:]mm:]ss</tt>, for example <tt>t=12:34</tt> to place the timer so much after the race start
if the viewer comes up in Replay mode. You may also use just seconds, as in <tt>t=1234</tt> which
makes specifying a time <em>before</em> the start somewhat easier (<tt>t=-123</tt> instead of
<tt>t=-02:-03</tt>).
</li>
</ul>
<h5 class="articleSubheadline">January 2016</h2> <h5 class="articleSubheadline">January 2016</h2>
<ul class="bulletList"> <ul class="bulletList">
<li>Show maneuver markers on the race board by default <li>Show maneuver markers on the race board by default
@@ -62,6 +62,7 @@ import com.sap.sailing.domain.base.CourseBase;
import com.sap.sailing.domain.base.DomainFactory; import com.sap.sailing.domain.base.DomainFactory;
import com.sap.sailing.domain.base.Fleet; import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.RaceColumn; import com.sap.sailing.domain.base.RaceColumn;
import com.sap.sailing.domain.common.CourseDesignerMode;
import com.sap.sailing.domain.common.MaxPointsReason; import com.sap.sailing.domain.common.MaxPointsReason;
import com.sap.sailing.domain.common.Wind; import com.sap.sailing.domain.common.Wind;
import com.sap.sailing.domain.common.racelog.Flags; import com.sap.sailing.domain.common.racelog.Flags;
@@ -514,7 +515,7 @@ public class TestStoringAndRetrievingRaceLogInLeaderboards extends RaceLogMongoD
@Test @Test
public void testStoreAndRetrieveSimpleLeaderboardWithRaceLogCourseDesignChangedEvent() { public void testStoreAndRetrieveSimpleLeaderboardWithRaceLogCourseDesignChangedEvent() {
CourseBase course = createCourseBase(); CourseBase course = createCourseBase();
RaceLogCourseDesignChangedEvent event = new RaceLogCourseDesignChangedEventImpl(now, author, 0, course); RaceLogCourseDesignChangedEvent event = new RaceLogCourseDesignChangedEventImpl(now, author, 0, course, CourseDesignerMode.ADMIN_CONSOLE);
addAndStoreRaceLogEvent(leaderboard, raceColumnName, event); addAndStoreRaceLogEvent(leaderboard, raceColumnName, event);
@@ -53,6 +53,7 @@ import com.sap.sailing.domain.base.RaceColumn;
import com.sap.sailing.domain.base.Regatta; import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.base.Series; import com.sap.sailing.domain.base.Series;
import com.sap.sailing.domain.base.impl.FleetImpl; import com.sap.sailing.domain.base.impl.FleetImpl;
import com.sap.sailing.domain.common.CourseDesignerMode;
import com.sap.sailing.domain.common.MaxPointsReason; import com.sap.sailing.domain.common.MaxPointsReason;
import com.sap.sailing.domain.common.Wind; import com.sap.sailing.domain.common.Wind;
import com.sap.sailing.domain.common.racelog.Flags; import com.sap.sailing.domain.common.racelog.Flags;
@@ -387,7 +388,7 @@ public class TestStoringAndRetrievingRaceLogInRegatta extends AbstractTestStorin
@Test @Test
public void testStoreAndRetrieveRegattaWithRaceLogCourseDesignChangedEvent() { public void testStoreAndRetrieveRegattaWithRaceLogCourseDesignChangedEvent() {
CourseBase course = createCourseBase(); CourseBase course = createCourseBase();
RaceLogCourseDesignChangedEvent event = new RaceLogCourseDesignChangedEventImpl(now, author, 0, course); RaceLogCourseDesignChangedEvent event = new RaceLogCourseDesignChangedEventImpl(now, author, 0, course, CourseDesignerMode.ADMIN_CONSOLE);
addAndStoreRaceLogEvent(regatta, raceColumnName, event); addAndStoreRaceLogEvent(regatta, raceColumnName, event);
@@ -10,6 +10,7 @@ import com.sap.sailing.domain.abstractlog.race.RaceLogEvent;
import com.sap.sailing.domain.abstractlog.race.impl.RaceLogCourseDesignChangedEventImpl; import com.sap.sailing.domain.abstractlog.race.impl.RaceLogCourseDesignChangedEventImpl;
import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.CourseBase; import com.sap.sailing.domain.base.CourseBase;
import com.sap.sailing.domain.common.CourseDesignerMode;
import com.sap.sailing.server.gateway.deserialization.JsonDeserializationException; import com.sap.sailing.server.gateway.deserialization.JsonDeserializationException;
import com.sap.sailing.server.gateway.deserialization.JsonDeserializer; import com.sap.sailing.server.gateway.deserialization.JsonDeserializer;
import com.sap.sailing.server.gateway.deserialization.coursedata.impl.CourseBaseDeserializer; import com.sap.sailing.server.gateway.deserialization.coursedata.impl.CourseBaseDeserializer;
@@ -31,8 +32,9 @@ public class RaceLogCourseDesignChangedEventDeserializer extends BaseRaceLogEven
JSONObject jsonCourseDesign = (JSONObject) object.get(RaceLogCourseDesignChangedEventSerializer.FIELD_COURSE_DESIGN); JSONObject jsonCourseDesign = (JSONObject) object.get(RaceLogCourseDesignChangedEventSerializer.FIELD_COURSE_DESIGN);
CourseBase courseData = courseDataDeserializer.deserialize(jsonCourseDesign); CourseBase courseData = courseDataDeserializer.deserialize(jsonCourseDesign);
final String courseDesignerModeName = (String) object.get(RaceLogCourseDesignChangedEventSerializer.FIELD_COURSE_DESIGNER_MODE);
return new RaceLogCourseDesignChangedEventImpl(createdAt, timePoint, author, id, passId, courseData); return new RaceLogCourseDesignChangedEventImpl(createdAt, timePoint, author, id, passId, courseData,
courseDesignerModeName == null ? null : CourseDesignerMode.valueOf(courseDesignerModeName));
} }
} }
@@ -12,6 +12,7 @@ public class RaceLogCourseDesignChangedEventSerializer extends BaseRaceLogEventS
public static final String VALUE_CLASS = RaceLogCourseDesignChangedEvent.class.getSimpleName(); public static final String VALUE_CLASS = RaceLogCourseDesignChangedEvent.class.getSimpleName();
public static final String FIELD_COURSE_DESIGN = "courseDesign"; public static final String FIELD_COURSE_DESIGN = "courseDesign";
public static final String FIELD_COURSE_DESIGNER_MODE = "courseDesignerMode";
private final JsonSerializer<CourseBase> courseBaseSerializer; private final JsonSerializer<CourseBase> courseBaseSerializer;
@@ -30,10 +31,9 @@ public class RaceLogCourseDesignChangedEventSerializer extends BaseRaceLogEventS
@Override @Override
public JSONObject serialize(RaceLogEvent object) { public JSONObject serialize(RaceLogEvent object) {
RaceLogCourseDesignChangedEvent courseChangedEvent = (RaceLogCourseDesignChangedEvent) object; RaceLogCourseDesignChangedEvent courseChangedEvent = (RaceLogCourseDesignChangedEvent) object;
JSONObject result = super.serialize(courseChangedEvent); JSONObject result = super.serialize(courseChangedEvent);
result.put(FIELD_COURSE_DESIGN, courseBaseSerializer.serialize(courseChangedEvent.getCourseDesign())); result.put(FIELD_COURSE_DESIGN, courseBaseSerializer.serialize(courseChangedEvent.getCourseDesign()));
result.put(FIELD_COURSE_DESIGNER_MODE, courseChangedEvent.getCourseDesignerMode() == null ? null : courseChangedEvent.getCourseDesignerMode().name());
return result; return result;
} }
@@ -23,6 +23,7 @@ import com.sap.sailing.domain.base.impl.ControlPointWithTwoMarksImpl;
import com.sap.sailing.domain.base.impl.CourseDataImpl; import com.sap.sailing.domain.base.impl.CourseDataImpl;
import com.sap.sailing.domain.base.impl.MarkImpl; import com.sap.sailing.domain.base.impl.MarkImpl;
import com.sap.sailing.domain.base.impl.WaypointImpl; import com.sap.sailing.domain.base.impl.WaypointImpl;
import com.sap.sailing.domain.common.CourseDesignerMode;
import com.sap.sailing.domain.common.MarkType; import com.sap.sailing.domain.common.MarkType;
import com.sap.sailing.domain.common.PassingInstruction; import com.sap.sailing.domain.common.PassingInstruction;
import com.sap.sailing.server.gateway.deserialization.JsonDeserializationException; import com.sap.sailing.server.gateway.deserialization.JsonDeserializationException;
@@ -63,7 +64,7 @@ public class RaceLogCourseDesignChangedEventSerializerTest {
factory), new GateDeserializer(factory, new MarkDeserializer(factory)))))); factory), new GateDeserializer(factory, new MarkDeserializer(factory))))));
now = MillisecondsTimePoint.now(); now = MillisecondsTimePoint.now();
event = new RaceLogCourseDesignChangedEventImpl(now, author, 0, createCourseData()); event = new RaceLogCourseDesignChangedEventImpl(now, author, 0, createCourseData(), CourseDesignerMode.BY_MARKS);
} }
@Test @Test
@@ -75,6 +76,7 @@ public class RaceLogCourseDesignChangedEventSerializerTest {
assertEquals(event.getId(), deserializedEvent.getId()); assertEquals(event.getId(), deserializedEvent.getId());
assertEquals(event.getPassId(), deserializedEvent.getPassId()); assertEquals(event.getPassId(), deserializedEvent.getPassId());
assertEquals(event.getLogicalTimePoint(), deserializedEvent.getLogicalTimePoint()); assertEquals(event.getLogicalTimePoint(), deserializedEvent.getLogicalTimePoint());
assertEquals(event.getCourseDesignerMode(), deserializedEvent.getCourseDesignerMode());
assertEquals(0, Util.size(event.getInvolvedBoats())); assertEquals(0, Util.size(event.getInvolvedBoats()));
assertEquals(0, Util.size(deserializedEvent.getInvolvedBoats())); assertEquals(0, Util.size(deserializedEvent.getInvolvedBoats()));
@@ -29,6 +29,7 @@ import com.sap.sailing.domain.abstractlog.race.tracking.impl.RaceLogDenoteForTra
import com.sap.sailing.domain.abstractlog.race.tracking.impl.RaceLogRegisterCompetitorEventImpl; import com.sap.sailing.domain.abstractlog.race.tracking.impl.RaceLogRegisterCompetitorEventImpl;
import com.sap.sailing.domain.abstractlog.race.tracking.impl.RaceLogStartTrackingEventImpl; import com.sap.sailing.domain.abstractlog.race.tracking.impl.RaceLogStartTrackingEventImpl;
import com.sap.sailing.domain.base.DomainFactory; import com.sap.sailing.domain.base.DomainFactory;
import com.sap.sailing.domain.common.CourseDesignerMode;
import com.sap.sailing.domain.common.racelog.RaceLogRaceStatus; import com.sap.sailing.domain.common.racelog.RaceLogRaceStatus;
import com.sap.sailing.domain.common.racelog.RacingProcedureType; import com.sap.sailing.domain.common.racelog.RacingProcedureType;
import com.sap.sailing.server.gateway.serialization.JsonSerializer; import com.sap.sailing.server.gateway.serialization.JsonSerializer;
@@ -149,7 +150,7 @@ public class RaceLogEventSerializerTest {
@Test @Test
public void testChoosesCourseDesignChangedSerializer() { public void testChoosesCourseDesignChangedSerializer() {
// we use the real event type here because we do not want to re-implement the dispatching. // we use the real event type here because we do not want to re-implement the dispatching.
RaceLogEvent event = new RaceLogCourseDesignChangedEventImpl(null, author, 0, null); RaceLogEvent event = new RaceLogCourseDesignChangedEventImpl(null, author, 0, null, CourseDesignerMode.ADMIN_CONSOLE);
serializer.serialize(event); serializer.serialize(event);
verify(courseDesignChangedEventSerializer).serialize(event); verify(courseDesignChangedEventSerializer).serialize(event);
} }
@@ -80,7 +80,7 @@ public class CompetitorsResource extends AbstractSailingServerResource {
.type(MediaType.TEXT_PLAIN).build(); .type(MediaType.TEXT_PLAIN).build();
} else { } else {
String jsonString = getCompetitorJSON(competitor).toJSONString(); String jsonString = getCompetitorJSON(competitor).toJSONString();
response = Response.ok(jsonString, MediaType.APPLICATION_JSON).build(); response = Response.ok(jsonString).header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build();
} }
return response; return response;
} }
@@ -110,7 +110,7 @@ public class CompetitorsResource extends AbstractSailingServerResource {
JSONObject teamJson = teamJsonSerializer.serialize(team); JSONObject teamJson = teamJsonSerializer.serialize(team);
String json = teamJson.toJSONString(); String json = teamJson.toJSONString();
return Response.ok(json, MediaType.APPLICATION_JSON).build(); return Response.ok(json).header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build();
} }
/** /**
@@ -130,7 +130,7 @@ public class DataMiningResource extends AbstractSailingServerResource {
} }
String json = predefinedQueryNames.toJSONString(); String json = predefinedQueryNames.toJSONString();
return Response.ok(json, MediaType.APPLICATION_JSON).build(); return Response.ok(json).header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build();
} }
public Response avgSpeedPerCompetitorAndLegType(String regattaName) { public Response avgSpeedPerCompetitorAndLegType(String regattaName) {
@@ -306,7 +306,7 @@ public class DataMiningResource extends AbstractSailingServerResource {
} }
jsonResult.put("resultUnit", resultUnit != null && !resultUnit.isEmpty() ? resultUnit : "None"); jsonResult.put("resultUnit", resultUnit != null && !resultUnit.isEmpty() ? resultUnit : "None");
jsonResult.put("results", resultValuesToJSON(result, numberExtractor, resultPlaces)); jsonResult.put("results", resultValuesToJSON(result, numberExtractor, resultPlaces));
response = Response.ok(jsonResult.toJSONString(), MediaType.APPLICATION_JSON).build(); response = Response.ok(jsonResult.toJSONString()).header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build();
} catch (NotJsonSerializableException e) { } catch (NotJsonSerializableException e) {
response = getNotSerializableErrorResponse(e.getNotSerializableClass()); response = getNotSerializableErrorResponse(e.getNotSerializableClass());
} }
@@ -46,7 +46,7 @@ public class EventsResource extends AbstractSailingServerResource {
} }
} }
String json = result.toJSONString(); String json = result.toJSONString();
return Response.ok(json, MediaType.APPLICATION_JSON).build(); return Response.ok(json).header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build();
} }
@GET @GET
@@ -71,7 +71,7 @@ public class EventsResource extends AbstractSailingServerResource {
JSONObject eventJson = eventSerializer.serialize(event); JSONObject eventJson = eventSerializer.serialize(event);
String json = eventJson.toJSONString(); String json = eventJson.toJSONString();
response = Response.ok(json, MediaType.APPLICATION_JSON).build(); response = Response.ok(json).header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build();
} }
return response; return response;
} }
@@ -98,7 +98,7 @@ public class EventsResource extends AbstractSailingServerResource {
filterByLeaderboard, filterByDayOffset, getService()); filterByLeaderboard, filterByDayOffset, getService());
JSONObject raceStatesJson = eventRaceStatesSerializer.serialize(new Pair<Event, Iterable<Leaderboard>>(event, getService().getLeaderboards().values())); JSONObject raceStatesJson = eventRaceStatesSerializer.serialize(new Pair<Event, Iterable<Leaderboard>>(event, getService().getLeaderboards().values()));
String json = raceStatesJson.toJSONString(); String json = raceStatesJson.toJSONString();
response = Response.ok(json, MediaType.APPLICATION_JSON).build(); response = Response.ok(json).header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build();
} }
return response; return response;
} }
@@ -13,6 +13,7 @@ import javax.ws.rs.POST;
import javax.ws.rs.Path; import javax.ws.rs.Path;
import javax.ws.rs.Produces; import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam; import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder; import javax.ws.rs.core.Response.ResponseBuilder;
import javax.ws.rs.core.Response.Status; import javax.ws.rs.core.Response.Status;
@@ -65,7 +66,7 @@ public class FileStorageResource extends AbstractSailingServerResource {
getService().getFileStorageManagementService().getActiveFileStorageService() getService().getFileStorageManagementService().getActiveFileStorageService()
.removeFile(new URI(uri)); .removeFile(new URI(uri));
result.put("status", Status.OK.name()); result.put("status", Status.OK.name());
response = Response.ok().entity(result.toJSONString()).build(); response = Response.ok().entity(result.toJSONString()).header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build();
} catch (NoCorrespondingServiceRegisteredException | OperationFailedException | InvalidPropertiesException } catch (NoCorrespondingServiceRegisteredException | OperationFailedException | InvalidPropertiesException
| URISyntaxException | IOException e) { | URISyntaxException | IOException e) {
final String errorMessage = "Could not delete file with URI "+uri+": "+e.getMessage(); final String errorMessage = "Could not delete file with URI "+uri+": "+e.getMessage();
@@ -52,7 +52,7 @@ public class LeaderboardGroupsResource extends AbstractSailingServerResource {
// header option is set to allow communication between two sapsailing servers, especially for // header option is set to allow communication between two sapsailing servers, especially for
// the master data import functionality // the master data import functionality
return Response.ok(json, MediaType.APPLICATION_JSON).header("Access-Control-Allow-Origin", "*").build(); return Response.ok(json).header("Access-Control-Allow-Origin", "*").header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build();
} }
@GET @GET
@@ -154,7 +154,7 @@ public class LeaderboardGroupsResource extends AbstractSailingServerResource {
} }
} }
String json = jsonLeaderboardGroup.toJSONString(); String json = jsonLeaderboardGroup.toJSONString();
response = Response.ok(json, MediaType.APPLICATION_JSON).build(); response = Response.ok(json).header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build();
} }
return response; return response;
} }
@@ -104,7 +104,7 @@ public class LeaderboardsResource extends AbstractSailingServerResource {
jsonLeaderboards.add(leaderboardName); jsonLeaderboards.add(leaderboardName);
} }
String json = jsonLeaderboards.toJSONString(); String json = jsonLeaderboards.toJSONString();
return Response.ok(json, MediaType.APPLICATION_JSON).build(); return Response.ok(json).header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build();
} }
@GET @GET
@@ -138,7 +138,7 @@ public class LeaderboardsResource extends AbstractSailingServerResource {
jsonLeaderboard.writeJSONString(sw); jsonLeaderboard.writeJSONString(sw);
String json = sw.getBuffer().toString(); String json = sw.getBuffer().toString();
response = Response.ok(json, MediaType.APPLICATION_JSON).build(); response = Response.ok(json).header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build();
} catch (NoWindException | InterruptedException | ExecutionException | IOException e) { } catch (NoWindException | InterruptedException | ExecutionException | IOException e) {
response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()) response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage())
.type(MediaType.TEXT_PLAIN).build(); .type(MediaType.TEXT_PLAIN).build();
@@ -509,7 +509,7 @@ public class LeaderboardsResource extends AbstractSailingServerResource {
} else { } else {
JSONObject json = CompetitorsResource.getCompetitorJSON(competitor); JSONObject json = CompetitorsResource.getCompetitorJSON(competitor);
json.put("displayName", leaderboard.getDisplayName(competitor)); json.put("displayName", leaderboard.getDisplayName(competitor));
response = Response.ok(json.toJSONString(), MediaType.APPLICATION_JSON).build(); response = Response.ok(json.toJSONString()).header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build();
} }
return response; return response;
} }
@@ -580,7 +580,7 @@ public class LeaderboardsResource extends AbstractSailingServerResource {
} }
JSONObject result = new JSONObject(); JSONObject result = new JSONObject();
result.put("marks", array); result.put("marks", array);
return Response.ok(result.toJSONString(), MediaType.APPLICATION_JSON).build(); return Response.ok(result.toJSONString()).header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build();
} }
private final MarkJsonSerializer markSerializer = new MarkJsonSerializer(); private final MarkJsonSerializer markSerializer = new MarkJsonSerializer();
@@ -671,7 +671,7 @@ public class LeaderboardsResource extends AbstractSailingServerResource {
.type(MediaType.TEXT_PLAIN).build(); .type(MediaType.TEXT_PLAIN).build();
} else { } else {
final JSONObject json = getJsonForColumnFactors(leaderboard, raceColumns); final JSONObject json = getJsonForColumnFactors(leaderboard, raceColumns);
response = Response.ok(json.toJSONString(), MediaType.APPLICATION_JSON).build(); response = Response.ok(json.toJSONString()).header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build();
} }
} }
return response; return response;
@@ -725,7 +725,7 @@ public class LeaderboardsResource extends AbstractSailingServerResource {
} else { } else {
raceColumn.setFactor(explicitFactor); raceColumn.setFactor(explicitFactor);
final JSONObject json = getJsonForColumnFactors(leaderboard, Collections.singleton(raceColumn)); final JSONObject json = getJsonForColumnFactors(leaderboard, Collections.singleton(raceColumn));
response = Response.ok(json.toJSONString(), MediaType.APPLICATION_JSON).build(); response = Response.ok(json.toJSONString()).header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build();
} }
} }
return response; return response;
@@ -3,6 +3,7 @@ package com.sap.sailing.server.gateway.jaxrs.api;
import javax.ws.rs.GET; import javax.ws.rs.GET;
import javax.ws.rs.Path; import javax.ws.rs.Path;
import javax.ws.rs.Produces; import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import com.sap.sailing.server.gateway.jaxrs.AbstractSailingServerResource; import com.sap.sailing.server.gateway.jaxrs.AbstractSailingServerResource;
@@ -12,6 +13,6 @@ public class PingResource extends AbstractSailingServerResource {
@GET @GET
@Produces("application/json;charset=UTF-8") @Produces("application/json;charset=UTF-8")
public Response ping() { public Response ping() {
return Response.ok().build(); return Response.ok().header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build();
} }
} }
@@ -170,7 +170,7 @@ public class PolarResource extends AbstractSailingServerResource {
} finally { } finally {
maneuverBasedWindEstimationTrackImpl.unlockAfterRead(); maneuverBasedWindEstimationTrackImpl.unlockAfterRead();
} }
response = Response.ok(resultAsJson.toJSONString(), MediaType.APPLICATION_JSON).build(); response = Response.ok(resultAsJson.toJSONString()).header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build();
} }
} }
return response; return response;
@@ -120,7 +120,7 @@ public class RegattasResource extends AbstractSailingServerResource {
regattasJson.add(regattaJsonSerializer.serialize(regatta)); regattasJson.add(regattaJsonSerializer.serialize(regatta));
} }
String json = regattasJson.toJSONString(); String json = regattasJson.toJSONString();
return Response.ok(json, MediaType.APPLICATION_JSON).build(); return Response.ok(json).header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build();
} }
@GET @GET
@@ -138,7 +138,7 @@ public class RegattasResource extends AbstractSailingServerResource {
JSONObject serializedRegatta = regattaSerializer.serialize(regatta); JSONObject serializedRegatta = regattaSerializer.serialize(regatta);
String json = serializedRegatta.toJSONString(); String json = serializedRegatta.toJSONString();
response = Response.ok(json, MediaType.APPLICATION_JSON).build(); response = Response.ok(json).header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build();
} }
return response; return response;
} }
@@ -167,7 +167,7 @@ public class RegattasResource extends AbstractSailingServerResource {
JSONObject serializedRegatta = regattaSerializer.serialize(regatta); JSONObject serializedRegatta = regattaSerializer.serialize(regatta);
String json = serializedRegatta.toJSONString(); String json = serializedRegatta.toJSONString();
response = Response.ok(json, MediaType.APPLICATION_JSON).build(); response = Response.ok(json).header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build();
} }
return response; return response;
} }
@@ -283,7 +283,7 @@ public class RegattasResource extends AbstractSailingServerResource {
jsonRace.put("competitors", jsonCompetitors); jsonRace.put("competitors", jsonCompetitors);
String json = jsonRace.toJSONString(); String json = jsonRace.toJSONString();
response = Response.ok(json, MediaType.APPLICATION_JSON).build(); response = Response.ok(json).header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build();
} }
} }
return response; return response;
@@ -398,7 +398,7 @@ public class RegattasResource extends AbstractSailingServerResource {
String json = jsonRace.toJSONString(); String json = jsonRace.toJSONString();
return Response.ok(json, MediaType.APPLICATION_JSON).build(); return Response.ok(json).header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build();
} }
private JSONObject addFixToJsonFixes(JSONArray jsonFixes, GPSFix fix) { private JSONObject addFixToJsonFixes(JSONArray jsonFixes, GPSFix fix) {
@@ -444,7 +444,7 @@ public class RegattasResource extends AbstractSailingServerResource {
JSONObject jsonCourse = serializer.serialize(course); JSONObject jsonCourse = serializer.serialize(course);
String json = jsonCourse.toJSONString(); String json = jsonCourse.toJSONString();
response = Response.ok(json, MediaType.APPLICATION_JSON).build(); response = Response.ok(json).header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build();
} }
} }
return response; return response;
@@ -485,7 +485,7 @@ public class RegattasResource extends AbstractSailingServerResource {
JSONObject jsonCourse = serializer.serialize(targetTime); JSONObject jsonCourse = serializer.serialize(targetTime);
String json = jsonCourse.toJSONString(); String json = jsonCourse.toJSONString();
response = Response.ok(json, MediaType.APPLICATION_JSON).build(); response = Response.ok(json).header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build();
} catch (NotEnoughDataHasBeenAddedException | NoWindException e) { } catch (NotEnoughDataHasBeenAddedException | NoWindException e) {
response = getNotEnoughDataAvailabeErrorResponse(regattaName, raceName); response = getNotEnoughDataAvailabeErrorResponse(regattaName, raceName);
} }
@@ -600,7 +600,7 @@ public class RegattasResource extends AbstractSailingServerResource {
jsonRaceTimes.put("currentServerTime-ms", now.getTime()); jsonRaceTimes.put("currentServerTime-ms", now.getTime());
String json = jsonRaceTimes.toJSONString(); String json = jsonRaceTimes.toJSONString();
response = Response.ok(json, MediaType.APPLICATION_JSON).build(); response = Response.ok(json).header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build();
} }
} }
return response; return response;
@@ -633,7 +633,7 @@ public class RegattasResource extends AbstractSailingServerResource {
windSourcesAvailable.add(windSourceJson); windSourcesAvailable.add(windSourceJson);
} }
} }
return Response.ok(windSourcesAvailable.toString(), MediaType.APPLICATION_JSON).build(); return Response.ok(windSourcesAvailable.toString()).header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build();
} }
} }
return response; return response;
@@ -691,7 +691,7 @@ public class RegattasResource extends AbstractSailingServerResource {
JSONObject jsonWindTracks = serializer.serialize(trackedRace); JSONObject jsonWindTracks = serializer.serialize(trackedRace);
String json = jsonWindTracks.toJSONString(); String json = jsonWindTracks.toJSONString();
return Response.ok(json, MediaType.APPLICATION_JSON).build(); return Response.ok(json).header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build();
} }
} }
return response; return response;
@@ -733,7 +733,7 @@ public class RegattasResource extends AbstractSailingServerResource {
JSONObject jsonBearing = serializer.serialize(trackedRace.getDirectionFromStartToNextMark(timePoint) JSONObject jsonBearing = serializer.serialize(trackedRace.getDirectionFromStartToNextMark(timePoint)
.getFrom()); .getFrom());
String json = jsonBearing.toJSONString(); String json = jsonBearing.toJSONString();
return Response.ok(json, MediaType.APPLICATION_JSON).build(); return Response.ok(json).header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build();
} }
} }
return response; return response;
@@ -760,7 +760,7 @@ public class RegattasResource extends AbstractSailingServerResource {
MarkPassingsJsonSerializer serializer = new MarkPassingsJsonSerializer(); MarkPassingsJsonSerializer serializer = new MarkPassingsJsonSerializer();
JSONObject jsonMarkPassings = serializer.serialize(trackedRace); JSONObject jsonMarkPassings = serializer.serialize(trackedRace);
String json = jsonMarkPassings.toJSONString(); String json = jsonMarkPassings.toJSONString();
return Response.ok(json, MediaType.APPLICATION_JSON).build(); return Response.ok(json).header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build();
} }
} }
return response; return response;
@@ -788,7 +788,7 @@ public class RegattasResource extends AbstractSailingServerResource {
jsonRace.put("id", race.getId().toString()); jsonRace.put("id", race.getId().toString());
} }
String json = jsonRaceResults.toJSONString(); String json = jsonRaceResults.toJSONString();
return Response.ok(json, MediaType.APPLICATION_JSON).build(); return Response.ok(json).header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build();
} }
return response; return response;
} }
@@ -935,7 +935,7 @@ public class RegattasResource extends AbstractSailingServerResource {
jsonRaceResults.put("legs", jsonLegs); jsonRaceResults.put("legs", jsonLegs);
String json = jsonRaceResults.toJSONString(); String json = jsonRaceResults.toJSONString();
return Response.ok(json, MediaType.APPLICATION_JSON).build(); return Response.ok(json).header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build();
} }
} }
return response; return response;
@@ -1044,7 +1044,7 @@ public class RegattasResource extends AbstractSailingServerResource {
} }
jsonLiveData.put("competitors", jsonCompetitors); jsonLiveData.put("competitors", jsonCompetitors);
String json = jsonLiveData.toJSONString(); String json = jsonLiveData.toJSONString();
return Response.ok(json, MediaType.APPLICATION_JSON).build(); return Response.ok(json).header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build();
} }
} }
return response; return response;
@@ -43,6 +43,6 @@ public class SearchResource extends AbstractSailingServerResource {
for (LeaderboardSearchResult searchResult : searchResults) { for (LeaderboardSearchResult searchResult : searchResults) {
jsonSearchResults.add(serializer.serialize(searchResult)); jsonSearchResults.add(serializer.serialize(searchResult));
} }
return Response.ok(jsonSearchResults.toJSONString(), MediaType.APPLICATION_JSON).build(); return Response.ok(jsonSearchResults.toJSONString()).header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build();
} }
} }
@@ -33,6 +33,7 @@ import com.sap.sailing.domain.base.impl.CourseDataImpl;
import com.sap.sailing.domain.base.impl.MarkImpl; import com.sap.sailing.domain.base.impl.MarkImpl;
import com.sap.sailing.domain.base.impl.RegattaImpl; import com.sap.sailing.domain.base.impl.RegattaImpl;
import com.sap.sailing.domain.base.impl.WaypointImpl; import com.sap.sailing.domain.base.impl.WaypointImpl;
import com.sap.sailing.domain.common.CourseDesignerMode;
import com.sap.sailing.domain.common.MarkType; import com.sap.sailing.domain.common.MarkType;
import com.sap.sailing.domain.common.PassingInstruction; import com.sap.sailing.domain.common.PassingInstruction;
import com.sap.sailing.domain.common.racelog.RaceLogRaceStatus; import com.sap.sailing.domain.common.racelog.RaceLogRaceStatus;
@@ -133,11 +134,11 @@ public class RaceLogReplicationTest extends AbstractLogReplicationTest<RaceLog,
final String raceColumnName = "R1"; final String raceColumnName = "R1";
Regatta masterRegatta = setupRegatta(RegattaImpl.getDefaultName(regattaName, BOAT_CLASS_NAME_49er), seriesName, fleetName, BOAT_CLASS_NAME_49er); Regatta masterRegatta = setupRegatta(RegattaImpl.getDefaultName(regattaName, BOAT_CLASS_NAME_49er), seriesName, fleetName, BOAT_CLASS_NAME_49er);
RaceLog masterLog = setupRaceColumn(masterRegatta, seriesName, raceColumnName, fleetName); RaceLog masterLog = setupRaceColumn(masterRegatta, seriesName, raceColumnName, fleetName);
raceLogEvent = new RaceLogCourseDesignChangedEventImpl(MillisecondsTimePoint.now(), author, 43, createCourseData()); raceLogEvent = new RaceLogCourseDesignChangedEventImpl(MillisecondsTimePoint.now(), author, 43, createCourseData(), CourseDesignerMode.ADMIN_CONSOLE);
masterLog.add(raceLogEvent); masterLog.add(raceLogEvent);
replicaReplicator.startToReplicateFrom(masterDescriptor); replicaReplicator.startToReplicateFrom(masterDescriptor);
RaceLog replicaLog = getReplicaLog(seriesName, fleetName, raceColumnName, masterRegatta); RaceLog replicaLog = getReplicaLog(seriesName, fleetName, raceColumnName, masterRegatta);
anotherRaceLogEvent = new RaceLogCourseDesignChangedEventImpl(MillisecondsTimePoint.now(), author, 43, createCourseData()); anotherRaceLogEvent = new RaceLogCourseDesignChangedEventImpl(MillisecondsTimePoint.now(), author, 43, createCourseData(), CourseDesignerMode.ADMIN_CONSOLE);
addAndValidateEventIds(masterLog, replicaLog, anotherRaceLogEvent); addAndValidateEventIds(masterLog, replicaLog, anotherRaceLogEvent);
compareReplicatedCourseDesignEvent(replicaLog, (RaceLogCourseDesignChangedEvent) anotherRaceLogEvent); compareReplicatedCourseDesignEvent(replicaLog, (RaceLogCourseDesignChangedEvent) anotherRaceLogEvent);
} }
@@ -162,11 +163,11 @@ public class RaceLogReplicationTest extends AbstractLogReplicationTest<RaceLog,
final String raceColumnName = "R1"; final String raceColumnName = "R1";
FlexibleLeaderboard masterLeaderboard = setupFlexibleLeaderboard(leaderboardName); FlexibleLeaderboard masterLeaderboard = setupFlexibleLeaderboard(leaderboardName);
RaceLog masterLog = setupRaceColumn(leaderboardName, fleetName, raceColumnName); RaceLog masterLog = setupRaceColumn(leaderboardName, fleetName, raceColumnName);
raceLogEvent = new RaceLogCourseDesignChangedEventImpl(MillisecondsTimePoint.now(), author, 43, createCourseData()); raceLogEvent = new RaceLogCourseDesignChangedEventImpl(MillisecondsTimePoint.now(), author, 43, createCourseData(), CourseDesignerMode.ADMIN_CONSOLE);
masterLog.add(raceLogEvent); masterLog.add(raceLogEvent);
replicaReplicator.startToReplicateFrom(masterDescriptor); replicaReplicator.startToReplicateFrom(masterDescriptor);
RaceLog replicaLog = getReplicaLog(fleetName, raceColumnName, masterLeaderboard); RaceLog replicaLog = getReplicaLog(fleetName, raceColumnName, masterLeaderboard);
anotherRaceLogEvent = new RaceLogCourseDesignChangedEventImpl(MillisecondsTimePoint.now(), author, 43, createCourseData()); anotherRaceLogEvent = new RaceLogCourseDesignChangedEventImpl(MillisecondsTimePoint.now(), author, 43, createCourseData(), CourseDesignerMode.ADMIN_CONSOLE);
addAndValidateEventIds(masterLog, replicaLog, anotherRaceLogEvent); addAndValidateEventIds(masterLog, replicaLog, anotherRaceLogEvent);
compareReplicatedCourseDesignEvent(replicaLog, (RaceLogCourseDesignChangedEvent) anotherRaceLogEvent); compareReplicatedCourseDesignEvent(replicaLog, (RaceLogCourseDesignChangedEvent) anotherRaceLogEvent);
} }
@@ -596,7 +596,7 @@ public class RacingActivity extends SessionActivity implements RaceListCallbacks
transaction.remove(extra); transaction.remove(extra);
} }
} }
if (findViewById(R.id.race_content) != null) { if (findViewById(R.id.race_content) != null && mSelectedRace != null) {
if (mSelectedRace.getStatus() != RaceLogRaceStatus.FINISHING) { if (mSelectedRace.getStatus() != RaceLogRaceStatus.FINISHING) {
content = RaceFlagViewerFragment.newInstance(); content = RaceFlagViewerFragment.newInstance();
} else { } else {
@@ -36,6 +36,7 @@ import com.sap.sailing.domain.base.Waypoint;
import com.sap.sailing.domain.base.impl.ControlPointWithTwoMarksImpl; import com.sap.sailing.domain.base.impl.ControlPointWithTwoMarksImpl;
import com.sap.sailing.domain.base.impl.CourseDataImpl; import com.sap.sailing.domain.base.impl.CourseDataImpl;
import com.sap.sailing.domain.base.impl.WaypointImpl; import com.sap.sailing.domain.base.impl.WaypointImpl;
import com.sap.sailing.domain.common.CourseDesignerMode;
import com.sap.sailing.domain.common.PassingInstruction; import com.sap.sailing.domain.common.PassingInstruction;
import com.sap.sailing.racecommittee.app.AppConstants; import com.sap.sailing.racecommittee.app.AppConstants;
import com.sap.sailing.racecommittee.app.R; import com.sap.sailing.racecommittee.app.R;
@@ -563,7 +564,7 @@ public class CourseFragmentMarks extends CourseFragment implements CourseMarkAda
} }
protected void sendCourseDataAndDismiss(CourseBase courseDesign) { protected void sendCourseDataAndDismiss(CourseBase courseDesign) {
getRaceState().setCourseDesign(MillisecondsTimePoint.now(), courseDesign); getRaceState().setCourseDesign(MillisecondsTimePoint.now(), courseDesign, CourseDesignerMode.BY_MARKS);
saveChangedCourseDesignInCache(courseDesign); saveChangedCourseDesignInCache(courseDesign);
switch (getArguments().getInt(START_MODE, START_MODE_PRESETUP)) { switch (getArguments().getInt(START_MODE, START_MODE_PRESETUP)) {
case START_MODE_PRESETUP: case START_MODE_PRESETUP:
@@ -14,6 +14,7 @@ import android.widget.ListView;
import com.sap.sailing.android.shared.util.ViewHelper; import com.sap.sailing.android.shared.util.ViewHelper;
import com.sap.sailing.domain.base.CourseBase; import com.sap.sailing.domain.base.CourseBase;
import com.sap.sailing.domain.base.impl.CourseDataImpl; import com.sap.sailing.domain.base.impl.CourseDataImpl;
import com.sap.sailing.domain.common.CourseDesignerMode;
import com.sap.sailing.racecommittee.app.AppConstants; import com.sap.sailing.racecommittee.app.AppConstants;
import com.sap.sailing.racecommittee.app.R; import com.sap.sailing.racecommittee.app.R;
import com.sap.sailing.racecommittee.app.ui.adapters.checked.CheckedItem; import com.sap.sailing.racecommittee.app.ui.adapters.checked.CheckedItem;
@@ -93,7 +94,7 @@ public class CourseFragmentName extends CourseFragment {
public void handleSelection(String course) { public void handleSelection(String course) {
CourseBase courseLayout = new CourseDataImpl(course); CourseBase courseLayout = new CourseDataImpl(course);
getRaceState().setCourseDesign(MillisecondsTimePoint.now(), courseLayout); getRaceState().setCourseDesign(MillisecondsTimePoint.now(), courseLayout, CourseDesignerMode.BY_NAME);
if (getArguments() != null && getArguments().getInt(START_MODE, START_MODE_PRESETUP) == START_MODE_PRESETUP) { if (getArguments() != null && getArguments().getInt(START_MODE, START_MODE_PRESETUP) == START_MODE_PRESETUP) {
openMainScheduleFragment(); openMainScheduleFragment();