diff --git a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/DomainFactory.java b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/DomainFactory.java
index aadcfeb2df4..2d379d911bc 100755
--- a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/DomainFactory.java
+++ b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/DomainFactory.java
@@ -264,4 +264,6 @@ public interface DomainFactory {
JSONService parseJSONURLForOneRaceRecord(URL jsonURL, String raceId, boolean loadClientParams) throws IOException, ParseException, org.json.simple.parser.ParseException, URISyntaxException;
+ MetadataParser getMetadataParser();
+
}
diff --git a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/MetadataParser.java b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/MetadataParser.java
new file mode 100755
index 00000000000..2b0b65ae151
--- /dev/null
+++ b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/MetadataParser.java
@@ -0,0 +1,44 @@
+package com.sap.sailing.domain.tractracadapter;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.Map;
+
+import com.sap.sailing.domain.common.MarkType;
+import com.sap.sailing.domain.common.Named;
+import com.sap.sailing.domain.common.NauticalSide;
+import com.sap.sailing.domain.tractracadapter.impl.ClientParamsPHP;
+import com.sap.sailing.domain.tractracadapter.impl.RaceCourseReceiver;
+
+/**
+ * TracTrac objects can be augmented by what TracTrac calls a "DataSheet." These optional data sheets can provide
+ * meta data about the object to which they belong. For example, a course's meta data can provide the passing
+ * sides for each waypoint in the list. The race can provide a definition of side lines in its meta data. And a
+ * control point can define its shape and color.
+ *
+ * This class encapsulates the patterns and procedures by which the additional data is extracted from the
+ * respective TracTrac data. It can be used for both, clientparams.php or TTCM based data.
+ *
+ * @see ClientParamsPHP
+ * @see RaceCourseReceiver
+ *
+ * @author Axel Uhl (d043530)
+ *
+ */
+public interface MetadataParser {
+ public interface ControlPointMetaData extends Named {
+ MarkType getType();
+ String getColor();
+ String getShape();
+ String getPattern();
+ Serializable getId();
+ }
+
+ Map parsePassingSideData(String routeMetadataString, List extends TracTracControlPoint> controlPoints);
+
+ /**
+ * Returns as many metadata objects as there are marks in the control point (two for a gate, one otherwise)
+ */
+ Iterable parseControlPointMetadata(TracTracControlPoint controlPoint);
+
+}
diff --git a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/DomainFactoryImpl.java b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/DomainFactoryImpl.java
index a7f83539d75..8054762be13 100644
--- a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/DomainFactoryImpl.java
+++ b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/DomainFactoryImpl.java
@@ -3,7 +3,6 @@ package com.sap.sailing.domain.tractracadapter.impl;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.Serializable;
-import java.io.StringReader;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
@@ -11,12 +10,10 @@ import java.net.URL;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
-import java.util.Properties;
import java.util.UUID;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -43,7 +40,6 @@ import com.sap.sailing.domain.base.impl.RaceDefinitionImpl;
import com.sap.sailing.domain.base.impl.RegattaImpl;
import com.sap.sailing.domain.base.impl.SidelineImpl;
import com.sap.sailing.domain.base.impl.TeamImpl;
-import com.sap.sailing.domain.common.MarkType;
import com.sap.sailing.domain.common.NauticalSide;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.ScoringSchemeType;
@@ -67,6 +63,8 @@ import com.sap.sailing.domain.tracking.WindTrack;
import com.sap.sailing.domain.tracking.impl.GPSFixMovingImpl;
import com.sap.sailing.domain.tractracadapter.DomainFactory;
import com.sap.sailing.domain.tractracadapter.JSONService;
+import com.sap.sailing.domain.tractracadapter.MetadataParser;
+import com.sap.sailing.domain.tractracadapter.MetadataParser.ControlPointMetaData;
import com.sap.sailing.domain.tractracadapter.Receiver;
import com.sap.sailing.domain.tractracadapter.ReceiverType;
import com.sap.sailing.domain.tractracadapter.TracTracConfiguration;
@@ -107,11 +105,19 @@ public class DomainFactoryImpl implements DomainFactory {
private final WeakIdentityHashMap weakRegattaCache = new WeakIdentityHashMap<>();
private final Map raceCache = new HashMap();
+
+ private final MetadataParser metadataParser;
public DomainFactoryImpl(com.sap.sailing.domain.base.DomainFactory baseDomainFactory) {
this.baseDomainFactory = baseDomainFactory;
+ this.metadataParser = new MetadataParserImpl();
}
+ @Override
+ public MetadataParser getMetadataParser() {
+ return metadataParser;
+ }
+
@Override
public com.sap.sailing.domain.base.DomainFactory getBaseDomainFactory() {
return baseDomainFactory;
@@ -149,39 +155,22 @@ public class DomainFactoryImpl implements DomainFactory {
synchronized (controlPointCache) {
com.sap.sailing.domain.base.ControlPoint domainControlPoint = controlPointCache.get(controlPoint);
if (domainControlPoint == null) {
- String controlPointName = controlPoint.getName();
- final String controlPointMetadataString = controlPoint.getMetadata();
- Map controlPointMetadata;
- if (controlPointMetadataString == null) {
- controlPointMetadata = Collections.emptyMap();
- } else {
- controlPointMetadata = parseControlPointMetadata(controlPointMetadataString);
+ final Iterable controlPointMetadata = getMetadataParser().parseControlPointMetadata(controlPoint);
+ List marks = new ArrayList();
+ for (ControlPointMetaData markMetadata : controlPointMetadata) {
+ Mark mark = baseDomainFactory.getOrCreateMark(markMetadata.getId(), markMetadata.getName(),
+ markMetadata.getType(), markMetadata.getColor(), markMetadata.getShape(),
+ markMetadata.getPattern());
+ marks.add(mark);
}
if (controlPoint.getHasTwoPoints()) {
// it's a gate
- MarkType type1 = resolveMarkTypeFromMetadata(controlPointMetadata, "P1.Type");
- MarkType type2 = resolveMarkTypeFromMetadata(controlPointMetadata, "P2.Type");
- String color1 = controlPointMetadata.get("P1.Color");
- String color2 = controlPointMetadata.get("P2.Color");
- String shape1 = controlPointMetadata.get("P1.Shape");
- String shape2 = controlPointMetadata.get("P2.Shape");
- String pattern1 = controlPointMetadata.get("P1.Pattern");
- String pattern2 = controlPointMetadata.get("P2.Pattern");
- String mark1UUID = controlPointMetadata.get("P1.UUID");
- String mark2UUID = controlPointMetadata.get("P2.UUID");
- final String name1 = controlPointName + " (1)";
- final Serializable id1 = mark1UUID == null ? name1 : UUID.fromString(mark1UUID);
- Mark mark1 = baseDomainFactory.getOrCreateMark(id1, name1, type1, color1, shape1, pattern1);
- final String name2 = controlPointName + " (2)";
- final Serializable id2 = mark2UUID == null ? name2 : UUID.fromString(mark2UUID);
- Mark mark2 = baseDomainFactory.getOrCreateMark(id2, name2, type2, color2, shape2, pattern2);
- domainControlPoint = baseDomainFactory.createGate(controlPoint.getId(), mark1, mark2, controlPointName);
+ Iterator markIter = marks.iterator();
+ Mark mark1 = markIter.next();
+ Mark mark2 = markIter.next();
+ domainControlPoint = baseDomainFactory.createGate(controlPoint.getId(), mark1, mark2, controlPoint.getName());
} else {
- MarkType type = resolveMarkTypeFromMetadata(controlPointMetadata, "Type");
- String color = controlPointMetadata.get("Color");
- String shape = controlPointMetadata.get("Shape");
- String pattern = controlPointMetadata.get("Pattern");
- Mark mark = baseDomainFactory.getOrCreateMark(controlPoint.getId(), controlPointName, type, color, shape, pattern);
+ Mark mark = marks.iterator().next();
domainControlPoint = mark;
}
controlPointCache.put(controlPoint, domainControlPoint);
@@ -190,33 +179,6 @@ public class DomainFactoryImpl implements DomainFactory {
}
}
- private MarkType resolveMarkTypeFromMetadata(Map controlPointMetadata, String typePropertyName) {
- MarkType result = MarkType.BUOY;
- String markType = controlPointMetadata.get(typePropertyName);
- if(markType != null && !markType.isEmpty()) {
- for(MarkType m: MarkType.values()) {
- if(m.name().equalsIgnoreCase(markType)) {
- result = m;
- break;
- }
- }
- }
- return result;
- }
-
- @SuppressWarnings({ "rawtypes", "unchecked" })
- private Map parseControlPointMetadata(String controlPointMetadata) {
- Map metadataMap = new HashMap();
- try {
- Properties p = new Properties();
- p.load(new StringReader(controlPointMetadata));
- metadataMap = new HashMap((Map) p);
- } catch (IOException e) {
- // do nothing
- }
- return metadataMap;
- }
-
@Override
public Course createCourse(String name, Iterable> controlPoints) {
List waypointList = new ArrayList();
diff --git a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/MetadataParserImpl.java b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/MetadataParserImpl.java
new file mode 100755
index 00000000000..00d27b20428
--- /dev/null
+++ b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/MetadataParserImpl.java
@@ -0,0 +1,176 @@
+package com.sap.sailing.domain.tractracadapter.impl;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.io.StringReader;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.UUID;
+
+import com.sap.sailing.domain.common.MarkType;
+import com.sap.sailing.domain.common.NauticalSide;
+import com.sap.sailing.domain.common.impl.NamedImpl;
+import com.sap.sailing.domain.tractracadapter.MetadataParser;
+import com.sap.sailing.domain.tractracadapter.TracTracControlPoint;
+
+/**
+ * TracTrac objects can be augmented by what TracTrac calls a "DataSheet." These optional data sheets can provide
+ * meta data about the object to which they belong. For example, a course's meta data can provide the passing
+ * sides for each waypoint in the list. The race can provide a definition of side lines in its meta data. And a
+ * control point can define its shape and color.
+ *
+ * This class encapsulates the patterns and procedures by which the additional data is extracted from the
+ * respective TracTrac data. It can be used for both, clientparams.php or TTCM based data.
+ *
+ * @see ClientParamsPHP
+ * @see RaceCourseReceiver
+ *
+ * @author Axel Uhl (d043530)
+ *
+ */
+public class MetadataParserImpl implements MetadataParser {
+ private class ControlPointMetaDataImpl extends NamedImpl implements ControlPointMetaData {
+ private static final long serialVersionUID = 1L;
+ private final MarkType type;
+ private final String color;
+ private final String shape;
+ private final String pattern;
+ private final Serializable id;
+
+ public ControlPointMetaDataImpl(String name, MarkType type, String color, String shape, String pattern, Serializable id) {
+ super(name);
+ this.type = type;
+ this.color = color;
+ this.shape = shape;
+ this.pattern = pattern;
+ this.id = id;
+ }
+
+ @Override
+ public MarkType getType() {
+ return type;
+ }
+
+ @Override
+ public String getColor() {
+ return color;
+ }
+
+ @Override
+ public String getShape() {
+ return shape;
+ }
+
+ @Override
+ public String getPattern() {
+ return pattern;
+ }
+
+ @Override
+ public Serializable getId() {
+ return id;
+ }
+ }
+
+ /**
+ * Parses the route metadata for additional course information
+ * The 'passing side' for each course waypoint is encoded like this...
+ * Seq.1=GATE
+ * Seq.2=PORT
+ * Seq.3=GATE
+ * Seq.4=STARBOARD
+ */
+ @Override
+ public Map parsePassingSideData(String routeMetadataString, List extends TracTracControlPoint> controlPoints) {
+ Map result = new HashMap();
+ int controlPointsCount = controlPoints.size();
+ if(routeMetadataString != null) {
+ Map routeMetadata = parseMetadata(routeMetadataString);
+ for(int i = 1; i <= controlPointsCount; i++) {
+ String seqValue = routeMetadata.get("Seq." + i);
+ TracTracControlPoint controlPoint = controlPoints.get(i-1);
+ if(!controlPoint.getHasTwoPoints() && seqValue != null) {
+ if("PORT".equalsIgnoreCase(seqValue)) {
+ result.put(i, NauticalSide.PORT);
+ } else if("STARBOARD".equalsIgnoreCase(seqValue)) {
+ result.put(i, NauticalSide.STARBOARD);
+ }
+ }
+ }
+ }
+ return result;
+ }
+
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ private Map parseMetadata(String metadata) {
+ Map metadataMap = new HashMap();
+ try {
+ Properties p = new Properties();
+ p.load(new StringReader(metadata));
+ metadataMap = new HashMap((Map) p);
+ } catch (IOException e) {
+ // do nothing
+ }
+ return metadataMap;
+ }
+
+ @Override
+ public Iterable parseControlPointMetadata(TracTracControlPoint controlPoint) {
+ final Iterable result;
+ final String controlPointMetadataString = controlPoint.getMetadata();
+ final String controlPointName = controlPoint.getName();
+ final Map controlPointMetadata;
+ if (controlPointMetadataString == null) {
+ controlPointMetadata = Collections.emptyMap();
+ } else {
+ controlPointMetadata = parseMetadata(controlPointMetadataString);
+ }
+ if (controlPoint.getHasTwoPoints()) {
+ // it's a gate
+ MarkType type1 = resolveMarkTypeFromMetadata(controlPointMetadata, "P1.Type");
+ MarkType type2 = resolveMarkTypeFromMetadata(controlPointMetadata, "P2.Type");
+ String color1 = controlPointMetadata.get("P1.Color");
+ String color2 = controlPointMetadata.get("P2.Color");
+ String shape1 = controlPointMetadata.get("P1.Shape");
+ String shape2 = controlPointMetadata.get("P2.Shape");
+ String pattern1 = controlPointMetadata.get("P1.Pattern");
+ String pattern2 = controlPointMetadata.get("P2.Pattern");
+ String mark1UUID = controlPointMetadata.get("P1.UUID");
+ String mark2UUID = controlPointMetadata.get("P2.UUID");
+ final String name1 = controlPointName + " (1)";
+ final Serializable id1 = mark1UUID == null ? name1 : UUID.fromString(mark1UUID);
+ ControlPointMetaData mark1Metadata = new ControlPointMetaDataImpl(name1, type1, color1, shape1, pattern1, id1);
+ final String name2 = controlPointName + " (2)";
+ final Serializable id2 = mark2UUID == null ? name2 : UUID.fromString(mark2UUID);
+ ControlPointMetaData mark2Metadata = new ControlPointMetaDataImpl(name2, type2, color2, shape2, pattern2, id2);
+ result = Arrays.asList(new ControlPointMetaData[] { mark1Metadata, mark2Metadata });
+ } else {
+ MarkType type = resolveMarkTypeFromMetadata(controlPointMetadata, "Type");
+ String color = controlPointMetadata.get("Color");
+ String shape = controlPointMetadata.get("Shape");
+ String pattern = controlPointMetadata.get("Pattern");
+ ControlPointMetaData markMetadata = new ControlPointMetaDataImpl(controlPointName, type, color, shape, pattern, controlPoint.getId());
+ result = Collections.singleton(markMetadata);
+ }
+ return result;
+ }
+
+ private MarkType resolveMarkTypeFromMetadata(Map controlPointMetadata, String typePropertyName) {
+ MarkType result = MarkType.BUOY;
+ String markType = controlPointMetadata.get(typePropertyName);
+ if(markType != null && !markType.isEmpty()) {
+ for(MarkType m: MarkType.values()) {
+ if(m.name().equalsIgnoreCase(markType)) {
+ result = m;
+ break;
+ }
+ }
+ }
+ return result;
+ }
+
+}
diff --git a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/RaceCourseReceiver.java b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/RaceCourseReceiver.java
index ac1053f4fe7..a1a1c087ed6 100755
--- a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/RaceCourseReceiver.java
+++ b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/RaceCourseReceiver.java
@@ -6,6 +6,8 @@ import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
@@ -101,12 +103,16 @@ public class RaceCourseReceiver extends AbstractReceiverWithQueue courseWaypointPassingSides = parseAdditionalCourseDataFromMetadata(route, event.getB().getPoints(), routeMetadataString);
+ final LinkedHashMap ttControlPointsForOriginalControlPoints = new LinkedHashMap<>();
+ for (com.tractrac.clientmodule.ControlPoint cp : event.getB().getPoints()) {
+ ttControlPointsForOriginalControlPoints.put(cp, new ControlPointAdapter(cp));
+ }
+ Map courseWaypointPassingSides = parseAdditionalCourseDataFromMetadata(ttControlPointsForOriginalControlPoints.values(), routeMetadataString);
List> ttControlPoints = new ArrayList<>();
int i = 1;
for (com.tractrac.clientmodule.ControlPoint cp : event.getB().getPoints()) {
NauticalSide nauticalSide = courseWaypointPassingSides.containsKey(i) ? courseWaypointPassingSides.get(i) : null;
- ttControlPoints.add(new Pair(new ControlPointAdapter(cp), nauticalSide));
+ ttControlPoints.add(new Pair(ttControlPointsForOriginalControlPoints.get(cp), nauticalSide));
i++;
}
Course course = getDomainFactory().createCourse(route.getName(), ttControlPoints);
@@ -156,17 +162,17 @@ public class RaceCourseReceiver extends AbstractReceiverWithQueue
- * @param routeMetadataString TODO
*/
- private Map parseAdditionalCourseDataFromMetadata(Route route,
- List controlPoints, String routeMetadataString) {
+ private Map parseAdditionalCourseDataFromMetadata(Iterable controlPoints,
+ String routeMetadataString) {
Map result = new HashMap();
- int controlPointsCount = controlPoints.size();
if (routeMetadataString != null) {
Map routeMetadata = parseMetadata(routeMetadataString);
- for (int i = 1; i <= controlPointsCount; i++) {
+ Iterator iter = controlPoints.iterator();
+ int i=1;
+ while (iter.hasNext()) {
String seqValue = routeMetadata.get("Seq." + i);
- com.tractrac.clientmodule.ControlPoint controlPoint = controlPoints.get(i - 1);
+ TracTracControlPoint controlPoint = iter.next();
if (!controlPoint.getHasTwoPoints() && seqValue != null) {
if ("PORT".equalsIgnoreCase(seqValue)) {
result.put(i, NauticalSide.PORT);
@@ -174,6 +180,7 @@ public class RaceCourseReceiver extends AbstractReceiverWithQueue newCourseControlPoints = new ArrayList<>();
List> newCourseControlPointsWithPassingSide = new ArrayList<>();
final List extends TracTracControlPoint> newTracTracControlPoints = clientParams.getRace().getDefaultRoute().getControlPoints();
- Map passingSideData = parsePassingSideData(clientParams.getRace().getDefaultRoute(), newTracTracControlPoints);
+ Map passingSideData = domainFactory.getMetadataParser().parsePassingSideData(
+ clientParams.getRace().getDefaultRoute().getMetadata(), newTracTracControlPoints);
int i = 1;
for (TracTracControlPoint newTracTracControlPoint : newTracTracControlPoints) {
NauticalSide nauticalSide = passingSideData.containsKey(i) ? passingSideData.get(i) : null;
@@ -329,48 +327,6 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl implements
}
}
- /**
- * Parses the route metadata for additional course information
- * The 'passing side' for each course waypoint is encoded like this...
- * Seq.1=GATE
- * Seq.2=PORT
- * Seq.3=GATE
- * Seq.4=STARBOARD
- */
- private Map parsePassingSideData(ClientParamsPHP.Route route, List extends TracTracControlPoint> controlPoints) {
- Map result = new HashMap();
- int controlPointsCount = controlPoints.size();
- String routeMetadataString = route.getMetadata();
- if(routeMetadataString != null) {
- Map routeMetadata = parseMetadata(routeMetadataString);
- for(int i = 1; i <= controlPointsCount; i++) {
- String seqValue = routeMetadata.get("Seq." + i);
- TracTracControlPoint controlPoint = controlPoints.get(i-1);
- if(!controlPoint.getHasTwoPoints() && seqValue != null) {
- if("PORT".equalsIgnoreCase(seqValue)) {
- result.put(i, NauticalSide.PORT);
- } else if("STARBOARD".equalsIgnoreCase(seqValue)) {
- result.put(i, NauticalSide.STARBOARD);
- }
- }
- }
- }
- return result;
- }
-
- @SuppressWarnings({ "rawtypes", "unchecked" })
- private Map parseMetadata(String metadata) {
- Map metadataMap = new HashMap();
- try {
- Properties p = new Properties();
- p.load(new StringReader(metadata));
- metadataMap = new HashMap((Map) p);
- } catch (IOException e) {
- // do nothing
- }
- return metadataMap;
- }
-
private void updateStartStopTimesAndLiveDelay(ClientParamsPHP clientParams, Simulator simulator) {
RaceDefinition currentRace = null;
long delayInMillis = clientParams.getLiveDelayInMillis();