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 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 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 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 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();