use RefNo as ID for ORC certificates; distinguish valid/invalid in certificate search

This commit is contained in:
Axel Uhl committed 2020-03-11 00:13:24 +01:00
1 parent b0ecf94880
commit ffc00ddbf0
18 files changed
+322 -151

No files matched your search

@@ -5,10 +5,12 @@ import java.util.Map;
import com.sap.sailing.domain.common.impl.KnotSpeedImpl;
import com.sap.sse.common.Bearing;
import com.sap.sse.common.CountryCode;
import com.sap.sse.common.Distance;
import com.sap.sse.common.Duration;
import com.sap.sse.common.Speed;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util.Pair;
import com.sap.sse.common.WithID;
import com.sap.sse.common.impl.DegreeBearingImpl;
@@ -24,10 +26,9 @@ import com.sap.sse.common.impl.DegreeBearingImpl;
* {@link RankingMetric}.
* <p>
*
* The {@link WithID} interface is to be implemented such that a {@link String} is produced as the ID that contains the
* concatenation (without intermediate white space) of the {@code NatAuth}, the {@code CertNo} and the {@code BIN}
* fields, as provided as single fields in the JSON representation, and as provided in the concatenated form in the
* {@code NATCERTN.FILE_ID} column in the RMS format.
* The {@link WithID} interface is to be implemented based on the {@link #getReferenceNumber()} which is unique across the
* ORC world-wide certificate database. Note that the {@link #getBoatKey()} consisting of the {@link #getFileId()} and the
* {@link #getIssuingCountry()} field values is a unique key for the boat to which the certificate belongs.
* <p>
*
* The certificate contains time allowances (or, conversely, speed predictions) for a combination of true wind speeds
@@ -64,14 +65,38 @@ public interface ORCCertificate extends WithID, Serializable {
Bearing[] ALLOWANCES_TRUE_WIND_ANGLES = { new DegreeBearingImpl(52), new DegreeBearingImpl(60),
new DegreeBearingImpl(75), new DegreeBearingImpl(90), new DegreeBearingImpl(110),
new DegreeBearingImpl(120), new DegreeBearingImpl(135), new DegreeBearingImpl(150) };
/**
* As the ID of an ORC certificate we use the concatenation (without intermediate white space) of the
* {@code NatAuth}, the {@code CertNo} and the {@code BIN} fields, as provided as single fields in the JSON
* representation
* We use the {@link #getReferenceNumber() reference number ("RefNo")} as the ID of a certificate document.
* It is unique for all of ORC's certificates issued world-wide.
*/
@Override
String getId();
String getReferenceNumber();
/**
* Technically, at ORC a certificate's boat is identified by a "file ID" as returned by this method, together
* with the {@link #getIssuingCountry() issuing country} which together makes this a unique boat key. For each
* such boat key there is at most one valid certificate at a time.
*/
String getFileId();
/**
* The country whose authority issued this certificate. Together with the {@link #getFileId() file ID} this makes for a
* unique key of the boat to which this certifiate belongs.
*/
CountryCode getIssuingCountry();
/**
* A unique key of the boat to which this certificate belongs; it consists of the {@link #getFileId() file ID} and the
* {@link #getIssuingCountry() issuing country}. For any such key the ORC database delivers at most one valid certificate
* at any given time.
*/
default Pair<String, CountryCode> getBoatKey() {
return new Pair<>(getFileId(), getIssuingCountry());
}
/**
* Returns the sailnumber of the {@link Competitor} which this certificate belongs to.
*
@@ -6,6 +6,7 @@ import java.util.Map;
import com.sap.sailing.domain.common.impl.NauticalMileDistance;
import com.sap.sailing.domain.common.orc.ORCCertificate;
import com.sap.sse.common.Bearing;
import com.sap.sse.common.CountryCode;
import com.sap.sse.common.Distance;
import com.sap.sse.common.Duration;
import com.sap.sse.common.Speed;
@@ -24,7 +25,7 @@ import com.sap.sse.common.Util;
public class ORCCertificateImpl implements ORCCertificate {
private static final long serialVersionUID = 8725162998514202782L;
private final String idConsistingOfNatAuthCertNoAndBIN;
private final String referenceNumber;
public final static Distance NAUTICAL_MILE = new NauticalMileDistance(1);
@@ -35,7 +36,9 @@ public class ORCCertificateImpl implements ORCCertificate {
private final Duration gph;
private final Double cdl;
private final TimePoint issueDate;
private final String fileId;
private final CountryCode issuingCountry;
/**
* The "core" of the certificate for performance curve scoring; the values in the map tell how fast the boat
* described by this certificate is expected to sail for the true wind speed (TWS) provided by the key, and the true
@@ -111,11 +114,13 @@ public class ORCCertificateImpl implements ORCCertificate {
* {@link ORCCertificate#ALLOWANCES_TRUE_WIND_ANGLES}.
*/
public ORCCertificateImpl(Speed[] allowancesTrueWindSpeeds, Bearing[] allowancesTrueWindAngles,
String idConsistingOfNatAuthCertNoAndBIN, String sailnumber, String boatName, String boatClassName,
Distance length, Duration gph, Double cdl, TimePoint issueDate,
String referenceNumber, String fileId, String sailnumber, String boatName,
String boatClassName, Distance length, Duration gph, Double cdl,
TimePoint issueDate, CountryCode issuingCountry,
Map<Speed, Map<Bearing, Speed>> velocityPredictionsPerTrueWindSpeedAndAngle, Map<Speed, Bearing> beatAngles,
Map<Speed, Speed> beatVMGPredictionPerTrueWindSpeed, Map<Speed, Duration> beatAllowancePerTrueWindSpeed,
Map<Speed, Bearing> runAngles, Map<Speed, Speed> runVMGPredictionPerTrueWindSpeed,
Map<Speed, Bearing> runAngles,
Map<Speed, Speed> runVMGPredictionPerTrueWindSpeed,
Map<Speed, Duration> runAllowancePerTrueWindSpeed,
Map<Speed, Speed> windwardLeewardSpeedPredictionsPerTrueWindSpeed,
Map<Speed, Speed> longDistanceSpeedPredictionsPerTrueWindSpeed,
@@ -123,7 +128,9 @@ public class ORCCertificateImpl implements ORCCertificate {
Map<Speed, Speed> nonSpinnakerSpeedPredictionsPerTrueWindSpeed) {
this.allowancesTrueWindAngles = allowancesTrueWindAngles;
this.allowancesTrueWindSpeeds = allowancesTrueWindSpeeds;
this.idConsistingOfNatAuthCertNoAndBIN = idConsistingOfNatAuthCertNoAndBIN;
this.referenceNumber = referenceNumber;
this.fileId = fileId;
this.issuingCountry = issuingCountry;
this.sailNumber = sailnumber;
this.boatName = boatName;
this.boatClassName = boatClassName;
@@ -150,21 +157,23 @@ public class ORCCertificateImpl implements ORCCertificate {
* {@link ORCCertificate#ALLOWANCES_TRUE_WIND_ANGLES}, respectively, for the matrix of time allowances
* or, conversely, the speed predictions.
*/
public ORCCertificateImpl(String idConsistingOfNatAuthCertNoAndBIN,
String sailnumber, String boatName, String boatClassName,
Distance length, Duration gph,
Double cdl, TimePoint issueDate,
Map<Speed, Map<Bearing, Speed>> velocityPredictionsPerTrueWindSpeedAndAngle,
public ORCCertificateImpl(String referenceNumber,
String fileId, String sailnumber, String boatName,
String boatClassName, Distance length,
Duration gph, Double cdl,
TimePoint issueDate,
CountryCode issuingCountry, Map<Speed, Map<Bearing, Speed>> velocityPredictionsPerTrueWindSpeedAndAngle,
Map<Speed, Bearing> beatAngles, Map<Speed, Speed> beatVMGPredictionPerTrueWindSpeed,
Map<Speed, Duration> beatAllowancePerTrueWindSpeed, Map<Speed, Bearing> runAngles,
Map<Speed, Duration> beatAllowancePerTrueWindSpeed,
Map<Speed, Bearing> runAngles,
Map<Speed, Speed> runVMGPredictionPerTrueWindSpeed,
Map<Speed, Duration> runAllowancePerTrueWindSpeed,
Map<Speed, Speed> windwardLeewardSpeedPredictionsPerTrueWindSpeed,
Map<Speed, Speed> longDistanceSpeedPredictionsPerTrueWindSpeed,
Map<Speed, Speed> circularRandomSpeedPredictionsPerTrueWindSpeed,
Map<Speed, Speed> nonSpinnakerSpeedPredictionsPerTrueWindSpeed) {
this(ALLOWANCES_TRUE_WIND_SPEEDS, ALLOWANCES_TRUE_WIND_ANGLES, idConsistingOfNatAuthCertNoAndBIN, sailnumber,
boatName, boatClassName, length, gph, cdl, issueDate, velocityPredictionsPerTrueWindSpeedAndAngle,
this(ALLOWANCES_TRUE_WIND_SPEEDS, ALLOWANCES_TRUE_WIND_ANGLES, referenceNumber, fileId, sailnumber, boatName,
boatClassName, length, gph, cdl, issueDate, issuingCountry, velocityPredictionsPerTrueWindSpeedAndAngle,
beatAngles, beatVMGPredictionPerTrueWindSpeed, beatAllowancePerTrueWindSpeed, runAngles,
runVMGPredictionPerTrueWindSpeed, runAllowancePerTrueWindSpeed,
windwardLeewardSpeedPredictionsPerTrueWindSpeed, longDistanceSpeedPredictionsPerTrueWindSpeed,
@@ -173,7 +182,22 @@ public class ORCCertificateImpl implements ORCCertificate {
@Override
public String getId() {
return idConsistingOfNatAuthCertNoAndBIN;
return getReferenceNumber();
}
@Override
public String getReferenceNumber() {
return referenceNumber;
}
@Override
public String getFileId() {
return fileId;
}
@Override
public CountryCode getIssuingCountry() {
return issuingCountry;
}
@Override
@@ -35,8 +35,8 @@ public abstract class AbstractORCCertificateImporterTest {
protected void testSimpleOnlineFileRead(String rmsOrJsonFormat) throws IOException, ParseException, InterruptedException {
Collection<ORCCertificate> certificates = customIgnoreRule.getAvailableCerts();
final ORCCertificate cert = certificates.stream().findFirst().get();
String countryCode = cert.getId().substring(0, 3);
String url = String.format("https://data.orc.org/public/WPub.dll?action=DownBoatRMS&CountryId=%s&ext=%s", countryCode, rmsOrJsonFormat);
String countryCode = cert.getIssuingCountry().getThreeLetterIOCCode();
String url = String.format("https://data.orc.org/public/WPub.dll?action=DownRMS&CountryId=%s&ext=%s", countryCode, rmsOrJsonFormat);
ORCCertificatesCollection importer = ORCCertificatesImporter.INSTANCE.read(new URL(url).openStream());
ORCCertificate someValidCert = null, referenceCert = null;
for (ORCCertificate orcCertificate : certificates) {
@@ -71,7 +71,7 @@ public class FailIfNoValidOrcCertificateRule implements TestRule {
certificateExists = false;
for (CountryCode cc : CountryCodeFactory.INSTANCE.getAll()) {
try {
Iterable<CertificateHandle> certificateHandles = db.search(cc, year, null, null, null, null);
Iterable<CertificateHandle> certificateHandles = db.search(cc, year, null, null, null, null, /* includeInvalid */ false);
Iterable<ORCCertificate> orcCertificates = db.getCertificates(certificateHandles);
orcCertificates.forEach(availableCerts::add);
if (orcCertificates.iterator().hasNext()) {
@@ -128,7 +128,7 @@ public class ORCPerformanceCurveRankingTest extends OnlineTracTracBasedTest {
final Boat boat = getTrackedRace().getRace().getBoatOfCompetitor(competitor);
ORCCertificate certificate;
if (boat.getSailID().equals("Air is blue")) {
certificate = certificates.getCertificateById("ITA52521I5252");
certificate = certificates.getCertificateById("ITA00052727");
} else {
certificate = certificates.getCertificateById(boat.getSailID());
if (certificate == null) {
@@ -10,7 +10,7 @@ public class TestORCCertificateImporterJSON extends AbstractORCCertificateImport
@Test
public void testSimpleLocalJSONFileRead() throws IOException, ParseException {
testSimpleLocalFileRead("GER2019.json", "GER166844GER7323");
testSimpleLocalFileRead("GER2019.json", "GER20041179");
}
@FailIfNoValidOrcCertificates
@@ -15,7 +15,7 @@ public class TestORCCertificateImporterRMS extends AbstractORCCertificateImporte
@Test
public void testSimpleLocalRMSFileRead() throws IOException, ParseException {
testSimpleLocalFileRead("GER2019.rms", "GER166844GER7323");
testSimpleLocalFileRead("GER2019.rms", "GER20041179");
}
@FailIfNoValidOrcCertificates
@@ -103,31 +103,31 @@ public class TestORCPerformanceCurve {
@Test
public void testLagrangeInterpolation60() throws FunctionEvaluationException {
ORCCertificate certificate = importerLocal.getCertificateById("GER140772GER5549");
ORCCertificate certificate = importerLocal.getCertificateById("GER20040783");
testAllowancePerLeg(certificate, 60.0, 498.4);
}
@Test
public void testLagrangeInterpolation62_5() throws FunctionEvaluationException {
ORCCertificate certificate = importerLocal.getCertificateById("GER140772GER5549");
ORCCertificate certificate = importerLocal.getCertificateById("GER20040783");
testAllowancePerLeg(certificate, 62.5, 492.2, 425.0, 403.6, 393.1, 386.7, 382.6, 371.4);
}
@Test
public void testLagrangeInterpolation98_3() throws FunctionEvaluationException {
ORCCertificate certificate = importerLocal.getCertificateById("GER140772GER5549");
ORCCertificate certificate = importerLocal.getCertificateById("GER20040783");
testAllowancePerLeg(certificate, 98.3, 483.6, 418.7, 394.8, 377.9, 360.2, 345.0, 321.7);
}
@Test
public void testLagrangeInterpolation120() throws FunctionEvaluationException {
ORCCertificate certificate = importerLocal.getCertificateById("GER140772GER5549");
ORCCertificate certificate = importerLocal.getCertificateById("GER20040783");
testAllowancePerLeg(certificate, 120.0, 506);
}
@Test
public void testLagrangeInterpolation138_7() throws FunctionEvaluationException {
ORCCertificate certificate = importerLocal.getCertificateById("GER140772GER5549");
ORCCertificate certificate = importerLocal.getCertificateById("GER20040783");
testAllowancePerLeg(certificate, 138.7, 588.1, 468.4, 413.8, 382.6, 355.1, 326.4, 275.4);
}
@@ -142,7 +142,7 @@ public class TestORCPerformanceCurve {
@Test
public void testSimpleConstructedCourse() throws FunctionEvaluationException {
ORCCertificate certificate = (ORCCertificate) importerLocal.getCertificateById("GER140772GER5549");
ORCCertificate certificate = (ORCCertificate) importerLocal.getCertificateById("GER20040783");
List<ORCPerformanceCurveLeg> legs = new ArrayList<>();
legs.add(new ORCPerformanceCurveLegImpl(new NauticalMileDistance(1.0), new DegreeBearingImpl(0)));
legs.add(new ORCPerformanceCurveLegImpl(new NauticalMileDistance(1.0), new DegreeBearingImpl(30)));
@@ -165,7 +165,7 @@ public class TestORCPerformanceCurve {
@Test
public void testPerformanceCurveInversion() throws MaxIterationsExceededException, FunctionEvaluationException {
Double accuracy = 0.1;
ORCCertificate certificate = (ORCCertificate) importerLocal.getCertificateById("GER140772GER5549");
ORCCertificate certificate = (ORCCertificate) importerLocal.getCertificateById("GER20040783");
ORCPerformanceCurveImpl performanceCurve = (ORCPerformanceCurveImpl) new ORCPerformanceCurveImpl(certificate, alturaCourse);
testBackwardForward(accuracy, performanceCurve, 11.5);
testBackwardForward(accuracy, performanceCurve, 17.23);
@@ -194,12 +194,12 @@ public class TestORCPerformanceCurve {
@Test
public void testImpliedWind() throws MaxIterationsExceededException, FunctionEvaluationException {
double accuracy = 0.0001;
ORCCertificate certificateMoana = importerLocal.getCertificateById("GER140772GER5549");
ORCCertificate certificateMilan = importerLocal.getCertificateById("GER166844GER7323");
ORCCertificate certificateTutima = importerLocal.getCertificateById("GER140618GER5609");
ORCCertificate certificateBank = importerLocal.getCertificateById("GER140755GER5555");
ORCCertificate certificateHaspa = importerLocal.getCertificateById("GER141411GER6300");
ORCCertificate certificateHalbtrocken = importerLocal.getCertificateById("GER141432GER5564");
ORCCertificate certificateMoana = importerLocal.getCertificateById("GER20040783");
ORCCertificate certificateMilan = importerLocal.getCertificateById("GER20041179");
ORCCertificate certificateTutima = importerLocal.getCertificateById("GER30010194");
ORCCertificate certificateBank = importerLocal.getCertificateById("GER20040647");
ORCCertificate certificateHaspa = importerLocal.getCertificateById("GER30010167");
ORCCertificate certificateHalbtrocken = importerLocal.getCertificateById("GER20040632");
ORCPerformanceCurve performanceCurveMoana = new ORCPerformanceCurveImpl(certificateMoana, alturaCourse);
ORCPerformanceCurve performanceCurveMilan = new ORCPerformanceCurveImpl(certificateMilan, alturaCourse);
ORCPerformanceCurve performanceCurveTutima = new ORCPerformanceCurveImpl(certificateTutima, alturaCourse);
@@ -231,12 +231,12 @@ public class TestORCPerformanceCurve {
@Test
public void testAllowances() throws FunctionEvaluationException {
double accuracy = 0.00001;
ORCCertificate certificateMoana = importerLocal.getCertificateById("GER140772GER5549");
ORCCertificate certificateMilan = importerLocal.getCertificateById("GER166844GER7323");
ORCCertificate certificateTutima = importerLocal.getCertificateById("GER140618GER5609");
ORCCertificate certificateBank = importerLocal.getCertificateById("GER140755GER5555");
ORCCertificate certificateHaspa = importerLocal.getCertificateById("GER141411GER6300");
ORCCertificate certificateHalbtrocken = importerLocal.getCertificateById("GER141432GER5564");
ORCCertificate certificateMoana = importerLocal.getCertificateById("GER20040783");
ORCCertificate certificateMilan = importerLocal.getCertificateById("GER20041179");
ORCCertificate certificateTutima = importerLocal.getCertificateById("GER30010194");
ORCCertificate certificateBank = importerLocal.getCertificateById("GER20040647");
ORCCertificate certificateHaspa = importerLocal.getCertificateById("GER30010167");
ORCCertificate certificateHalbtrocken = importerLocal.getCertificateById("GER20040632");
ORCPerformanceCurve performanceCurveMoana = new ORCPerformanceCurveImpl(certificateMoana, alturaCourse);
ORCPerformanceCurve performanceCurveMilan = new ORCPerformanceCurveImpl(certificateMilan, alturaCourse);
ORCPerformanceCurve performanceCurveTutima = new ORCPerformanceCurveImpl(certificateTutima, alturaCourse);
@@ -268,7 +268,7 @@ public class TestORCPerformanceCurve {
final double highAccuracy = 0.01;
final double allowanceAccuracy = 0.1;
final Distance ONE_NAUTICAL_MILE = new NauticalMileDistance(1.0);
final ORCCertificate certificateWithSpecificBins = importerWithSpecificBins.getCertificateById("GRA00073GR317");
final ORCCertificate certificateWithSpecificBins = importerWithSpecificBins.getCertificateById("N/A");
for (final Bearing twa : certificateWithSpecificBins.getTrueWindAngles()) {
final ORCPerformanceCurveCourse singleLegOneMileCourseWithTwa = createSingleLegCourseWithTwa(twa);
final ORCPerformanceCurve performanceCurveSpecificBins = new ORCPerformanceCurveImpl(certificateWithSpecificBins, singleLegOneMileCourseWithTwa);
@@ -299,13 +299,13 @@ public class TestORCPerformanceCurve {
ORCPerformanceCurveCourse complexCourse = new ORCPerformanceCurveCourseImpl(list);
final double accuracy = 0.0001;
ORCCertificate certificateMoana = importerLocal.getCertificateById("GER140772GER5549");
ORCCertificate certificateMilan = importerLocal.getCertificateById("GER166844GER7323");
ORCCertificate certificateTutima = importerLocal.getCertificateById("GER140618GER5609");
ORCCertificate certificateBank = importerLocal.getCertificateById("GER140755GER5555");
ORCCertificate certificateHaspa = importerLocal.getCertificateById("GER141411GER6300");
ORCCertificate certificateHalbtrocken = importerLocal.getCertificateById("GER141432GER5564");
ORCCertificate certificateWithSpecificBins=importerWithSpecificBins.getCertificateById("GRA00073GR317");
ORCCertificate certificateMoana = importerLocal.getCertificateById("GER20040783");
ORCCertificate certificateMilan = importerLocal.getCertificateById("GER20041179");
ORCCertificate certificateTutima = importerLocal.getCertificateById("GER30010194");
ORCCertificate certificateBank = importerLocal.getCertificateById("GER20040647");
ORCCertificate certificateHaspa = importerLocal.getCertificateById("GER30010167");
ORCCertificate certificateHalbtrocken = importerLocal.getCertificateById("GER20040632");
ORCCertificate certificateWithSpecificBins=importerWithSpecificBins.getCertificateById("N/A");
ORCPerformanceCurve performanceCurveMoana = new ORCPerformanceCurveImpl(certificateMoana, complexCourse);
ORCPerformanceCurve performanceCurveMilan = new ORCPerformanceCurveImpl(certificateMilan, complexCourse);
ORCPerformanceCurve performanceCurveTutima = new ORCPerformanceCurveImpl(certificateTutima, complexCourse);
@@ -334,13 +334,13 @@ public class TestORCPerformanceCurve {
ORCPerformanceCurveCourse complexCourse = new ORCPerformanceCurveCourseImpl(list);
double accuracy = 0.00001;
ORCCertificate certificateMoana = importerLocal.getCertificateById("GER140772GER5549");
ORCCertificate certificateMilan = importerLocal.getCertificateById("GER166844GER7323");
ORCCertificate certificateTutima = importerLocal.getCertificateById("GER140618GER5609");
ORCCertificate certificateBank = importerLocal.getCertificateById("GER140755GER5555");
ORCCertificate certificateHaspa = importerLocal.getCertificateById("GER141411GER6300");
ORCCertificate certificateHalbtrocken = importerLocal.getCertificateById("GER141432GER5564");
ORCCertificate certificateWithSpecificBins=importerWithSpecificBins.getCertificateById("GRA00073GR317");
ORCCertificate certificateMoana = importerLocal.getCertificateById("GER20040783");
ORCCertificate certificateMilan = importerLocal.getCertificateById("GER20041179");
ORCCertificate certificateTutima = importerLocal.getCertificateById("GER30010194");
ORCCertificate certificateBank = importerLocal.getCertificateById("GER20040647");
ORCCertificate certificateHaspa = importerLocal.getCertificateById("GER30010167");
ORCCertificate certificateHalbtrocken = importerLocal.getCertificateById("GER20040632");
ORCCertificate certificateWithSpecificBins=importerWithSpecificBins.getCertificateById("N/A");
ORCPerformanceCurve performanceCurveMoana = new ORCPerformanceCurveImpl(certificateMoana, complexCourse);
ORCPerformanceCurve performanceCurveMilan = new ORCPerformanceCurveImpl(certificateMilan, complexCourse);
ORCPerformanceCurve performanceCurveTutima = new ORCPerformanceCurveImpl(certificateTutima, complexCourse);
@@ -2,6 +2,7 @@ package com.sap.sailing.domain.orc;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.util.Arrays;
@@ -92,7 +93,7 @@ public class TestORCPublicCertificateDatabase {
final String referenceNumber = "FRA00013881";
final Iterable<CertificateHandle> result = db.search(/* country */ null, /* yearOfIssuance */ null,
/* referenceNumber */ referenceNumber, /* yachtName */ null, /* sailNumber */ null,
/* boatClassName */ null);
/* boatClassName */ null, /* includeInvalid */ true);
assertEquals(1, Util.size(result));
assertSoulmate(referenceNumber, result.iterator().next());
}
@@ -102,7 +103,7 @@ public class TestORCPublicCertificateDatabase {
final String referenceNumber = "FRA00013881";
final Iterable<CertificateHandle> result = db.search(/* country */ null, /* yearOfIssuance */ 2019,
/* referenceNumber */ null, /* yachtName */ "Soulmate", /* sailNumber */ "DEN- 13",
/* boatClassName */ null);
/* boatClassName */ null, /* includeInvalid */ true);
assertEquals(1, Util.size(result));
assertSoulmate(referenceNumber, result.iterator().next());
}
@@ -112,7 +113,7 @@ public class TestORCPublicCertificateDatabase {
final String referenceNumber = "FRA00013881";
final Iterable<CertificateHandle> result = db.search(/* country */ null, /* yearOfIssuance */ null,
/* referenceNumber */ null, /* yachtName */ "Soulmate", /* sailNumber */ null,
/* boatClassName */ null);
/* boatClassName */ null, /* includeInvalid */ true);
assertTrue(Util.size(result)>1);
boolean found = false;
for (final CertificateHandle handle : result) {
@@ -140,13 +141,14 @@ public class TestORCPublicCertificateDatabase {
cert.getSailNumber(), /*
* boat class name; could be set to cert.getBoatClassName() but there are
* deviations in ORC DBs and query API, so leaving null:
*/ null);
*/ null, /* includeInvalid */ false);
Optional<CertificateHandle> certificateHandle = Optional.ofNullable(certHandles.iterator().hasNext() ? certHandles.iterator().next() : null);
assertTrue("No certificate found for handle "+certificateHandle+
" extracted from certificates "+certificates, certificateHandle.isPresent());
final String referenceNumber = certificateHandle.get().getReferenceNumber();
final CertificateHandle handle = db.getCertificateHandle(referenceNumber);
final ORCCertificate result = db.getCertificate(referenceNumber);
assertNotNull("Unable to load certificate for reference number "+referenceNumber+" from handle "+certificateHandle);
assertEquals(handle.getGPH(), result.getGPH().asSeconds(), 0.00001);
// Use some tolerance as we found differences as much as 5s between the dxtDate in the handle coming from the XML search result
// and the IssueDate field in the JSON. Both suggest to report millisecond accuracy, but dxtDate always seems to have the
@@ -17,8 +17,14 @@ import com.sap.sse.common.Util;
/**
* An interface to {@code data.orc.org}, mostly the search functionality at
* {@code https://data.orc.org/public/WPub.dll?action=SrchCert&xslp=scert.php}. It can search for certificates by a
* number of criteria, among them the issuing country, sail number, ORC reference number, or boat name.
* &lt;p&gt;
* number of criteria, among them the issuing country, sail number, ORC reference number, or boat name. Those search
* results are represented as {@link CertificateHandle} objects. Such handles can also be obtained for certificates that
* are not currently valid. See the {@code includeInvalid} parameter of the
* {@link #search(CountryCode, Integer, String, String, String, String, boolean)} method. However, handles for
* {@link CertificateHandle#isValid() invalid} certificates cannot be resolved into {@link ORCCertificate} objects
* through {@link #getCertificates(Iterable)} or {@link #getCertificates(CertificateHandle...)} because the database
* returns full certificates only if they are valid.
* <p>
*
* As the web site emits XML documents as search results, those can be parsed and turned into handles to certificates.
* An example output (excerpt):
@@ -80,8 +86,8 @@ import com.sap.sse.common.Util;
&lt;/DATA&gt;
* </pre>
*
* This also shows that some query results do not provide a valid link to a downloadable copy of the certificate. Those are
* automatically removed from the results here.
* This also shows that some query results do not provide a valid link to a downloadable copy of the certificate. Those
* are automatically removed from the results here.
*
* @author Axel Uhl (D043530)
*
@@ -97,6 +103,7 @@ public interface ORCPublicCertificateDatabase {
*/
public interface CertificateHandle {
CountryCode getIssuingCountry();
String getFileId();
Double getGPH();
String getSSSID();
UUID getDatInGID();
@@ -111,6 +118,7 @@ public interface ORCPublicCertificateDatabase {
Integer getCertType();
Boolean isOd();
Boolean isProvisional();
Boolean isValid();
}
/**
@@ -118,11 +126,12 @@ public interface ORCPublicCertificateDatabase {
* results based on that criterion. You can use "%" as wildcards in the {@code yachtName}, {@code sailNumber} and
* {@code boatClassName} parameters.
* <p>
* @param includeInvalid TODO
*
* @return an always valid, never {@code null} object which may be {@link Util#isEmpty() empty}.
*/
Iterable<CertificateHandle> search(CountryCode country, Integer yearOfIssuance, String referenceNumber,
String yachtName, String sailNumber, String boatClassName) throws Exception;
String yachtName, String sailNumber, String boatClassName, boolean includeInvalid) throws Exception;
default Iterable<ORCCertificate> getCertificates(Iterable<CertificateHandle> handles) throws Exception {
return Util.map(handles, handle->{
@@ -139,7 +148,7 @@ public interface ORCPublicCertificateDatabase {
}
default CertificateHandle getCertificateHandle(String referenceNumber) throws Exception {
return search(null, null, referenceNumber, null, null, null).iterator().next();
return search(null, null, referenceNumber, null, null, null, /* includeInvalid */ true).iterator().next();
}
ORCCertificate getCertificate(String referenceNumber) throws Exception;
@@ -21,6 +21,7 @@ import com.sap.sailing.domain.common.impl.MeterDistance;
import com.sap.sailing.domain.common.orc.ORCCertificate;
import com.sap.sailing.domain.common.orc.impl.ORCCertificateImpl;
import com.sap.sse.common.Bearing;
import com.sap.sse.common.CountryCodeFactory;
import com.sap.sse.common.Distance;
import com.sap.sse.common.Duration;
import com.sap.sse.common.Speed;
@@ -52,16 +53,15 @@ public class ORCCertificatesCollectionJSON extends AbstractORCCertificatesCollec
private static final String YACHT_NAME = "YachtName";
private static final String LOA = "LOA";
private static final String SAIL_NO = "SailNo";
private static final String CERT_NO = "CertNo";
private static final String BIN = "BIN";
private static final String NAT_AUTH = "NatAuth";
private static final String REFERENCE_NUMBER = "RefNo";
private static final String RUN = "Run";
private static final String BEAT = "Beat";
private static final String WINDWARD_LEEWARD = "WL";
private static final String LONG_DISTANCE = "OC";
private static final String CIRCULAR_RANDOM = "CR";
private static final String NON_SPINNAKER = "NS";
private static final String EMPTY_CERT_NO = " ";
private static final String WIND_SPEEDS = "WindSpeeds";
/**
@@ -99,6 +99,9 @@ public class ORCCertificatesCollectionJSON extends AbstractORCCertificatesCollec
*/
@Override
public ORCCertificate getCertificateById(String certificateId) {
String refNo = null;
String natAuth = null;
String fileId = null;
String boatName = null;
String boatclass = null;
Distance length = null;
@@ -121,6 +124,15 @@ public class ORCCertificatesCollectionJSON extends AbstractORCCertificatesCollec
String sailNumber = null;
for (Entry<Object, Object> entry : object.entrySet()) {
switch ((String) entry.getKey()) {
case REFERENCE_NUMBER:
refNo = entry.getValue() == null ? null : entry.getValue().toString();
break;
case NAT_AUTH:
natAuth = entry.getValue() == null ? null : entry.getValue().toString();
break;
case BIN:
fileId = entry.getValue() == null ? null : entry.getValue().toString();
break;
case SAIL_NO:
sailNumber = entry.getValue() == null ? null : entry.getValue().toString();
break;
@@ -256,7 +268,8 @@ public class ORCCertificatesCollectionJSON extends AbstractORCCertificatesCollec
Arrays.equals(ORCCertificate.ALLOWANCES_TRUE_WIND_ANGLES, dynamicAllowancesTrueWindAngles)
? ORCCertificate.ALLOWANCES_TRUE_WIND_ANGLES
: dynamicAllowancesTrueWindAngles,
getId(object), sailNumber, boatName, boatclass, length, gph, cdl, issueDate,
refNo, fileId, sailNumber, boatName, boatclass, length, gph, cdl,
issueDate, CountryCodeFactory.INSTANCE.getFromThreeLetterIOCName(natAuth),
velocityPredictionPerTrueWindSpeedAndAngle, beatAngles, beatVMGPredictionPerTrueWindSpeed,
beatAllowancePerTrueWindSpeed, gybeAngles, runVMGPredictionPerTrueWindSpeed,
runAllowancePerTrueWindSpeed, windwardLeewardSpeedPredictionPerTrueWindSpeed,
@@ -267,13 +280,7 @@ public class ORCCertificatesCollectionJSON extends AbstractORCCertificatesCollec
}
private String getId(JSONObject certificateAsJson) {
return getIdFromFields(certificateAsJson.get(NAT_AUTH).toString(),
certificateAsJson.get(CERT_NO) == null ? EMPTY_CERT_NO : certificateAsJson.get(CERT_NO).toString(),
certificateAsJson.get(BIN).toString());
}
private String getIdFromFields(final String natAuth, final String certNo, final String bin) {
return natAuth + certNo + bin;
return certificateAsJson.get(REFERENCE_NUMBER).toString();
}
@Override
@@ -14,10 +14,13 @@ import com.sap.sailing.domain.common.impl.MeterDistance;
import com.sap.sailing.domain.common.orc.ORCCertificate;
import com.sap.sailing.domain.common.orc.impl.ORCCertificateImpl;
import com.sap.sse.common.Bearing;
import com.sap.sse.common.CountryCode;
import com.sap.sse.common.CountryCodeFactory;
import com.sap.sse.common.Distance;
import com.sap.sse.common.Duration;
import com.sap.sse.common.Speed;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util.Triple;
import com.sap.sse.common.impl.DegreeBearingImpl;
import com.sap.sse.common.impl.MillisecondsTimePoint;
import com.sap.sse.common.impl.SecondsDurationImpl;
@@ -52,6 +55,7 @@ public class ORCCertificatesCollectionRMS extends AbstractORCCertificatesCollect
private static final String SAILNUMBER = "SAILNUMB";
private static final String BOATCLASS = "TYPE";
static final String ISSUEDATE = "DD_MM_yyYY";
static final String LOA = "LOA";
private static final String ISSUETIME = "HH:MM:SS";
private static final String RUN_ALLOWANCE = "D";
private static final String RUN_ANGLE = "DA";
@@ -61,7 +65,12 @@ public class ORCCertificatesCollectionRMS extends AbstractORCCertificatesCollect
private static final String LONG_DISTANCE = "OC";
private static final String CIRCULAR_RANDOM = "CR";
private static final String NON_SPINNAKER = "NSP";
private static final String NATCERTN_FILE_ID = "NATCERTN.FILE_ID";
private static final String ISSUING_NATIONAL_AUTHORITY = "NAT";
private static final String CERTIFICATE_NUMBER = "CERTN";
private static final String NATCERTN = ISSUING_NATIONAL_AUTHORITY+CERTIFICATE_NUMBER;
private static final String FILE_ID = "FILE_ID";
private static final String NATCERTN_FILE_ID = NATCERTN+"."+FILE_ID;
static final String REFERENCE_NUMBER = "ReferenceNo";
private static final DateFormat timestampFormat = new SimpleDateFormat("dd MM yyyy HH:mm:ssZ");
/**
@@ -108,6 +117,7 @@ public class ORCCertificatesCollectionRMS extends AbstractORCCertificatesCollect
if (certificateValues == null) {
result = null;
} else {
final String refNo = certificateValues.getValue(REFERENCE_NUMBER);
final String sailNumber = certificateValues.getValue(SAILNUMBER);
final String boatclass = certificateValues.getValue(BOATCLASS);
final String boatName = certificateValues.getValue(BOATNAME);
@@ -165,15 +175,25 @@ public class ORCCertificatesCollectionRMS extends AbstractORCCertificatesCollect
}
velocityPredictionsPerTrueWindSpeedAndAngle.put(tws, velocityPredictionPerTrueWindAngle);
}
result = new ORCCertificateImpl(certificateValues.getValue(NATCERTN_FILE_ID), sailNumber, boatName, boatclass,
length, gph, cdl, issueDate, velocityPredictionsPerTrueWindSpeedAndAngle, beatAngles, beatVMGPredictionPerTrueWindSpeed,
beatAllowancePerTrueWindSpeed, runAngles, runVMGPredictionPerTrueWindSpeed,
final Triple<CountryCode, String, String> natCertNoFileId = getIssuingNationalityCertificateNumberAndFileId(certificateValues.getValue(NATCERTN_FILE_ID));
result = new ORCCertificateImpl(refNo, natCertNoFileId.getC(), sailNumber, boatName, boatclass, length, gph,
cdl, issueDate, natCertNoFileId.getA(), velocityPredictionsPerTrueWindSpeedAndAngle, beatAngles,
beatVMGPredictionPerTrueWindSpeed, beatAllowancePerTrueWindSpeed, runAngles, runVMGPredictionPerTrueWindSpeed,
runAllowancePerTrueWindSpeed, windwardLeewardSpeedPredictionPerTrueWindSpeed,
longDistanceSpeedPredictionPerTrueWindSpeed, circularRandomSpeedPredictionPerTrueWindSpeed, nonSpinnakerSpeedPredictionPerTrueWindSpeed);
longDistanceSpeedPredictionPerTrueWindSpeed, circularRandomSpeedPredictionPerTrueWindSpeed,
nonSpinnakerSpeedPredictionPerTrueWindSpeed);
}
return result;
}
private Triple<CountryCode, String, String> getIssuingNationalityCertificateNumberAndFileId(String natCertFileId) {
final CountryCode nationalityIOCCode = CountryCodeFactory.INSTANCE
.getFromThreeLetterIOCName(natCertFileId.substring(0, ISSUING_NATIONAL_AUTHORITY.length()));
final String certNo = natCertFileId.substring(ISSUING_NATIONAL_AUTHORITY.length(), ISSUING_NATIONAL_AUTHORITY.length()+CERTIFICATE_NUMBER.length()).trim();
final String fileId = natCertFileId.substring(NATCERTN.length()+".".length()).trim();
return new Triple<>(nationalityIOCCode, certNo, fileId);
}
@Override
public Iterable<String> getCertificateIds() {
return Collections.unmodifiableCollection(certificateValuesByCertificateId.keySet());
@@ -5,7 +5,6 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
@@ -13,16 +12,27 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.sap.sailing.domain.common.orc.ORCCertificate;
import com.sap.sse.common.Util.Pair;
/**
* Represents a file in format {@code .rms} which is a simple ASCII file format, column-based, with fixed-width columns,
* defined by a header line that defines the names and for the first columns up to the column labeled
* {@link #NAME_OF_LAST_LEFT_ALIGNED_COLUMN_HEADER} also the width of the columns. The header uses column names that do
* not contain spaces, separated by one or more spaces. For the columns up to and including the column
* {@link #NAME_OF_LAST_LEFT_ALIGNED_COLUMN_HEADER} the column names are formatted left-aligned. For subsequent columns
* which contain all numeric values without spaces the column names may also be centered. For those columns parsing can
* simply split by space characters.
* <p>
* Represents a file in format {@code .rms} which is a strange ASCII file format, column-based, with fixed-width columns,
* "defined" by a header line that gives the names and for the first columns up to the column labeled
* {@link #NAME_OF_COLUMN_HEADER_WHERE_THINGS_GET_WEIRD} also the width of the columns. Starting with the column following
* {@link #NAME_OF_COLUMN_HEADER_WHERE_THINGS_GET_WEIRD}, field names may be "centered" over their character columns; for example,
* the "LOA" field is represented as " LOA " with the last space marking the separating space to the next column,
* and the values are placed somewhere within those first six "payload" columns; they may appear centered, left-aligned
* or right-aligned, and there doesn't seem to be a recognizable pattern to it.<p>
*
* To make matters worse, some columns do not contain values at all. In this case, no alternation between space and non-space
* characters can be used to determine field boundaries. At least, after the {@link #NAME_OF_COLUMN_HEADER_WHERE_THINGS_GET_WEIRD} no
* columns can contain space characters unless they are entirely empty. And in all cases do those column values after
* {@link #NAME_OF_COLUMN_HEADER_WHERE_THINGS_GET_WEIRD} have a non-space character in at least one of the columns used by the corresponding
* field name in the header, unless the field value is left blank.<p>
*
* Therefore, a good strategy seems to look for non-space characters in the columns used for the field name in the header.
* If none are found, the field is assumed to be left empty. Otherwise, from the first non-blank character found in the columns
* used by the field name in the header, all characters to the left and to the right that are no space are added to the
* field value.<p>
*
* The result of successfully parsing a {@code .rms} file is a map keyed by the sailnumber, with values being
* equal-sized maps from the column names to the {@link String} values. Additionally, the column names corresponding to
@@ -35,19 +45,18 @@ import com.sap.sailing.domain.common.orc.ORCCertificate;
*/
public class ORCCertificatesRmsImporter extends AbstractORCCertificatesImporter {
private static final String NAME_OF_LAST_LEFT_ALIGNED_COLUMN_HEADER = ORCCertificatesCollectionRMS.ISSUEDATE;
private static final String NAME_OF_COLUMN_HEADER_WHERE_THINGS_GET_WEIRD = ORCCertificatesCollectionRMS.ISSUEDATE;
public ORCCertificatesCollectionRMS read(Reader reader) throws IOException {
final BufferedReader br = new BufferedReader(reader);
final LinkedHashMap<String, Integer> columnNamesAndWidths = readColumnWidthsFromFirstLine(br.readLine());
final Iterator<String> i = columnNamesAndWidths.keySet().iterator();
final String certificateIdColumnName = i.next();
final String header = br.readLine();
final LinkedHashMap<String, Pair<Integer, Integer>> columnNamesAndFieldNameStartInHeader = readColumnHeaderFieldNameStartColumnsAndLengthFromFirstLine(header);
final Map<String, Map<String, String>> certificateValuesByCertificateId = new HashMap<>();
String line;
while ((line = br.readLine()) != null) {
if (!line.trim().isEmpty()) {
final Map<String, String> parsedLine = parseLine(line, columnNamesAndWidths);
certificateValuesByCertificateId.put(parsedLine.get(certificateIdColumnName), parsedLine);
final Map<String, String> parsedLine = parseLine(line, columnNamesAndFieldNameStartInHeader);
certificateValuesByCertificateId.put(parsedLine.get(ORCCertificatesCollectionRMS.REFERENCE_NUMBER), parsedLine);
}
}
return new ORCCertificatesCollectionRMS(certificateValuesByCertificateId);
@@ -57,41 +66,70 @@ public class ORCCertificatesRmsImporter extends AbstractORCCertificatesImporter
return read(getReaderForInputStream(in));
}
private Map<String, String> parseLine(final String line, LinkedHashMap<String, Integer> columnNamesAndWidths) {
assert columnNamesAndWidths != null;
private Map<String, String> parseLine(final String line, LinkedHashMap<String, Pair<Integer, Integer>> columnNamesAndFieldNameStartAndLengthInHeader) {
assert columnNamesAndFieldNameStartAndLengthInHeader != null;
final Map<String, String> result = new LinkedHashMap<>();
int start=0;
boolean splitBySpaceMode = false; // start using column names; switch to splitting by spaces when NAME_OF_LAST_LEFT_ALIGNED_COLUMN_HEADER has been found
for (final Entry<String, Integer> columnNameAndWidth : columnNamesAndWidths.entrySet()) {
boolean leftAligned = true;
for (final Entry<String, Pair<Integer, Integer>> columnNameAndFieldNameStartInHeader : columnNamesAndFieldNameStartAndLengthInHeader.entrySet()) {
final int start = columnNameAndFieldNameStartInHeader.getValue().getA();
final int end;
if (splitBySpaceMode) {
boolean foundSpace = false;
int i;
for (i=start; i<line.length() && (!foundSpace || line.charAt(i) == ' '); i++) {
if (!foundSpace) {
foundSpace = line.charAt(i) == ' ';
}
}
end = i;
final String value;
if (leftAligned) {
end = start+columnNameAndFieldNameStartInHeader.getValue().getB();
value = line.substring(start, end).trim();
} else {
end = start+columnNameAndWidth.getValue();
end = start+columnNameAndFieldNameStartInHeader.getKey().length();
value = expandFromNonSpaceCharacterInRange(line, start, end);
}
result.put(columnNameAndWidth.getKey(), line.substring(start, end).trim());
if (columnNameAndWidth.getKey().equals(NAME_OF_LAST_LEFT_ALIGNED_COLUMN_HEADER)) {
splitBySpaceMode = true;
result.put(columnNameAndFieldNameStartInHeader.getKey(), value);
if (columnNameAndFieldNameStartInHeader.getKey().equals(NAME_OF_COLUMN_HEADER_WHERE_THINGS_GET_WEIRD)) {
leftAligned = false;
}
start = end;
}
return result;
}
private LinkedHashMap<String, Integer> readColumnWidthsFromFirstLine(final String readLine) {
final LinkedHashMap<String, Integer> result = new LinkedHashMap<>();
/**
* If only space characters are found in {@code line} starting at index {@code start} and ending before index {@code end},
* the result is {@code null}. Otherwise, starting at any non-space character in the range, expansion to the left and right is
* performed, collecting all contiguous non-space characters.
*/
private String expandFromNonSpaceCharacterInRange(String line, int start, int end) {
final String result;
if (start >= line.length()) {
result = null;
} else {
int left = start;
// search for first non-whitespace from start looking right:
while (left<line.length() && left < end && left < line.length() && Character.isWhitespace(line.charAt(left))) {
left++;
}
if (left >= end) {
result = null;
} else {
int right = left+1;
// expand left:
while (left > 0 && Character.isWhitespace(line.charAt(left-1))) {
left--;
}
// expand right:
while (right < line.length() && !Character.isWhitespace(line.charAt(right))) {
right++;
}
result = line.substring(left, right);
}
}
return result;
}
private LinkedHashMap<String, Pair<Integer, Integer>> readColumnHeaderFieldNameStartColumnsAndLengthFromFirstLine(final String readLine) {
final LinkedHashMap<String, Pair<Integer, Integer>> result = new LinkedHashMap<>();
final Pattern p = Pattern.compile("([^ ]+ *)");
final Matcher m = p.matcher(readLine);
int start = 0;
while (m.find(start)) {
result.put(m.group(1).trim(), m.group(1).length());
final String fieldNameTrimmed = m.group(1).trim();
result.put(fieldNameTrimmed, new Pair<>(m.start(1), m.group(1).length()));
start += m.group(1).length();
}
return result;
@@ -77,6 +77,7 @@ public class ORCPublicCertificateDatabaseImpl implements ORCPublicCertificateDat
private static final String ROOT_ELEMENT = "ROOT";
private static final String DATA_ELEMENT = "DATA";
private static final String ROW_ELEMENT = "ROW";
private static final String DXT_SUFFIX = ".dxt";
private static final DateTimeFormatter[] DATE_FORMATTERS =
new DateTimeFormatter[] {
@@ -100,6 +101,7 @@ public class ORCPublicCertificateDatabaseImpl implements ORCPublicCertificateDat
*/
private static class CertificateHandleImpl implements CertificateHandle {
private final CountryCode issuingCountry;
private final String fileId;
private final String sssid;
private final Double gph;
private final UUID datInGID;
@@ -114,12 +116,15 @@ public class ORCPublicCertificateDatabaseImpl implements ORCPublicCertificateDat
private final Integer certType;
private final Boolean isOneDesign;
private final Boolean isProvisional;
private final Boolean isValid;
public CertificateHandleImpl(CountryCode issuingCountry, Double gph, String sssid, UUID datInGID,
String referenceNumber, String yachtName, String sailNumber, String boatClassName, String designer,
String builder, Integer yearBuilt, TimePoint issueDate, Integer certType, Boolean oneDesign, Boolean isProvisional) {
public CertificateHandleImpl(CountryCode issuingCountry, String fileId, Double gph, String sssid,
UUID datInGID, String referenceNumber, String yachtName, String sailNumber, String boatClassName,
String designer, String builder, Integer yearBuilt, TimePoint issueDate, Integer certType,
Boolean oneDesign, Boolean isProvisional, Boolean isValid) {
super();
this.issuingCountry = issuingCountry;
this.fileId = fileId;
this.gph = gph;
this.sssid = sssid;
this.datInGID = datInGID;
@@ -134,6 +139,7 @@ public class ORCPublicCertificateDatabaseImpl implements ORCPublicCertificateDat
this.certType = certType;
this.isOneDesign = oneDesign;
this.isProvisional = isProvisional;
this.isValid = isValid;
}
@Override
@@ -166,6 +172,11 @@ public class ORCPublicCertificateDatabaseImpl implements ORCPublicCertificateDat
return issuingCountry;
}
@Override
public String getFileId() {
return fileId;
}
@Override
public Double getGPH() {
return gph;
@@ -235,6 +246,11 @@ public class ORCPublicCertificateDatabaseImpl implements ORCPublicCertificateDat
public Boolean isOd() {
return isOneDesign;
}
@Override
public Boolean isValid() {
return isValid;
}
@Override
public String toString() {
@@ -242,13 +258,14 @@ public class ORCPublicCertificateDatabaseImpl implements ORCPublicCertificateDat
+ ", datInGID=" + datInGID + ", referenceNumber=" + referenceNumber + ", yachtName=" + yachtName
+ ", sailNumber=" + sailNumber + ", boatClassName=" + boatClassName + ", designer=" + designer
+ ", builder=" + builder + ", yearBuilt=" + yearBuilt + ", issueDate=" + issueDate + ", certType="
+ certType + ", isOneDesign=" + isOneDesign + ", isProvisional=" + isProvisional + "]";
+ certType + ", isOneDesign=" + isOneDesign + ", isProvisional=" + isProvisional +
", isValid=" + isValid + "]";
}
}
@Override
public Iterable<CertificateHandle> search(CountryCode issuingCountry, Integer yearOfIssuance, String referenceNumber,
String yachtName, String sailNumber, String boatClassName) throws Exception {
String yachtName, String sailNumber, String boatClassName, boolean includeInvalid) throws Exception {
final Set<ORCPublicCertificateDatabase.CertificateHandle> result = new HashSet<>();
final HttpClient client = new SystemDefaultHttpClient();
final List<NameValuePair> params = new ArrayList<>();
@@ -285,8 +302,11 @@ public class ORCPublicCertificateDatabaseImpl implements ORCPublicCertificateDat
for (int rowNodeIndex=0; rowNodeIndex<rowNodes.getLength(); rowNodeIndex++) {
final Node rowNode = rowNodes.item(rowNodeIndex);
if (rowNode.getNodeName().equals(ROW_ELEMENT)) {
// TODO only include results that have <CanSelect>True</CanSelect> CERTIFICATE_IS_ACTIVE
result.add(parseHandle(rowNode));
final CertificateHandle certificateHandle = parseHandle(rowNode);
// certificates can only be obtained if they are currently valid, so ignore expired ones
if (includeInvalid || certificateHandle.isValid()) {
result.add(certificateHandle);
}
}
}
}
@@ -302,6 +322,7 @@ public class ORCPublicCertificateDatabaseImpl implements ORCPublicCertificateDat
private CertificateHandle parseHandle(Node rowNode) throws DOMException, ParseException {
assert rowNode.getNodeName().equals(ROW_ELEMENT);
CountryCode issuingCountry = null;
String fileId = null;
String sssid = null;
Double gph = null;
UUID datInGID = null;
@@ -316,6 +337,7 @@ public class ORCPublicCertificateDatabaseImpl implements ORCPublicCertificateDat
TimePoint issueDate = null;
Boolean isOneDesign = null;
Boolean isProvisional = null;
Boolean isValid = null;
for (int i=0; i<rowNode.getChildNodes().getLength(); i++) {
final Node child = rowNode.getChildNodes().item(i);
switch (child.getNodeName()) {
@@ -352,6 +374,12 @@ public class ORCPublicCertificateDatabaseImpl implements ORCPublicCertificateDat
case "dxtDate":
issueDate = new MillisecondsTimePoint(parseDate(child.getTextContent())); // assume UTC
break;
case "dxtName":
final String fileIdWithOptionalDxtSuffix = child.getTextContent();
fileId = fileIdWithOptionalDxtSuffix.toLowerCase().endsWith(DXT_SUFFIX) ?
fileIdWithOptionalDxtSuffix.substring(0, fileIdWithOptionalDxtSuffix.length()-DXT_SUFFIX.length()) :
fileIdWithOptionalDxtSuffix;
break;
case "CertType":
certType = Integer.valueOf(child.getTextContent().trim());
break;
@@ -364,10 +392,13 @@ public class ORCPublicCertificateDatabaseImpl implements ORCPublicCertificateDat
case "Provisional":
isProvisional = Boolean.valueOf(child.getTextContent());
break;
case "CanSelect":
isValid = Boolean.valueOf(child.getTextContent());
break;
}
}
return new CertificateHandleImpl(issuingCountry, gph, sssid, datInGID, referenceNumber, yachtName,
sailNumber, boatClassName, designer, builder, yearBuilt, issueDate, certType, isOneDesign, isProvisional);
return new CertificateHandleImpl(issuingCountry, fileId, gph, sssid, datInGID, referenceNumber,
yachtName, sailNumber, boatClassName, designer, builder, yearBuilt, issueDate, certType, isOneDesign, isProvisional, isValid);
}
@Override
@@ -525,13 +556,13 @@ public class ORCPublicCertificateDatabaseImpl implements ORCPublicCertificateDat
*/
private Iterable<CertificateHandle> fuzzySearchVaryingBoatClassName(final String yachtName, final String sailNumber, final BoatClass boatClass) throws Exception {
String successfulBoatClassName = boatClass==null?null:boatClass.getName();
Iterable<CertificateHandle> certificateHandles = search(/* issuingCountry */ null, /* yearOfIssuance */ null, /* referenceNumber */ null, yachtName, sailNumber, successfulBoatClassName);
Iterable<CertificateHandle> certificateHandles = search(/* issuingCountry */ null, /* yearOfIssuance */ null, /* referenceNumber */ null, yachtName, sailNumber, successfulBoatClassName, /* includeInvalid */ false);
if (!containsHandleForCurrentYear(certificateHandles) && successfulBoatClassName != null) {
if (yachtName != null || sailNumber != null) {
logger.fine(()->"Nothing found; removing boat class name restriction "+boatClass.getName());
// try without boat class restriction
successfulBoatClassName = null;
certificateHandles = search(/* issuingCountry */ null, /* yearOfIssuance */ null, /* referenceNumber */ null, yachtName, sailNumber, successfulBoatClassName);
certificateHandles = search(/* issuingCountry */ null, /* yearOfIssuance */ null, /* referenceNumber */ null, yachtName, sailNumber, successfulBoatClassName, /* includeInvalid */ false);
} else {
logger.fine(()->"No current certificates found for boat class "+boatClass.getName()+
" but yacht name and sail number are not specified either; giving up.");
@@ -12,6 +12,7 @@ import com.google.gwt.user.client.rpc.SerializationStreamWriter;
import com.sap.sailing.domain.common.orc.ORCCertificate;
import com.sap.sailing.domain.common.orc.impl.ORCCertificateImpl;
import com.sap.sse.common.Bearing;
import com.sap.sse.common.CountryCode;
import com.sap.sse.common.Distance;
import com.sap.sse.common.Duration;
import com.sap.sse.common.Speed;
@@ -27,7 +28,9 @@ public class ORCCertificateImpl_CustomFieldSerializer extends CustomFieldSeriali
public static void serialize(SerializationStreamWriter streamWriter, ORCCertificateImpl instance)
throws SerializationException {
streamWriter.writeString(instance.getId());
streamWriter.writeString(instance.getReferenceNumber());
streamWriter.writeString(instance.getFileId());
streamWriter.writeObject(instance.getIssuingCountry());
streamWriter.writeString(instance.getSailNumber());
streamWriter.writeString(instance.getBoatName());
streamWriter.writeString(instance.getBoatClassName());
@@ -65,7 +68,9 @@ public class ORCCertificateImpl_CustomFieldSerializer extends CustomFieldSeriali
public static ORCCertificateImpl instantiate(SerializationStreamReader streamReader)
throws SerializationException {
final String idConsistingOfNatAuthCertNoAndBIN = streamReader.readString();
final String referenceNumber = streamReader.readString();
final String fileId = streamReader.readString();
final CountryCode issuingCountry = (CountryCode) streamReader.readObject();
final String sailnumber = streamReader.readString();
final String boatName = streamReader.readString();
final String boatclass = streamReader.readString();
@@ -101,12 +106,12 @@ public class ORCCertificateImpl_CustomFieldSerializer extends CustomFieldSeriali
@SuppressWarnings("unchecked")
final List<Speed> readTWSs = (List<Speed>) streamReader.readObject();
final Speed[] trueWindSpeeds = readTWSs == null ? ORCCertificate.ALLOWANCES_TRUE_WIND_SPEEDS : readTWSs.toArray(new Speed[readTWSs.size()]);
return new ORCCertificateImpl(trueWindSpeeds,trueWindAngles,idConsistingOfNatAuthCertNoAndBIN, sailnumber, boatName, boatclass, length, gph,
cdl, issueDate, velocityPredictionsPerTrueWindSpeedAndAngle, beatAngles,
beatVMGPredictionPerTrueWindSpeed, beatAllowancePerTrueWindSpeed, runAngles,
return new ORCCertificateImpl(trueWindSpeeds,trueWindAngles,referenceNumber, fileId, sailnumber, boatName, boatclass, length,
gph, cdl, issueDate, issuingCountry,
velocityPredictionsPerTrueWindSpeedAndAngle, beatAngles, beatVMGPredictionPerTrueWindSpeed,
beatAllowancePerTrueWindSpeed, runAngles,
runVMGPredictionPerTrueWindSpeed, runAllowancePerTrueWindSpeed,
windwardLeewardSpeedPredictionsPerTrueWindSpeed, longDistanceSpeedPredictionsPerTrueWindSpeed,
circularRandomSpeedPredictionsPerTrueWindSpeed, nonSpinnakerSpeedPredictionsPerTrueWindSpeed);
windwardLeewardSpeedPredictionsPerTrueWindSpeed, longDistanceSpeedPredictionsPerTrueWindSpeed, circularRandomSpeedPredictionsPerTrueWindSpeed, nonSpinnakerSpeedPredictionsPerTrueWindSpeed);
}
@Override
@@ -9981,7 +9981,7 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet
String referenceNumber, String yachtName, String sailNumber, String boatClassName) throws Exception {
final ORCPublicCertificateDatabase db = ORCPublicCertificateDatabase.INSTANCE;
final Set<ORCCertificate> result = new HashSet<>();
final Iterable<CertificateHandle> searchResult = db.search(country, yearOfIssuance, referenceNumber, yachtName, sailNumber, boatClassName);
final Iterable<CertificateHandle> searchResult = db.search(country, yearOfIssuance, referenceNumber, yachtName, sailNumber, boatClassName, /* includeInvalid */ false);
Util.addAll(
db.getCertificates(searchResult),
result);
@@ -17,6 +17,8 @@ import com.sap.sailing.server.gateway.deserialization.JsonDeserializationExcepti
import com.sap.sailing.server.gateway.deserialization.JsonDeserializer;
import com.sap.sailing.server.gateway.serialization.racelog.impl.ORCCertificateJsonSerializer;
import com.sap.sse.common.Bearing;
import com.sap.sse.common.CountryCode;
import com.sap.sse.common.CountryCodeFactory;
import com.sap.sse.common.Distance;
import com.sap.sse.common.Duration;
import com.sap.sse.common.Speed;
@@ -29,6 +31,9 @@ public class ORCCertificateJsonDeserializer implements JsonDeserializer<ORCCerti
@Override
public ORCCertificate deserialize(JSONObject json) throws JsonDeserializationException {
String fileId = (String) json.get(ORCCertificateJsonSerializer.ORC_CERTIFICATE_FILE_ID);
CountryCode issuingCountry = CountryCodeFactory.INSTANCE.getFromThreeLetterIOCName(
((String) json.get(ORCCertificateJsonSerializer.ORC_CERTIFICATE_ISSUING_COUNTRY_IOC)));
String sailnumber = (String) json.get(ORCCertificateJsonSerializer.ORC_CERTIFICATE_SAILNUMBER);
String boatName = (String) json.get(ORCCertificateJsonSerializer.ORC_CERTIFICATE_BOATNAME);
String boatclass = (String) json.get(ORCCertificateJsonSerializer.ORC_CERTIFICATE_BOATCLASS);
@@ -103,13 +108,14 @@ public class ORCCertificateJsonDeserializer implements JsonDeserializer<ORCCerti
velocityPredictionsPerTrueWindSpeedAndAngle.put(tws,
velocityPredictionAtCurrentTrueWindSpeedPerTrueWindAngle);
}
final String idConsistingOfNatAuthCertNoAndBIN = json.get(ORCCertificateJsonSerializer.ORC_CERTIFICATE_ID).toString();
final ORCCertificate certificate = new ORCCertificateImpl(trueWindSpeeds,trueWindAngles,idConsistingOfNatAuthCertNoAndBIN, sailnumber,
boatName, boatclass, length, gph, cdl, issueDate, velocityPredictionsPerTrueWindSpeedAndAngle,
beatAngles, beatVMGPredictionPerTrueWindSpeed, beatAllowancePerTrueWindSpeed, runAngles,
runVMGPredictionPerTrueWindSpeed, runAllowancePerTrueWindSpeed,
windwardLeewardSpeedPredictionPerTrueWindSpeed, longDistanceSpeedPredictionPerTrueWindSpeed,
circularRandomSpeedPredictionPerTrueWindSpeed, nonSpinnakerSpeedPredictionPerTrueWindSpeed);
final String referenceNumber = json.get(ORCCertificateJsonSerializer.ORC_CERTIFICATE_ID).toString();
final ORCCertificate certificate = new ORCCertificateImpl(trueWindSpeeds, trueWindAngles, referenceNumber,
fileId, sailnumber, boatName, boatclass, length, gph, cdl, issueDate, issuingCountry,
velocityPredictionsPerTrueWindSpeedAndAngle, beatAngles, beatVMGPredictionPerTrueWindSpeed,
beatAllowancePerTrueWindSpeed, runAngles, runVMGPredictionPerTrueWindSpeed,
runAllowancePerTrueWindSpeed, windwardLeewardSpeedPredictionPerTrueWindSpeed,
longDistanceSpeedPredictionPerTrueWindSpeed, circularRandomSpeedPredictionPerTrueWindSpeed,
nonSpinnakerSpeedPredictionPerTrueWindSpeed);
return certificate;
}
@@ -23,6 +23,8 @@ public class ORCCertificateJsonSerializer implements JsonSerializer<ORCCertifica
public static final String ORC_CERTIFICATE_CIRCULAR_RANDOM_SPEED_PREDICTIONS = "circularRandomSpeedPredictions";
public static final String ORC_CERTIFICATE_NON_SPINNAKER_SPEED_PREDICTIONS = "nonSpinnakerSpeedPredictions";
public static final String ORC_CERTIFICATE_TWA_SPEED_PREDICTIONS = "twaSpeedPredictions";
public static final String ORC_CERTIFICATE_FILE_ID = "fileId";
public static final String ORC_CERTIFICATE_ISSUING_COUNTRY_IOC = "issuingCountryIOC";
public static final String ORC_CERTIFICATE_SAILNUMBER = "sailNumber";
public static final String ORC_CERTIFICATE_BOATNAME = "boatName";
public static final String ORC_CERTIFICATE_BOATCLASS = "boatClass";
@@ -55,7 +57,9 @@ public class ORCCertificateJsonSerializer implements JsonSerializer<ORCCertifica
@Override
public JSONObject serialize(ORCCertificate certificate) {
JSONObject result = new JSONObject();
result.put(ORC_CERTIFICATE_ID, certificate.getId());
result.put(ORC_CERTIFICATE_ID, certificate.getReferenceNumber());
result.put(ORC_CERTIFICATE_ISSUING_COUNTRY_IOC, certificate.getIssuingCountry().getThreeLetterIOCCode());
result.put(ORC_CERTIFICATE_FILE_ID, certificate.getFileId());
result.put(ORC_CERTIFICATE_SAILNUMBER, certificate.getSailNumber());
result.put(ORC_CERTIFICATE_BOATNAME, certificate.getBoatName());
result.put(ORC_CERTIFICATE_BOATCLASS, certificate.getBoatClassName());