Merge branch 'main' into bug6095

This commit is contained in:
Axel Uhl
2025-02-21 17:52:52 +01:00
51 changed files with 2989 additions and 5 deletions
@@ -24,7 +24,7 @@ import java.util.Map;
*
*/
public abstract class AbstractSailNumberCanonicalizerAndMatcher<CompetitorType> {
protected static final String sailIdRegexpPattern = "^([A-Z][A-Z][A-Z])\\s*[^0-9]*([0-9]*)$";
protected static final String sailIdRegexpPattern = "^([A-Z][A-Z][A-Z])?\\s*[^0-9]*([0-9]*)$";
public static class SailNumberMatch {
private final String iocCode;
@@ -43,7 +43,7 @@ public abstract class AbstractSailNumberCanonicalizerAndMatcher<CompetitorType>
}
/**
* Parses a sail number into a nationality code expected at the beginning of the string,
* Parses a sail number into a nationality code expected at the beginning of the string but possibly empty,
* and a numeric part assumed to be at the end of the string. Before matching, the {@code sailId}
* string will be {@link String#trim() trimmed}, removing leading and trailing whitespace. If
* the parameter matches the {@link #sailIdRegexpPattern} then a valid {@link SailNumberMatch}
@@ -102,10 +102,10 @@ public abstract class AbstractSailNumberCanonicalizerAndMatcher<CompetitorType>
}
public Map<String, CompetitorType> canonicalizeLeaderboardSailIDs(final Iterable<CompetitorType> competitors) {
Map<String, CompetitorType> result = new HashMap<>();
final Map<String, CompetitorType> result = new HashMap<>();
for (final CompetitorType competitor : competitors) {
final String competitorIdentifyingText = getCompetitorIdentifyingText(competitor);
String canonicalizedSailID = canonicalizeSailID(competitorIdentifyingText.trim(), getThreeLetterIocCountryCode(competitor).trim());
final String canonicalizedSailID = canonicalizeSailID(competitorIdentifyingText.trim(), getThreeLetterIocCountryCode(competitor).trim());
if (canonicalizedSailID != null) {
result.put(canonicalizedSailID, competitor);
}
@@ -68,6 +68,7 @@
<plugin id="com.sap.sailing.resultimport" autoStart="true" startLevel="4" />
<plugin id="com.sap.sailing.sailti.resultimport" autoStart="true" startLevel="4" />
<plugin id="com.sap.sailing.sailwave.resultimport" autoStart="true" startLevel="4" />
<plugin id="com.sap.sailing.sailwave.html.resultimport" autoStart="true" startLevel="4" />
<plugin id="com.sap.sailing.server" autoStart="true" startLevel="5" />
<plugin id="com.sap.sailing.server.gateway" autoStart="true" startLevel="5" />
<plugin id="com.sap.sailing.shared.server" autoStart="true" startLevel="5" />
+4
View File
@@ -133,6 +133,10 @@ Computes leaderboard information for sailing races and offers RESTful APIs to al
id="com.sap.sailing.sailwave.resultimport"
version="0.0.0"/>
<plugin
id="com.sap.sailing.sailwave.html.resultimport"
version="0.0.0"/>
<plugin
id="com.sap.sailing.manage2sail.resultimport"
version="0.0.0"/>
@@ -1,5 +1,6 @@
package com.sap.sailing.gwt.ui.adminconsole;
import java.util.Collections;
import java.util.List;
import java.util.Set;
@@ -82,6 +83,7 @@ public class ResultImportUrlsListComposite extends Composite {
sailingServiceWrite.getUrlResultProviderNamesAndOptionalSampleURL(new AsyncCallback<List<Pair<String, String>>>() {
@Override
public void onSuccess(List<Pair<String, String>> urlProviderNamesAndOptionalSampleURL) {
Collections.sort(urlProviderNamesAndOptionalSampleURL, (u1, u2)->u1.getA().compareTo(u2.getA()));
urlProviderListBox.clear();
urlProviderListBox.addItem(stringMessages.pleaseSelectAURLProvider());
for (Pair<String, String> urlProviderNameAndSampleURL : urlProviderNamesAndOptionalSampleURL) {
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="src" path="src">
<attributes>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" path="resources">
<attributes>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="bin"/>
</classpath>
@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>com.sap.sailing.sailwave.html.resultimport.test</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.pde.ManifestBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.pde.SchemaBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.pde.PluginNature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>
@@ -0,0 +1,2 @@
eclipse.preferences.version=1
encoding/<project>=UTF-8
@@ -0,0 +1,7 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
org.eclipse.jdt.core.compiler.compliance=1.8
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.release=disabled
org.eclipse.jdt.core.compiler.source=1.8
@@ -0,0 +1,3 @@
eclipse.preferences.version=1
pluginProject.extensions=false
resolve.requirebundle=false
@@ -0,0 +1,10 @@
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Test
Bundle-SymbolicName: com.sap.sailing.sailwave.html.resultimport.test
Bundle-Version: 1.0.0.qualifier
Bundle-Vendor: SAP
Fragment-Host: com.sap.sailing.sailwave.html.resultimport
Require-Bundle: org.junit
Automatic-Module-Name: com.sap.sailing.sailwave.html.resultimport.test
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
@@ -0,0 +1,6 @@
source.. = src/,\
resources/
output.. = bin/
bin.includes = META-INF/,\
.,\
resources/
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>root</artifactId>
<groupId>com.sap.sailing</groupId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>com.sap.sailing.sailwave.html.resultimport.test</artifactId>
<packaging>eclipse-test-plugin</packaging>
</project>
@@ -0,0 +1,542 @@
<!DOCTYPE html>
<html><head>
<meta http-equiv="content-type" content="text/html; charset=Big5">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="description" content="sailwave results">
<meta name="viewport" content="width=device-width">
<title>Sailwave results for 2025 29er Asian Championship at 2025</title>
<style type="text/css">
body {font: 72% arial, helvetica, sans-serif; text-align: center;}
.hardleft {text-align: left; float: left; margin: 15px 0 15px 25px;}
.hardright {text-align: right; float: right; margin: 15px 25px 15px 0;}
table {text-align: left; margin: 0px auto 30px auto; font-size: 1em; border-collapse: collapse; border: 1px #fff solid;}
td, th {padding: 4px; border: 2px #fff solid; vertical-align: top;}
.caption {padding: 5px; text-align: center; border: 0; font-weight: bold;}
h1 {font-size: 1.6em;}
h2 {font-size: 1.4em;}
h3 {font-size: 1.2em;}
p {text-align: center;}
th {background-color: #aaf;}
.contents {text-align: left; margin-left: 20%;}
.even {background-color: #bbf;}
.odd {background-color: #ddf;}
.natflag {border: 1px #999 solid;}
.nattext {font-size: 0.8em;}
.place1 {font-weight: bold; background-color: #ffffaa;}
.place2 {font-weight: bold; background-color: #aaaaff;}
.place3 {font-weight: bold; background-color: #ffaaaa;}
.placen {}
</style>
<script type="text/javascript">
</script>
</head>
<body>
<header>
</header>
<div id="wrap">
<h1>2025 29er Asian Championship</h1>
<h2></h2>
<div style="clear:both;"></div>
<style>
div.applicant-break {page-break-after:always;}
</style>
<h3 class="seriestitle">Results are provisional as of 19:09 on February 15, 2025</h3>
<h3 class="summarytitle">Overall</h3>
<div class="caption summarycaption">Sailed: 12, Discards: 2, To count: 10, Entries: 19, Scoring system: Appendix A</div>
<table class="summarytable" cellspacing="0" cellpadding="0" border="0">
<colgroup span="20">
<col class="rank">
<col class="class">
<col class="division">
<col class="nat">
<col class="sailno">
<col class="helmname">
<col class="race">
<col class="race">
<col class="race">
<col class="race">
<col class="race">
<col class="race">
<col class="race">
<col class="race">
<col class="race">
<col class="race">
<col class="race">
<col class="race">
<col class="total">
<col class="nett">
</colgroup>
<thead>
<tr class="titlerow">
<th>Rank</th>
<th>Class</th>
<th>Divisions</th>
<th>Nat</th>
<th>Sail No</th>
<th>Sailors</th>
<th>R1</th>
<th>R2</th>
<th>R3</th>
<th>R4</th>
<th>R5</th>
<th>R6</th>
<th>R7</th>
<th>R8</th>
<th>R9</th>
<th>R10</th>
<th>R11</th>
<th>R12</th>
<th>Total</th>
<th>Nett</th>
</tr>
</thead>
<tbody>
<tr class="odd summaryrow">
<td>1st</td>
<td>29er</td>
<td>Mixed</td>
<td><img class="natflag" title="HKG" src="29er_files/HKG.jpg"></td>
<td>3356</td>
<td>Raphael Mak, Tiffany Mak</td>
<td class="rank1">1.0</td>
<td class="rank1">1.0</td>
<td class="rank1">1.0</td>
<td class="rank2">2.0</td>
<td class="rank3">(3.0)</td>
<td class="rank3">3.0</td>
<td class="rank2">2.0</td>
<td class="rank1">1.0</td>
<td>(6.0)</td>
<td class="rank1">1.0</td>
<td class="rank2">2.0</td>
<td class="rank2">2.0</td>
<td>25.0</td>
<td>16.0</td>
</tr>
<tr class="even summaryrow">
<td>2nd</td>
<td>29er</td>
<td>Men</td>
<td><img class="natflag" title="USA" src="29er_files/USA.jpg"></td>
<td>20</td>
<td>Kevin Cason, Holland Vierling</td>
<td>(7.0)</td>
<td>(6.0)</td>
<td class="rank3">3.0</td>
<td class="rank1">1.0</td>
<td class="rank2">2.0</td>
<td class="rank1">1.0</td>
<td>5.0</td>
<td class="rank3">3.0</td>
<td class="rank2">2.0</td>
<td class="rank2">2.0</td>
<td class="rank1">1.0</td>
<td class="rank3">3.0</td>
<td>36.0</td>
<td>23.0</td>
</tr>
<tr class="odd summaryrow">
<td>3rd</td>
<td>29er</td>
<td>Mixed</td>
<td><img class="natflag" title="THA" src="29er_files/THA.jpg"></td>
<td>621</td>
<td>Patcharaphan Ongkaloy, Kan Kachachuen</td>
<td>4.0</td>
<td class="rank3">3.0</td>
<td>(6.0)</td>
<td class="rank3">3.0</td>
<td>5.0</td>
<td class="rank2">2.0</td>
<td>6.0</td>
<td class="rank2">2.0</td>
<td>4.0</td>
<td>(7.0)</td>
<td>5.0</td>
<td>4.0</td>
<td>51.0</td>
<td>38.0</td>
</tr>
<tr class="even summaryrow">
<td>4th</td>
<td>29er</td>
<td>Junior , Men</td>
<td><img class="natflag" title="HKG" src="29er_files/HKG.jpg"></td>
<td>3354</td>
<td>Ethan Kiu, Louis Polson</td>
<td class="rank2">2.0</td>
<td>(8.0)</td>
<td class="rank2">2.0</td>
<td>7.0</td>
<td>(9.0)</td>
<td>7.0</td>
<td class="rank1">1.0</td>
<td>6.0</td>
<td class="rank3">3.0</td>
<td>8.0</td>
<td>4.0</td>
<td class="rank1">1.0</td>
<td>58.0</td>
<td>41.0</td>
</tr>
<tr class="odd summaryrow">
<td>5th</td>
<td>29er</td>
<td>Women</td>
<td><img class="natflag" title="HKG" src="29er_files/HKG.jpg"></td>
<td>2840</td>
<td>Sienne Thiry, Tomoe Thiry</td>
<td class="rank3">3.0</td>
<td>5.0</td>
<td>7.0</td>
<td>4.0</td>
<td>4.0</td>
<td>4.0</td>
<td>4.0</td>
<td>(20.0 DSQ)</td>
<td>5.0</td>
<td>(9.0)</td>
<td>8.0</td>
<td>5.0</td>
<td>78.0</td>
<td>49.0</td>
</tr>
<tr class="even summaryrow">
<td>6th</td>
<td>29er</td>
<td>Men</td>
<td><img class="natflag" title="HKG" src="29er_files/HKG.jpg"></td>
<td>3398</td>
<td>Ayden Pang, Xu Peng CHEN</td>
<td>6.0</td>
<td>4.0</td>
<td>4.0</td>
<td>(11.0)</td>
<td>6.0</td>
<td>11.0</td>
<td>(20.0 DSQ)</td>
<td>5.0</td>
<td>7.0</td>
<td>5.0</td>
<td class="rank3">3.0</td>
<td>7.0</td>
<td>89.0</td>
<td>58.0</td>
</tr>
<tr class="odd summaryrow">
<td>7th</td>
<td>29er</td>
<td>Mixed</td>
<td><img class="natflag" title="HKG" src="29er_files/HKG.jpg"></td>
<td>2566</td>
<td>Chloe Kong, Ethan Kong</td>
<td>5.0</td>
<td>11.0</td>
<td>5.0</td>
<td>5.0</td>
<td>8.0</td>
<td>6.0</td>
<td>(20.0 SCP50)</td>
<td>(20.0 DSQ)</td>
<td>9.0</td>
<td>6.0</td>
<td>6.0</td>
<td>6.0</td>
<td>107.0</td>
<td>67.0</td>
</tr>
<tr class="even summaryrow">
<td>8th</td>
<td>29er</td>
<td>Junior , Women</td>
<td><img class="natflag" title="HKG" src="29er_files/HKG.jpg"></td>
<td>3355</td>
<td>ZiHan Liu, Bernice Pang</td>
<td>8.0</td>
<td>(12.0)</td>
<td>10.0</td>
<td>(12.0)</td>
<td>11.0</td>
<td>9.0</td>
<td class="rank3">3.0</td>
<td>4.0</td>
<td class="rank1">1.0</td>
<td class="rank3">3.0</td>
<td>10.0</td>
<td>9.0</td>
<td>92.0</td>
<td>68.0</td>
</tr>
<tr class="odd summaryrow">
<td>9th</td>
<td>29er</td>
<td>Junior , Men</td>
<td><img class="natflag" title="HKG" src="29er_files/HKG.jpg"></td>
<td>3405</td>
<td>Nicholas Hon, Kaden Chan</td>
<td>(12.0)</td>
<td class="rank2">2.0</td>
<td>8.0</td>
<td>10.0</td>
<td>(15.0)</td>
<td>12.0</td>
<td>12.0</td>
<td>7.0</td>
<td>10.0</td>
<td>4.0</td>
<td>7.0</td>
<td>8.0</td>
<td>107.0</td>
<td>80.0</td>
</tr>
<tr class="even summaryrow">
<td>10th</td>
<td>29er</td>
<td>Women</td>
<td><img class="natflag" title="HKG" src="29er_files/HKG.jpg"></td>
<td>3393</td>
<td>Kristen Hwang, McCarley Wong</td>
<td>9.0</td>
<td>7.0</td>
<td>12.0</td>
<td>8.0</td>
<td>7.0</td>
<td>5.0</td>
<td>7.0</td>
<td>10.0</td>
<td>8.0</td>
<td>(14.0)</td>
<td>(13.0)</td>
<td>10.0</td>
<td>110.0</td>
<td>83.0</td>
</tr>
<tr class="odd summaryrow">
<td>11th</td>
<td>29er</td>
<td>Men</td>
<td><img class="natflag" title="HKG" src="29er_files/HKG.jpg"></td>
<td>3055</td>
<td>Yan Cheuk NG, Kam Hing HU</td>
<td>11.0</td>
<td>9.0</td>
<td>9.0</td>
<td>6.0</td>
<td>(13.0)</td>
<td>10.0</td>
<td>9.0</td>
<td>(20.0 DSQ)</td>
<td>12.0</td>
<td>11.0</td>
<td>9.0</td>
<td>11.0</td>
<td>130.0</td>
<td>97.0</td>
</tr>
<tr class="even summaryrow">
<td>12th</td>
<td>29er</td>
<td>Junior , Men</td>
<td><img class="natflag" title="HKG" src="29er_files/HKG.jpg"></td>
<td>3383</td>
<td>James Ashwood, Conrad Lumsden</td>
<td>(13.0)</td>
<td>10.0</td>
<td>11.0</td>
<td>9.0</td>
<td>12.0</td>
<td>(20.0 RET)</td>
<td>8.0</td>
<td>9.0</td>
<td>13.0</td>
<td>12.0</td>
<td>11.0</td>
<td>12.0</td>
<td>140.0</td>
<td>107.0</td>
</tr>
<tr class="odd summaryrow">
<td>13th</td>
<td>29er</td>
<td>Men</td>
<td><img class="natflag" title="HKG" src="29er_files/HKG.jpg"></td>
<td>3056</td>
<td>Kyle Lee, Brandon Liu</td>
<td>10.0</td>
<td>(14.0)</td>
<td>(13.0)</td>
<td>13.0</td>
<td>10.0</td>
<td>13.0</td>
<td>10.0</td>
<td>8.0</td>
<td>11.0</td>
<td>10.0</td>
<td>12.0</td>
<td>13.0</td>
<td>137.0</td>
<td>110.0</td>
</tr>
<tr class="even summaryrow">
<td>14th</td>
<td>29er</td>
<td>Mixed</td>
<td><img class="natflag" title="HKG" src="29er_files/HKG.jpg"></td>
<td>2750</td>
<td>Hins Law, Daisy Chan</td>
<td>14.0</td>
<td>13.0</td>
<td>14.0</td>
<td>14.0</td>
<td>(16.0)</td>
<td>14.0</td>
<td>13.0</td>
<td>(20.0 UFD)</td>
<td>15.0</td>
<td>13.0</td>
<td>14.0</td>
<td>14.0</td>
<td>174.0</td>
<td>138.0</td>
</tr>
<tr class="odd summaryrow">
<td>15th</td>
<td>29er</td>
<td>Junior , Women</td>
<td><img class="natflag" title="HKG" src="29er_files/HKG.jpg"></td>
<td>2846</td>
<td>Alexandra Yam, Keira Slaughter</td>
<td>15.0</td>
<td>15.0</td>
<td>(20.0 RET)</td>
<td>(20.0 DNF)</td>
<td>20.0 RET</td>
<td>15.0</td>
<td>14.0</td>
<td>12.0</td>
<td>14.0</td>
<td>16.0</td>
<td>15.0</td>
<td>15.0</td>
<td>191.0</td>
<td>151.0</td>
</tr>
<tr class="even summaryrow">
<td>16th</td>
<td>29er</td>
<td>Men</td>
<td><img class="natflag" title="HKG" src="29er_files/HKG.jpg"></td>
<td>3053</td>
<td>Cameron Law, Christopher Lam</td>
<td>(20.0 DNC)</td>
<td>(20.0 DNC)</td>
<td>20.0 DNC</td>
<td>20.0 DNC</td>
<td class="rank1">1.0</td>
<td>8.0</td>
<td>4.5 RDG</td>
<td>20.0 DNS</td>
<td>20.0 DNC</td>
<td>20.0 DNC</td>
<td>20.0 DNC</td>
<td>20.0 DNC</td>
<td>193.5</td>
<td>153.5</td>
</tr>
<tr class="odd summaryrow">
<td>17th</td>
<td>29er</td>
<td>Junior, Mixed</td>
<td><img class="natflag" title="HKG" src="29er_files/HKG.jpg"></td>
<td>2531</td>
<td>Belmira Iong, Alfred Fong</td>
<td>(20.0 NSC)</td>
<td>(20.0 NSC)</td>
<td>20.0 DNS</td>
<td>20.0 RET</td>
<td>17.0</td>
<td>20.0 DNF</td>
<td>20.0 DNF</td>
<td>13.0</td>
<td>16.0</td>
<td>15.0</td>
<td>16.0</td>
<td>16.0</td>
<td>213.0</td>
<td>173.0</td>
</tr>
<tr class="even summaryrow">
<td>18th</td>
<td>29er</td>
<td>Mixed</td>
<td><img class="natflag" title="HKG" src="29er_files/HKG.jpg"></td>
<td>2521</td>
<td>Jade Zirah, Alfred Yip</td>
<td>16.0</td>
<td>(20.0 DNF)</td>
<td>(20.0 DNF)</td>
<td>20.0 RET</td>
<td>14.0</td>
<td>20.0 RET</td>
<td>15.0</td>
<td>11.0</td>
<td>20.0 DNC</td>
<td>20.0 DNC</td>
<td>20.0 DNC</td>
<td>20.0 DNC</td>
<td>216.0</td>
<td>176.0</td>
</tr>
<tr class="odd summaryrow">
<td>19th</td>
<td>29er</td>
<td>Junior , Men</td>
<td><img class="natflag" title="HKG" src="29er_files/HKG.jpg"></td>
<td>2411</td>
<td>Sebastian Chun, Ambrose Chan</td>
<td>(20.0 DNC)</td>
<td>(20.0 DNC)</td>
<td>20.0 DNC</td>
<td>20.0 DNC</td>
<td>20.0 DNC</td>
<td>20.0 DNC</td>
<td>20.0 DNC</td>
<td>20.0 DNC</td>
<td>20.0 DNC</td>
<td>20.0 DNC</td>
<td>20.0 DNC</td>
<td>20.0 DNC</td>
<td>240.0</td>
<td>200.0</td>
</tr>
</tbody>
</table>
<p class="hardleft"><a href="https://"></a><br><a href="mailto:"></a></p>
<p class="hardright"><a href="https://"></a><br><a href="mailto:"></a></p>
<p>Sailwave Scoring Software 2.37.00<br><a href="https://www.sailwave.com/">www.sailwave.com</a></p>
</div>
<footer>
</footer>
<div id="scrollbottom"></div>
</body></html>
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,422 @@
<!DOCTYPE html>
<html><head>
<meta http-equiv="content-type" content="text/html; charset=Big5">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="description" content="sailwave results">
<meta name="viewport" content="width=device-width">
<title>Sailwave results for Sun Hung Kai &amp; Co. Hong Kong Race Week 2025 at 2025</title>
<style type="text/css">
body {font: 72% arial, helvetica, sans-serif; text-align: center;}
.hardleft {text-align: left; float: left; margin: 15px 0 15px 25px;}
.hardright {text-align: right; float: right; margin: 15px 25px 15px 0;}
table {text-align: left; margin: 0px auto 30px auto; font-size: 1em; border-collapse: collapse; border: 1px #fff solid;}
td, th {padding: 4px; border: 2px #fff solid; vertical-align: top;}
.caption {padding: 5px; text-align: center; border: 0; font-weight: bold;}
h1 {font-size: 1.6em;}
h2 {font-size: 1.4em;}
h3 {font-size: 1.2em;}
p {text-align: center;}
th {background-color: #aaf;}
.contents {text-align: left; margin-left: 20%;}
.even {background-color: #bbf;}
.odd {background-color: #ddf;}
.natflag {border: 1px #999 solid;}
.nattext {font-size: 0.8em;}
.place1 {font-weight: bold; background-color: #ffffaa;}
.place2 {font-weight: bold; background-color: #aaaaff;}
.place3 {font-weight: bold; background-color: #ffaaaa;}
.placen {}
</style>
<script type="text/javascript">
</script>
</head>
<body>
<header>
</header>
<div id="wrap">
<h1>Sun Hung Kai &amp; Co. Hong Kong Race Week 2025</h1>
<h2></h2>
<div style="clear:both;"></div>
<style>
div.applicant-break {page-break-after:always;}
</style>
<h3 class="seriestitle">Results are provisional as of 9:43 on February 16, 2025</h3>
<h3 class="summarytitle" id="summaryilca_6">ILCA 6 Class</h3>
<div class="caption summarycaption">Sailed: 9, Discards: 2, To count: 7, Entries: 17, Scoring system: Appendix A</div>
<table class="summarytable" cellspacing="0" cellpadding="0" border="0">
<colgroup span="16">
<col class="rank">
<col class="class">
<col class="nat">
<col class="sailno">
<col class="helmname">
<col class="race">
<col class="race">
<col class="race">
<col class="race">
<col class="race">
<col class="race">
<col class="race">
<col class="race">
<col class="race">
<col class="total">
<col class="nett">
</colgroup>
<thead>
<tr class="titlerow">
<th>Rank</th>
<th>Class</th>
<th>Nat</th>
<th>Sail No</th>
<th>Helm Name</th>
<th>R1</th>
<th>R2</th>
<th>R3</th>
<th>R4</th>
<th>R5</th>
<th>R6</th>
<th>R7</th>
<th>R8</th>
<th>R9</th>
<th>Total</th>
<th>Nett</th>
</tr>
</thead>
<tbody>
<tr class="odd summaryrow">
<td>1st</td>
<td>ILCA 6</td>
<td><img class="natflag" title="HKG" src="ILCA6_files/HKG.jpg"></td>
<td>191713</td>
<td>Stephanie Norton</td>
<td class="rank1">1.0</td>
<td class="rank1">1.0</td>
<td class="rank2">(2.0)</td>
<td class="rank1">1.0</td>
<td class="rank2">(2.0)</td>
<td class="rank1">1.0</td>
<td class="rank1">1.0</td>
<td class="rank1">1.0</td>
<td class="rank1">1.0</td>
<td>11.0</td>
<td>7.0</td>
</tr>
<tr class="even summaryrow">
<td>2nd</td>
<td>ILCA 6</td>
<td><img class="natflag" title="HKG" src="ILCA6_files/HKG.jpg"></td>
<td>211717</td>
<td>Nancy Highfield</td>
<td class="rank2">(2.0)</td>
<td class="rank2">2.0</td>
<td class="rank1">1.0</td>
<td class="rank2">2.0</td>
<td class="rank1">1.0</td>
<td class="rank2">2.0</td>
<td class="rank2">2.0</td>
<td>(11.0)</td>
<td class="rank2">2.0</td>
<td>25.0</td>
<td>12.0</td>
</tr>
<tr class="odd summaryrow">
<td>3rd</td>
<td>ILCA 6</td>
<td><img class="natflag" title="JPN" src="ILCA6_files/JPN.jpg"></td>
<td>208625</td>
<td>NANA Funazawa</td>
<td class="rank3">3.0</td>
<td>4.0</td>
<td>5.0</td>
<td class="rank3">3.0</td>
<td class="rank3">3.0</td>
<td class="rank3">3.0</td>
<td>4.0</td>
<td>(7.0)</td>
<td>(6.0)</td>
<td>38.0</td>
<td>25.0</td>
</tr>
<tr class="even summaryrow">
<td>4th</td>
<td>ILCA 6</td>
<td><img class="natflag" title="HKG" src="ILCA6_files/HKG.jpg"></td>
<td>223750</td>
<td>Daniel Kin Chung Chan</td>
<td>(10.0)</td>
<td class="rank3">3.0</td>
<td class="rank3">3.0</td>
<td>4.0</td>
<td>(5.0)</td>
<td>4.0</td>
<td class="rank3">3.0</td>
<td>4.0</td>
<td>4.0</td>
<td>40.0</td>
<td>25.0</td>
</tr>
<tr class="odd summaryrow">
<td>5th</td>
<td>ILCA 6</td>
<td><img class="natflag" title="JPN" src="ILCA6_files/JPN.jpg"></td>
<td>217702</td>
<td>Haruka Hattori</td>
<td>4.0</td>
<td>6.0</td>
<td>8.0</td>
<td>8.0</td>
<td>7.0</td>
<td>(9.0)</td>
<td>5.0</td>
<td>(10.0)</td>
<td class="rank3">3.0</td>
<td>60.0</td>
<td>41.0</td>
</tr>
<tr class="even summaryrow">
<td>6th</td>
<td>ILCA 6</td>
<td><img class="natflag" title="HKG" src="ILCA6_files/HKG.jpg"></td>
<td>209319</td>
<td>Seb Van Ommeren</td>
<td>5.0</td>
<td>(8.0)</td>
<td>7.0</td>
<td>7.0</td>
<td>(13.0)</td>
<td>8.0</td>
<td>6.0</td>
<td class="rank2">2.0</td>
<td>8.0</td>
<td>64.0</td>
<td>43.0</td>
</tr>
<tr class="odd summaryrow">
<td>7th</td>
<td>ILCA 6</td>
<td><img class="natflag" title="CHN" src="ILCA6_files/CHN.jpg"></td>
<td>223276</td>
<td>Xinran Chen</td>
<td>7.0</td>
<td>7.0</td>
<td>(11.0)</td>
<td>(9.0)</td>
<td>4.0</td>
<td>6.0</td>
<td>8.0</td>
<td>6.0</td>
<td>9.0</td>
<td>67.0</td>
<td>47.0</td>
</tr>
<tr class="even summaryrow">
<td>8th</td>
<td>ILCA 6</td>
<td><img class="natflag" title="CHN" src="ILCA6_files/CHN.jpg"></td>
<td>210199</td>
<td>Haolun Xia</td>
<td>(11.0 SCP30%)</td>
<td>5.0</td>
<td>6.0</td>
<td>5.0</td>
<td>6.0</td>
<td>(11.0)</td>
<td>10.0</td>
<td>8.0</td>
<td>7.0</td>
<td>69.0</td>
<td>47.0</td>
</tr>
<tr class="odd summaryrow">
<td>9th</td>
<td>ILCA 6</td>
<td><img class="natflag" title="HKG" src="ILCA6_files/HKG.jpg"></td>
<td>213211</td>
<td>Xavier Wilde</td>
<td>8.0</td>
<td>(11.0)</td>
<td>4.0</td>
<td>6.0</td>
<td>9.0</td>
<td>7.0</td>
<td>(11.0)</td>
<td>9.0</td>
<td>5.0</td>
<td>70.0</td>
<td>48.0</td>
</tr>
<tr class="even summaryrow">
<td>10th</td>
<td>ILCA 6</td>
<td><img class="natflag" title="HKG" src="ILCA6_files/HKG.jpg"></td>
<td>221934</td>
<td>Samuel Tan-Hardy</td>
<td>9.0</td>
<td>9.0</td>
<td>10.0</td>
<td>(12.0)</td>
<td>8.0</td>
<td>5.0</td>
<td>9.0</td>
<td class="rank3">3.0</td>
<td>(11.0)</td>
<td>76.0</td>
<td>53.0</td>
</tr>
<tr class="odd summaryrow">
<td>11th</td>
<td>ILCA 6</td>
<td><img class="natflag" title="HKG" src="ILCA6_files/HKG.jpg"></td>
<td>221401</td>
<td>Ziyan Huang</td>
<td>11.0</td>
<td>10.0</td>
<td>9.0</td>
<td>10.0</td>
<td>11.0</td>
<td>10.0</td>
<td>7.0</td>
<td>(17.0)</td>
<td>(15.0 SCP30%)</td>
<td>100.0</td>
<td>68.0</td>
</tr>
<tr class="even summaryrow">
<td>12th</td>
<td>ILCA 6</td>
<td><img class="natflag" title="SGP" src="ILCA6_files/SGP.jpg"></td>
<td>225262</td>
<td>John Gabriel LIM</td>
<td>(12.0)</td>
<td>12.0</td>
<td>12.0</td>
<td>11.0</td>
<td>10.0</td>
<td>12.0</td>
<td>12.0</td>
<td>(13.9 SCP5%)</td>
<td>12.0</td>
<td>106.9</td>
<td>81.0</td>
</tr>
<tr class="odd summaryrow">
<td>13th</td>
<td>ILCA 6</td>
<td><img class="natflag" title="CHN" src="ILCA6_files/CHN.jpg"></td>
<td>225505</td>
<td>Yan Duo Pu</td>
<td>(15.0)</td>
<td>(16.0)</td>
<td>14.0</td>
<td>15.0</td>
<td>14.0</td>
<td>14.0</td>
<td>13.0</td>
<td>5.0</td>
<td>14.0</td>
<td>120.0</td>
<td>89.0</td>
</tr>
<tr class="even summaryrow">
<td>14th</td>
<td>ILCA 6</td>
<td><img class="natflag" title="HKG" src="ILCA6_files/HKG.jpg"></td>
<td>213316</td>
<td>Adam Kan</td>
<td>(13.0)</td>
<td>13.0</td>
<td>13.0</td>
<td>13.0</td>
<td>12.0</td>
<td>13.0</td>
<td>(14.0)</td>
<td>12.0</td>
<td>13.0</td>
<td>116.0</td>
<td>89.0</td>
</tr>
<tr class="odd summaryrow">
<td>15th</td>
<td>ILCA 6</td>
<td><img class="natflag" title="HKG" src="ILCA6_files/HKG.jpg"></td>
<td>213251</td>
<td>Nick Akov</td>
<td>14.0</td>
<td>(15.0)</td>
<td>(15.0)</td>
<td>14.0</td>
<td>15.0</td>
<td>15.0</td>
<td>15.0</td>
<td>14.0</td>
<td>15.0</td>
<td>132.0</td>
<td>102.0</td>
</tr>
<tr class="even summaryrow">
<td>16th</td>
<td>ILCA 6</td>
<td><img class="natflag" title="HKG" src="ILCA6_files/HKG.jpg"></td>
<td>211730</td>
<td>Jeremy Chan</td>
<td>(16.0)</td>
<td>14.0</td>
<td>16.0</td>
<td>16.0</td>
<td>16.0</td>
<td>16.0</td>
<td>(18.0 DNF)</td>
<td>16.0</td>
<td>16.0</td>
<td>144.0</td>
<td>110.0</td>
</tr>
<tr class="odd summaryrow">
<td>17th</td>
<td>ILCA 6</td>
<td><img class="natflag" title="HKG" src="ILCA6_files/HKG.jpg"></td>
<td>213343</td>
<td>Marcello Giamporcaro</td>
<td>(18.0 DNC)</td>
<td>(18.0 DNC)</td>
<td>18.0 DNC</td>
<td>18.0 DNC</td>
<td>18.0 DNC</td>
<td>18.0 DNC</td>
<td>18.0 DNC</td>
<td>15.0</td>
<td>17.0</td>
<td>158.0</td>
<td>122.0</td>
</tr>
</tbody>
</table>
<p class="hardleft"><a href="https://"></a><br><a href="mailto:"></a></p>
<p class="hardright"><a href="https://"></a><br><a href="mailto:"></a></p>
<p>Sailwave Scoring Software 2.37.00<br><a href="https://www.sailwave.com/">www.sailwave.com</a></p>
</div>
<footer>
</footer>
<div id="scrollbottom"></div>
</body></html>
@@ -0,0 +1,188 @@
<!DOCTYPE html>
<html><head>
<meta http-equiv="content-type" content="text/html; charset=Big5">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="description" content="sailwave results">
<meta name="viewport" content="width=device-width">
<title>Sailwave results for Sun Hung Kai &amp; Co. Hong Kong Race Week 2025 at 2025</title>
<style type="text/css">
body {font: 72% arial, helvetica, sans-serif; text-align: center;}
.hardleft {text-align: left; float: left; margin: 15px 0 15px 25px;}
.hardright {text-align: right; float: right; margin: 15px 25px 15px 0;}
table {text-align: left; margin: 0px auto 30px auto; font-size: 1em; border-collapse: collapse; border: 1px #fff solid;}
td, th {padding: 4px; border: 2px #fff solid; vertical-align: top;}
.caption {padding: 5px; text-align: center; border: 0; font-weight: bold;}
h1 {font-size: 1.6em;}
h2 {font-size: 1.4em;}
h3 {font-size: 1.2em;}
p {text-align: center;}
th {background-color: #aaf;}
.contents {text-align: left; margin-left: 20%;}
.even {background-color: #bbf;}
.odd {background-color: #ddf;}
.natflag {border: 1px #999 solid;}
.nattext {font-size: 0.8em;}
.place1 {font-weight: bold; background-color: #ffffaa;}
.place2 {font-weight: bold; background-color: #aaaaff;}
.place3 {font-weight: bold; background-color: #ffaaaa;}
.placen {}
</style>
<script type="text/javascript">
</script>
</head>
<body>
<header>
</header>
<div id="wrap">
<h1>Sun Hung Kai &amp; Co. Hong Kong Race Week 2025</h1>
<h2></h2>
<div style="clear:both;"></div>
<style>
div.applicant-break {page-break-after:always;}
</style>
<h3 class="seriestitle">Results are provisional as of 19:08 on February 15, 2025</h3>
<h3 class="summarytitle" id="summaryilca_7">ILCA 7 Class</h3>
<div class="caption summarycaption">Sailed: 9, Discards: 2, To count: 7, Entries: 4, Scoring system: Appendix A</div>
<table class="summarytable" cellspacing="0" cellpadding="0" border="0">
<colgroup span="16">
<col class="rank">
<col class="class">
<col class="nat">
<col class="sailno">
<col class="helmname">
<col class="race">
<col class="race">
<col class="race">
<col class="race">
<col class="race">
<col class="race">
<col class="race">
<col class="race">
<col class="race">
<col class="total">
<col class="nett">
</colgroup>
<thead>
<tr class="titlerow">
<th>Rank</th>
<th>Class</th>
<th>Nat</th>
<th>Sail No</th>
<th>Helm Name</th>
<th>R1</th>
<th>R2</th>
<th>R3</th>
<th>R4</th>
<th>R5</th>
<th>R6</th>
<th>R7</th>
<th>R8</th>
<th>R9</th>
<th>Total</th>
<th>Nett</th>
</tr>
</thead>
<tbody>
<tr class="odd summaryrow">
<td>1st</td>
<td>ILCA 7</td>
<td><img class="natflag" title="HKG" src="ILCA7_files/HKG.jpg"></td>
<td>171979</td>
<td>Nicholas Halliday</td>
<td class="rank1">(1.0)</td>
<td class="rank1">1.0</td>
<td class="rank1">1.0</td>
<td class="rank1">1.0</td>
<td class="rank2">(2.0)</td>
<td class="rank1">1.0</td>
<td class="rank1">1.0</td>
<td class="rank1">1.0</td>
<td class="rank1">1.0</td>
<td>10.0</td>
<td>7.0</td>
</tr>
<tr class="even summaryrow">
<td>2nd</td>
<td>ILCA 7</td>
<td><img class="natflag" title="HKG" src="ILCA7_files/HKG.jpg"></td>
<td>215313</td>
<td>Peter Jessop</td>
<td class="rank2">(2.0)</td>
<td class="rank2">(2.0)</td>
<td class="rank2">2.0</td>
<td class="rank2">2.0</td>
<td class="rank1">1.0</td>
<td class="rank2">2.0</td>
<td class="rank2">2.0</td>
<td class="rank2">2.0</td>
<td class="rank2">2.0</td>
<td>17.0</td>
<td>13.0</td>
</tr>
<tr class="odd summaryrow">
<td>3rd</td>
<td>ILCA 7</td>
<td><img class="natflag" title="HKG" src="ILCA7_files/HKG.jpg"></td>
<td>150109</td>
<td>Michael Wilson</td>
<td>(5.0 DNF)</td>
<td>(5.0 DNF)</td>
<td class="rank3">5.0 DNF</td>
<td class="rank3">5.0 DNC</td>
<td class="rank3">5.0 DNC</td>
<td class="rank3">5.0 DNC</td>
<td class="rank3">5.0 DNC</td>
<td class="rank3">3.0</td>
<td class="rank3">3.0</td>
<td>41.0</td>
<td>31.0</td>
</tr>
<tr class="even summaryrow">
<td>4th</td>
<td>ILCA 7</td>
<td><img class="natflag" title="SWE" src="ILCA7_files/SWE.jpg"></td>
<td>221751</td>
<td>Axel Wieland</td>
<td class="rank3">3.0</td>
<td class="rank3">3.0</td>
<td class="rank3">(5.0 DNC)</td>
<td class="rank3">(5.0 DNC)</td>
<td class="rank3">5.0 DNC</td>
<td class="rank3">5.0 DNC</td>
<td class="rank3">5.0 DNC</td>
<td>5.0 DNC</td>
<td>5.0 DNC</td>
<td>41.0</td>
<td>31.0</td>
</tr>
</tbody>
</table>
<p class="hardleft"><a href="https://"></a><br><a href="mailto:"></a></p>
<p class="hardright"><a href="https://"></a><br><a href="mailto:"></a></p>
<p>Sailwave Scoring Software 2.37.00<br><a href="https://www.sailwave.com/">www.sailwave.com</a></p>
</div>
<footer>
</footer>
<div id="scrollbottom"></div>
</body></html>
@@ -0,0 +1,124 @@
package com.sap.sailing.sailwave.html.resultimport.test;
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.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.List;
import org.junit.Test;
import com.sap.sailing.resultimport.CompetitorRow;
import com.sap.sailing.resultimport.RegattaResults;
import com.sap.sailing.sailwave.html.resultimport.impl.SailwaveHtmlParser;
import com.sap.sse.common.Util;
public class SimpleHtmlParsingTest {
@Test
public void testOpenSampleHtml() throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(getClass().getClassLoader().getResourceAsStream("ILCA4.html")));
final String readLine = br.readLine();
assertNotNull(readLine);
br.close();
}
@Test
public void testMultiLineTD() throws IOException {
final InputStream resourceAsStream = getClass().getClassLoader().getResourceAsStream("ILCA6.html");
SailwaveHtmlParser parser = new SailwaveHtmlParser();
final RegattaResults regattaResults = parser.getRegattaResults(resourceAsStream);
List<CompetitorRow> result = regattaResults.getCompetitorResults();
assertFalse(result.isEmpty());
assertEquals(17, result.size());
assertTrue(regattaResults.getMetadata().get("seriestitle").contains("Results are provisional as of 9:43 on February 16, 2025"));
int competitorsChecked = 0;
for (CompetitorRow row : result) {
if (Util.contains(row.getNames(), "Daniel Kin Chung Chan")) {
competitorsChecked++;
assertEquals("HKG 223750", row.getSailID());
assertEquals(25.0, row.getScoreAfterDiscarding(), 0.000000001);
assertEquals(40.0, row.getTotalPointsBeforeDiscarding(), 0.000000001);
}
if (Util.contains(row.getNames(), "Haolun Xia")) {
competitorsChecked++;
assertEquals("CHN 210199", row.getSailID());
assertEquals(47.0, row.getScoreAfterDiscarding(), 0.000000001);
assertEquals(69.0, row.getTotalPointsBeforeDiscarding(), 0.000000001);
assertEquals((double) 11.00, (double) Util.get(row.getRankAndMaxPointsReasonAndPointsAndDiscarded(), 0).getScore(), 0.000000001);
assertEquals("SCP", Util.get(row.getRankAndMaxPointsReasonAndPointsAndDiscarded(), 0).getMaxPointsReason());
assertEquals(true, Util.get(row.getRankAndMaxPointsReasonAndPointsAndDiscarded(), 0).isDiscarded());
}
}
assertEquals(2, competitorsChecked);
}
@Test
public void testGetCompetitorRows29er() throws IOException {
SailwaveHtmlParser parser = new SailwaveHtmlParser();
final InputStream resourceAsStream = getClass().getClassLoader().getResourceAsStream("29er.html");
List<CompetitorRow> result = parser.getRegattaResults(resourceAsStream).getCompetitorResults();
assertFalse(result.isEmpty());
assertEquals(19, result.size());
int competitorsChecked = 0;
for (CompetitorRow row : result) {
if (Util.contains(row.getNames(), "Ethan Kiu")) {
competitorsChecked++;
assertEquals("HKG 3354", row.getSailID());
assertEquals(41.00, row.getScoreAfterDiscarding(), 0.000000001);
assertEquals(58.00, row.getTotalPointsBeforeDiscarding(), 0.000000001);
assertEquals((double) 2, (double) Util.get(row.getRankAndMaxPointsReasonAndPointsAndDiscarded(), 0).getScore(), 0.000000001);
assertEquals(false, Util.get(row.getRankAndMaxPointsReasonAndPointsAndDiscarded(), 0).isDiscarded());
} else if (Util.contains(row.getNames(), "Kaden Chan")) {
competitorsChecked++;
assertEquals("HKG 3405", row.getSailID());
assertEquals(80.00, row.getScoreAfterDiscarding(), 0.000000001);
assertEquals(107.00, row.getTotalPointsBeforeDiscarding(), 0.000000001);
assertEquals((double) 8, (double) Util.get(row.getRankAndMaxPointsReasonAndPointsAndDiscarded(), 2).getScore(), 0.000000001);
assertEquals(false, Util.get(row.getRankAndMaxPointsReasonAndPointsAndDiscarded(), 2).isDiscarded());
assertEquals((double) 15, (double) Util.get(row.getRankAndMaxPointsReasonAndPointsAndDiscarded(), 4).getScore(), 0.000000001);
assertEquals(true, Util.get(row.getRankAndMaxPointsReasonAndPointsAndDiscarded(), 4).isDiscarded());
} else if (Util.contains(row.getNames(), "Chloe Kong")) {
competitorsChecked++;
assertEquals("HKG 2566", row.getSailID());
assertEquals(67.00, row.getScoreAfterDiscarding(), 0.000000001);
assertEquals(107.00, row.getTotalPointsBeforeDiscarding(), 0.000000001);
assertEquals("SCP", Util.get(row.getRankAndMaxPointsReasonAndPointsAndDiscarded(), 6).getMaxPointsReason());
assertEquals((double) 20, (double) Util.get(row.getRankAndMaxPointsReasonAndPointsAndDiscarded(), 6).getScore(), 0.000000001);
assertEquals(true, Util.get(row.getRankAndMaxPointsReasonAndPointsAndDiscarded(), 6).isDiscarded());
}
}
assertEquals(3, competitorsChecked);
}
@Test
public void testGetCompetitorRowsILCA4() throws IOException {
SailwaveHtmlParser parser = new SailwaveHtmlParser();
final InputStream resourceAsStream = getClass().getClassLoader().getResourceAsStream("ILCA4.html");
List<CompetitorRow> result = parser.getRegattaResults(resourceAsStream).getCompetitorResults();
assertFalse(result.isEmpty());
assertEquals(50, result.size());
int competitorsChecked = 0;
for (CompetitorRow row : result) {
if (Util.contains(row.getNames(), "YONG LIANG Chuang")) {
competitorsChecked++;
assertEquals("TPE 221094", row.getSailID());
assertEquals(198.00, row.getScoreAfterDiscarding(), 0.000000001);
assertEquals(300.00, row.getTotalPointsBeforeDiscarding(), 0.000000001);
assertEquals((double) 17, (double) Util.get(row.getRankAndMaxPointsReasonAndPointsAndDiscarded(), 0).getScore(), 0.000000001);
assertEquals(false, Util.get(row.getRankAndMaxPointsReasonAndPointsAndDiscarded(), 0).isDiscarded());
assertEquals((double) 30, (double) Util.get(row.getRankAndMaxPointsReasonAndPointsAndDiscarded(), 1).getScore(), 0.000000001);
assertEquals(false, Util.get(row.getRankAndMaxPointsReasonAndPointsAndDiscarded(), 1).isDiscarded());
assertEquals("DNC", Util.get(row.getRankAndMaxPointsReasonAndPointsAndDiscarded(), 6).getMaxPointsReason());
assertEquals((double) 51, (double) Util.get(row.getRankAndMaxPointsReasonAndPointsAndDiscarded(), 6).getScore(), 0.000000001);
assertEquals(false, Util.get(row.getRankAndMaxPointsReasonAndPointsAndDiscarded(), 6).isDiscarded());
}
}
assertEquals(1, competitorsChecked);
}
}
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="output" path="bin"/>
</classpath>
@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>com.sap.sailing.sailwave.html.resultimport</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.pde.ManifestBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.pde.SchemaBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.pde.PluginNature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>
@@ -0,0 +1,2 @@
eclipse.preferences.version=1
encoding/<project>=UTF-8
@@ -0,0 +1,7 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
org.eclipse.jdt.core.compiler.compliance=1.8
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.release=disabled
org.eclipse.jdt.core.compiler.source=1.8
@@ -0,0 +1,3 @@
eclipse.preferences.version=1
pluginProject.extensions=false
resolve.requirebundle=false
@@ -0,0 +1,17 @@
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Resultimport
Bundle-SymbolicName: com.sap.sailing.sailwave.html.resultimport
Bundle-Version: 1.0.0.qualifier
Bundle-Activator: com.sap.sailing.sailwave.html.resultimport.impl.Activator
Bundle-Vendor: SAP
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Automatic-Module-Name: com.sap.sailing.sailwave.html.resultimport
Import-Package: org.apache.commons.lang;version="[2.6.0,3.0.0)",
org.osgi.framework;version="1.3.0",
org.osgi.util.tracker;version="[1.5.0,2.0.0)"
Require-Bundle: com.sap.sailing.resultimport,
com.sap.sailing.domain.common,
com.sap.sailing.domain.shared.android,
com.sap.sse.common
Bundle-ActivationPolicy: lazy
@@ -0,0 +1,4 @@
source.. = src/
output.. = bin/
bin.includes = META-INF/,\
.
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>root</artifactId>
<groupId>com.sap.sailing</groupId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>com.sap.sailing.sailwave.html.resultimport</artifactId>
<packaging>eclipse-plugin</packaging>
</project>
@@ -0,0 +1,30 @@
package com.sap.sailing.sailwave.html.resultimport.impl;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.util.tracker.ServiceTracker;
import com.sap.sailing.domain.common.ScoreCorrectionProvider;
import com.sap.sailing.resultimport.ResultUrlRegistry;
import com.sap.sailing.resultimport.impl.ResultUrlRegistryServiceTrackerCustomizer;
public class Activator implements BundleActivator {
private ServiceTracker<ResultUrlRegistry, ResultUrlRegistry> resultUrlRegistryServiceTracker;
public void start(BundleContext bundleContext) throws Exception {
resultUrlRegistryServiceTracker = new ServiceTracker<>(bundleContext, ResultUrlRegistry.class,
new ResultUrlRegistryServiceTrackerCustomizer(bundleContext) {
@Override
protected ScoreCorrectionProvider configureScoreCorrectionProvider(
ResultUrlRegistry resultUrlRegistry) {
final ScoreCorrectionProviderImpl service = new ScoreCorrectionProviderImpl(resultUrlRegistry);
return service;
}
});
resultUrlRegistryServiceTracker.open();
}
public void stop(BundleContext bundleContext) throws Exception {
resultUrlRegistryServiceTracker.close();
}
}
@@ -0,0 +1,281 @@
package com.sap.sailing.sailwave.html.resultimport.impl;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Arrays;
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;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.lang.StringEscapeUtils;
import com.sap.sailing.resultimport.CompetitorEntry;
import com.sap.sailing.resultimport.CompetitorRow;
import com.sap.sailing.resultimport.RegattaResults;
import com.sap.sailing.resultimport.impl.CompetitorRowImpl;
import com.sap.sailing.resultimport.impl.DefaultCompetitorEntryImpl;
public class SailwaveHtmlParser {
static final String CLASS_METADATA = "class";
private static final Logger logger = Logger.getLogger(SailwaveHtmlParser.class.getName());
public List<String> getRowContents(BufferedReader br) throws IOException {
Pattern tr = Pattern.compile("<(tr|TR)([^<>]*)>");
Pattern slashTr = Pattern.compile("</(tr|TR)([^<>]*)>");
boolean inTr = false;
final StringBuilder trContents = new StringBuilder();
final List<String> tableRows = new ArrayList<String>(); // the contents of the <tr> tags found
String readLine = br.readLine();
int start = 0;
int end = 0;
while (readLine != null && !readLine.contains("</tbody>")) {
if (!inTr) {
final Matcher trMatcher = tr.matcher(readLine);
boolean foundTr = trMatcher.find(end);
if (foundTr) {
start = trMatcher.end(0);
inTr = true;
} else {
readLine = br.readLine();
end = 0;
}
} else { // we are within the contents of a <tr> tag here
final Matcher slashTrMatcher = slashTr.matcher(readLine);
boolean foundSlashTr = slashTrMatcher.find(start);
if (foundSlashTr) {
end = slashTrMatcher.start();
inTr = false;
} else {
end = readLine.length();
}
trContents.append(readLine.substring(start, end));
if (!foundSlashTr) {
trContents.append('\n');
readLine = br.readLine();
start = 0;
} else {
tableRows.add(trContents.toString());
trContents.delete(0, trContents.length());
}
}
}
return tableRows;
}
public List<String> getTagContents(String s, String tag) {
Pattern tagPattern = Pattern.compile("<("+tag.toLowerCase()+"|"+tag.toUpperCase()+")([^<>]*)>");
Pattern slashTag = Pattern.compile("</("+tag.toLowerCase()+"|"+tag.toUpperCase()+")([^<>]*)>");
boolean inTag = false;
List<String> result = new ArrayList<String>(); // the contents of the <td> tags found
int start = 0;
int end = 0;
boolean finished = false;
while (!finished) {
if (!inTag) {
final Matcher tagMatcher = tagPattern.matcher(s);
boolean foundTag = tagMatcher.find(end);
if (foundTag) {
start = tagMatcher.end(0);
inTag = true;
} else {
finished = true;
}
} else { // we are within the contents of a <tr> tag here
final Matcher slashTagMatcher = slashTag.matcher(s);
boolean foundSlashTag = slashTagMatcher.find(start);
if (foundSlashTag) {
end = slashTagMatcher.start();
inTag = false;
result.add(StringEscapeUtils.unescapeHtml(s.substring(start, end).trim()));
} else {
result.add(StringEscapeUtils.unescapeHtml(s.substring(start).trim()));
finished = true;
logger.warning("unclosed "+tag+" tag in string \""+s+"\"");
}
}
}
return result;
}
/**
* @param is closed before the method returns, also in case of exception
*/
public RegattaResults getRegattaResults(InputStream is) throws IOException {
try {
Map<String, Integer> classesCounts = new HashMap<>(); // counts the occurrences of boat class names
final List<CompetitorRow> result = new ArrayList<CompetitorRow>();
final BufferedReader br = new BufferedReader(new InputStreamReader(is));
final Map<String, String> metadata = readMetadataBeforeTable(br);
final LinkedHashMap<String, String> columnNamesAndStyles = readTableHeader(br);
findTableBody(br);
final List<String> rowContents = getRowContents(br);
for (final String row : rowContents) {
final List<String> tdContent = getTagContents(row, "td");
result.add(createCompetitorRow(tdContent, columnNamesAndStyles, classesCounts));
}
addMostLikelyClassToMetadata(classesCounts, metadata);
return new RegattaResults() {
@Override
public Map<String, String> getMetadata() {
return metadata;
}
@Override
public List<CompetitorRow> getCompetitorResults() {
return result;
}
};
} finally {
is.close();
}
}
private void addMostLikelyClassToMetadata(Map<String, Integer> classesCounts, Map<String, String> metadata) {
if (!classesCounts.isEmpty()) {
// sort descending and pick first:
metadata.put(CLASS_METADATA, classesCounts.entrySet().stream().sorted((e1, e2)->Integer.compare(e2.getValue(), e1.getValue())).findFirst().get().getKey());
}
}
private void findTableBody(BufferedReader br) throws IOException {
String line;
while ((line=br.readLine()) != null && !line.contains("<tbody>"))
;
}
private LinkedHashMap<String, String> readTableHeader(BufferedReader br) throws IOException {
final LinkedHashMap<String, String> result = new LinkedHashMap<>();
final List<String> classes = new ArrayList<>();
final Pattern colClassPattern = Pattern.compile("<col class=\"([^\"]*)\" */?>");
final Pattern colTitlePattern = Pattern.compile("<th>([^<]*)</th>");
String line;
while ((line=br.readLine()) != null && !line.contains("<colgroup"))
;
while (line != null && !line.contains("</colgroup")) {
final Matcher colMatcher = colClassPattern.matcher(line);
if (colMatcher.matches()) {
classes.add(colMatcher.group(1));
}
line = br.readLine();
}
while ((line=br.readLine()) != null && !line.contains("<tr class=\"titlerow\">"))
;
final Iterator<String> classIter = classes.iterator();
while (line != null && !line.contains("</tr")) {
final Matcher colTitleMatcher = colTitlePattern.matcher(line);
if (colTitleMatcher.matches()) {
result.put(colTitleMatcher.group(1), classIter.next());
}
line = br.readLine();
}
return result;
}
private Map<String, String> readMetadataBeforeTable(BufferedReader br) throws IOException {
final Pattern h3MetadataElementPattern = Pattern.compile("<h3 class=\"([^\"]*)\"[^>]*>([^<]*)</h3>");
final Pattern divMetadataElementPattern = Pattern.compile("<div class=\"([^\"]*)\"[^>]*>([^<]*)</div>");
final Map<String, String> result = new HashMap<>();
String line;
while ((line=br.readLine()) != null && !line.contains("<h3"))
;
while (line != null && !line.contains("<table")) {
final Matcher h3Matcher = h3MetadataElementPattern.matcher(line);
if (h3Matcher.matches()) {
result.put(h3Matcher.group(1), h3Matcher.group(2));
}
final Matcher divMatcher = divMetadataElementPattern.matcher(line);
if (divMatcher.matches()) {
result.put(divMatcher.group(1), divMatcher.group(2));
}
line = br.readLine();
}
return result;
}
private CompetitorRow createCompetitorRow(List<String> trContent, LinkedHashMap<String, String> columnNamesAndStyles, Map<String, Integer> classesCounts) throws UnsupportedEncodingException {
final Pattern rankPattern = Pattern.compile("([0-9]+)[A-Za-z]*"); // captures the 3 in "3rd"
final Pattern nationalityPattern = Pattern.compile("^(<img .*\\btitle=\")?([A-Za-z][A-Za-z][A-Za-z])(\".*>)?$"); // captures the 3 in "3rd"
final Iterator<String> columnValueIterator = trContent.iterator();
String nationality = null;
String sailNumber = null;
Integer totalRank = null;
List<String> names = null;
List<CompetitorEntry> rankAndMaxPointsReasonAndPointsAndDiscarded = new ArrayList<CompetitorEntry>();
Double scoreAfterDiscarding = null;
Double totalPointsBeforeDiscarding = null;
for (final Entry<String, String> columnNameAndStyle : columnNamesAndStyles.entrySet()) {
final String columnValue = columnValueIterator.next();
switch (columnNameAndStyle.getValue()) {
case "rank":
final Matcher rankMatcher = rankPattern.matcher(columnValue);
if (rankMatcher.matches()) {
totalRank = Integer.parseInt(rankMatcher.group(1));
}
break;
case "class":
if (classesCounts.containsKey(columnValue)) {
classesCounts.put(columnValue, classesCounts.get(columnValue)+1);
} else {
classesCounts.put(columnValue, 1);
}
break;
case "nat":
// example value: <img class="natflag" title="HKG" src="ILCA6_files/HKG.jpg">
// but we should also be prepared for just the nationality string, such as "HKG"
final Matcher natMatcher = nationalityPattern.matcher(columnValue);
if (natMatcher.matches()) {
nationality = natMatcher.group(2);
} else {
nationality = columnValue;
}
break;
case "sailno":
sailNumber = columnValue;
break;
case "helmname":
names = getNames(columnValue);
break;
case "race":
CompetitorEntry rankAndMaxPointsReasonAndPointsAndDiscardedForOnceRace = getRankAndMaxPointsReasonAndPointsAndDiscardedForOnceRace(columnValue.trim());
rankAndMaxPointsReasonAndPointsAndDiscarded.add(rankAndMaxPointsReasonAndPointsAndDiscardedForOnceRace);
break;
case "total":
totalPointsBeforeDiscarding = Double.parseDouble(columnValue);
break;
case "nett":
scoreAfterDiscarding = Double.parseDouble(columnValue);
}
}
return new CompetitorRowImpl(totalRank, nationality+" "+sailNumber, names, scoreAfterDiscarding, totalPointsBeforeDiscarding,
rankAndMaxPointsReasonAndPointsAndDiscarded);
}
private CompetitorEntry getRankAndMaxPointsReasonAndPointsAndDiscardedForOnceRace(String cell) {
final Pattern oneRaceScorePattern = Pattern.compile("^\\(?([0-9]+\\.[0-9]+)( ([A-Z][A-Z][A-Z]))?.*$");
boolean isDiscarded = cell.trim().startsWith("(") && cell.trim().endsWith(")");
final Matcher matcher = oneRaceScorePattern.matcher(cell);
final CompetitorEntry result;
if (matcher.matches()) {
final String maxPointsReason = matcher.group(3);
final Double points = Double.parseDouble(matcher.group(1));
result = new DefaultCompetitorEntryImpl(/* rank */ null, maxPointsReason, points, isDiscarded);
} else {
result = null;
}
return result;
}
private List<String> getNames(String cell) {
return Arrays.asList(cell.split(", *"));
}
}
@@ -0,0 +1,102 @@
package com.sap.sailing.sailwave.html.resultimport.impl;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import com.sap.sailing.domain.common.RegattaScoreCorrections;
import com.sap.sailing.domain.common.ScoreCorrectionProvider;
import com.sap.sailing.domain.resultimport.ResultUrlProvider;
import com.sap.sailing.resultimport.AbstractResultUrlProvider;
import com.sap.sailing.resultimport.RegattaResults;
import com.sap.sailing.resultimport.ResultUrlRegistry;
import com.sap.sailing.resultimport.impl.RegattaScoreCorrectionsImpl;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util;
import com.sap.sse.common.impl.MillisecondsTimePoint;
public class ScoreCorrectionProviderImpl extends AbstractResultUrlProvider implements ScoreCorrectionProvider, ResultUrlProvider {
private static final long serialVersionUID = 5853404150107387702L;
public static final String PROVIDER_NAME = "Sailwave HTML Score Importer";
public ScoreCorrectionProviderImpl(ResultUrlRegistry resultUrlRegistry) {
super(resultUrlRegistry);
}
@Override
public String getName() {
return PROVIDER_NAME;
}
@Override
public Map<String, Set<Util.Pair<String, TimePoint>>> getHasResultsForBoatClassFromDateByEventName() {
Map<String, Set<Util.Pair<String, TimePoint>>> result = new HashMap<String, Set<Util.Pair<String,TimePoint>>>();
SailwaveHtmlParser parser = new SailwaveHtmlParser();
for (URL url : getReadableUrls()) {
URLConnection conn;
try {
conn = url.openConnection();
TimePoint lastModified = new MillisecondsTimePoint(conn.getLastModified());
RegattaResults regattaResult = parser.getRegattaResults((InputStream) conn.getContent());
final String boatClassName = getBoatClassName(regattaResult);
result.put(boatClassName, Collections.singleton(new Util.Pair<String, TimePoint>(boatClassName, lastModified)));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
return result;
}
/**
* @return the first non-empty string of the list of metadata, hoping it's something pointing at the boat class at least...
*/
private String getBoatClassName(RegattaResults regattaResult) {
Map<String, String> metadata = regattaResult.getMetadata();
if (metadata.containsKey(SailwaveHtmlParser.CLASS_METADATA)) {
return metadata.get(SailwaveHtmlParser.CLASS_METADATA);
} else {
for (String metadatum : metadata.values()) {
if (metadatum != null && metadatum.length() > 0) {
return metadatum;
}
}
return null;
}
}
@Override
public URL resolveUrl(String url) throws MalformedURLException {
return new URL(url);
}
@Override
public RegattaScoreCorrections getScoreCorrections(String eventName, String boatClassName,
TimePoint timePoint) throws Exception {
final SailwaveHtmlParser parser = new SailwaveHtmlParser();
for (URL url : getReadableUrls()) {
final URLConnection conn = url.openConnection();
final RegattaResults regattaResult = parser.getRegattaResults((InputStream) conn.getContent());
if ((boatClassName == null && getBoatClassName(regattaResult) == null) ||
boatClassName.equals(getBoatClassName(regattaResult))) {
return new RegattaScoreCorrectionsImpl(this, regattaResult);
}
}
return null;
}
@Override
public RegattaScoreCorrections getScoreCorrections(InputStream inputStream) throws Exception {
return new RegattaScoreCorrectionsImpl(this, new SailwaveHtmlParser().getRegattaResults(inputStream));
}
@Override
public String getOptionalSampleURL() {
return null;
}
}
@@ -180,6 +180,11 @@
<level>4</level>
<autoStart>true</autoStart>
</bundle>
<bundle>
<id>com.sap.sailing.sailwave.html.resultimport</id>
<level>4</level>
<autoStart>true</autoStart>
</bundle>
<bundle>
<id>com.sap.sailing.manage2sail.resultimport</id>
<level>4</level>
@@ -241,6 +241,10 @@
<setEntry value="com.diffplug.osgi.extension.sun.misc@default:default"/>
<setEntry value="com.google.protobuf@default:default"/>
<setEntry value="com.google.protobuf.source@default:default"/>
<setEntry value="com.sun.jna@default:default"/>
<setEntry value="com.sun.jna.source@default:default"/>
<setEntry value="com.sun.jna.platform@default:default"/>
<setEntry value="com.sun.jna.platform.source@default:default"/>
</setAttribute>
<setAttribute key="selected_workspace_bundles">
<setEntry value="com.sap.sailing.geocoding@default:default"/>
@@ -270,6 +274,7 @@
<setEntry value="com.sap.sailing.freg.resultimport@4:true"/>
<setEntry value="com.sap.sailing.barbados.resultimport@4:true"/>
<setEntry value="com.sap.sailing.sailwave.resultimport@4:true"/>
<setEntry value="com.sap.sailing.sailwave.html.resultimport@4:true"/>
<setEntry value="com.sap.sailing.manage2sail.resultimport@4:true"/>
<setEntry value="com.sap.sailing.sailti.resultimport@4:true"/>
<setEntry value="com.sap.sailing.yachtscoring.resultimport@4:true"/>
@@ -247,6 +247,10 @@
<setEntry value="com.diffplug.osgi.extension.sun.misc@default:default"/>
<setEntry value="com.google.protobuf@default:default"/>
<setEntry value="com.google.protobuf.source@default:default"/>
<setEntry value="com.sun.jna@default:default"/>
<setEntry value="com.sun.jna.source@default:default"/>
<setEntry value="com.sun.jna.platform@default:default"/>
<setEntry value="com.sun.jna.platform.source@default:default"/>
</setAttribute>
<setAttribute key="selected_workspace_bundles">
<setEntry value="com.sap.sailing.geocoding@default:default"/>
@@ -276,6 +280,7 @@
<setEntry value="com.sap.sailing.freg.resultimport@4:true"/>
<setEntry value="com.sap.sailing.barbados.resultimport@4:true"/>
<setEntry value="com.sap.sailing.sailwave.resultimport@4:true"/>
<setEntry value="com.sap.sailing.sailwave.html.resultimport@4:true"/>
<setEntry value="com.sap.sailing.manage2sail.resultimport@4:true"/>
<setEntry value="com.sap.sailing.sailti.resultimport@4:true"/>
<setEntry value="com.sap.sailing.yachtscoring.resultimport@4:true"/>
@@ -247,6 +247,10 @@
<setEntry value="com.diffplug.osgi.extension.sun.misc@default:default"/>
<setEntry value="com.google.protobuf@default:default"/>
<setEntry value="com.google.protobuf.source@default:default"/>
<setEntry value="com.sun.jna@default:default"/>
<setEntry value="com.sun.jna.source@default:default"/>
<setEntry value="com.sun.jna.platform@default:default"/>
<setEntry value="com.sun.jna.platform.source@default:default"/>
</setAttribute>
<setAttribute key="selected_workspace_bundles">
<setEntry value="com.sap.sailing.geocoding@default:default"/>
@@ -276,6 +280,7 @@
<setEntry value="com.sap.sailing.freg.resultimport@4:true"/>
<setEntry value="com.sap.sailing.barbados.resultimport@4:true"/>
<setEntry value="com.sap.sailing.sailwave.resultimport@4:true"/>
<setEntry value="com.sap.sailing.sailwave.html.resultimport@4:true"/>
<setEntry value="com.sap.sailing.manage2sail.resultimport@4:true"/>
<setEntry value="com.sap.sailing.sailti.resultimport@4:true"/>
<setEntry value="com.sap.sailing.yachtscoring.resultimport@4:true"/>
@@ -239,6 +239,10 @@
<setEntry value="com.diffplug.osgi.extension.sun.misc@default:default"/>
<setEntry value="com.google.protobuf@default:default"/>
<setEntry value="com.google.protobuf.source@default:default"/>
<setEntry value="com.sun.jna@default:default"/>
<setEntry value="com.sun.jna.source@default:default"/>
<setEntry value="com.sun.jna.platform@default:default"/>
<setEntry value="com.sun.jna.platform.source@default:default"/>
</setAttribute>
<setAttribute key="selected_workspace_bundles">
<setEntry value="com.sap.sailing.geocoding@default:default"/>
@@ -268,6 +272,7 @@
<setEntry value="com.sap.sailing.freg.resultimport@4:true"/>
<setEntry value="com.sap.sailing.barbados.resultimport@4:true"/>
<setEntry value="com.sap.sailing.sailwave.resultimport@4:true"/>
<setEntry value="com.sap.sailing.sailwave.html.resultimport@4:true"/>
<setEntry value="com.sap.sailing.manage2sail.resultimport@4:true"/>
<setEntry value="com.sap.sailing.sailti.resultimport@4:true"/>
<setEntry value="com.sap.sailing.yachtscoring.resultimport@4:true"/>
@@ -245,6 +245,10 @@
<setEntry value="com.diffplug.osgi.extension.sun.misc@default:default"/>
<setEntry value="com.google.protobuf@default:default"/>
<setEntry value="com.google.protobuf.source@default:default"/>
<setEntry value="com.sun.jna@default:default"/>
<setEntry value="com.sun.jna.source@default:default"/>
<setEntry value="com.sun.jna.platform@default:default"/>
<setEntry value="com.sun.jna.platform.source@default:default"/>
</setAttribute>
<setAttribute key="selected_workspace_bundles">
<setEntry value="com.sap.sailing.geocoding@default:default"/>
@@ -274,6 +278,7 @@
<setEntry value="com.sap.sailing.freg.resultimport@4:true"/>
<setEntry value="com.sap.sailing.barbados.resultimport@4:true"/>
<setEntry value="com.sap.sailing.sailwave.resultimport@4:true"/>
<setEntry value="com.sap.sailing.sailwave.html.resultimport@4:true"/>
<setEntry value="com.sap.sailing.manage2sail.resultimport@4:true"/>
<setEntry value="com.sap.sailing.sailti.resultimport@4:true"/>
<setEntry value="com.sap.sailing.yachtscoring.resultimport@4:true"/>
@@ -244,6 +244,10 @@
<setEntry value="com.diffplug.osgi.extension.sun.misc@default:default"/>
<setEntry value="com.google.protobuf@default:default"/>
<setEntry value="com.google.protobuf.source@default:default"/>
<setEntry value="com.sun.jna@default:default"/>
<setEntry value="com.sun.jna.source@default:default"/>
<setEntry value="com.sun.jna.platform@default:default"/>
<setEntry value="com.sun.jna.platform.source@default:default"/>
</setAttribute>
<setAttribute key="selected_workspace_bundles">
<setEntry value="com.sap.sailing.geocoding@default:default"/>
@@ -273,6 +277,7 @@
<setEntry value="com.sap.sailing.freg.resultimport@4:true"/>
<setEntry value="com.sap.sailing.barbados.resultimport@4:true"/>
<setEntry value="com.sap.sailing.sailwave.resultimport@4:true"/>
<setEntry value="com.sap.sailing.sailwave.html.resultimport@4:true"/>
<setEntry value="com.sap.sailing.manage2sail.resultimport@4:true"/>
<setEntry value="com.sap.sailing.sailti.resultimport@4:true"/>
<setEntry value="com.sap.sailing.yachtscoring.resultimport@4:true"/>
@@ -238,6 +238,10 @@
<setEntry value="com.diffplug.osgi.extension.sun.misc@default:default"/>
<setEntry value="com.google.protobuf@default:default"/>
<setEntry value="com.google.protobuf.source@default:default"/>
<setEntry value="com.sun.jna@default:default"/>
<setEntry value="com.sun.jna.source@default:default"/>
<setEntry value="com.sun.jna.platform@default:default"/>
<setEntry value="com.sun.jna.platform.source@default:default"/>
</setAttribute>
<setAttribute key="selected_workspace_bundles">
<setEntry value="com.sap.sailing.geocoding@default:default"/>
@@ -267,6 +271,7 @@
<setEntry value="com.sap.sailing.freg.resultimport@4:true"/>
<setEntry value="com.sap.sailing.barbados.resultimport@4:true"/>
<setEntry value="com.sap.sailing.sailwave.resultimport@4:true"/>
<setEntry value="com.sap.sailing.sailwave.html.resultimport@4:true"/>
<setEntry value="com.sap.sailing.manage2sail.resultimport@4:true"/>
<setEntry value="com.sap.sailing.sailti.resultimport@4:true"/>
<setEntry value="com.sap.sailing.yachtscoring.resultimport@4:true"/>
@@ -234,6 +234,10 @@
<setEntry value="com.diffplug.osgi.extension.sun.misc@default:default"/>
<setEntry value="com.google.protobuf@default:default"/>
<setEntry value="com.google.protobuf.source@default:default"/>
<setEntry value="com.sun.jna@default:default"/>
<setEntry value="com.sun.jna.source@default:default"/>
<setEntry value="com.sun.jna.platform@default:default"/>
<setEntry value="com.sun.jna.platform.source@default:default"/>
</setAttribute>
<setAttribute key="selected_workspace_bundles">
<setEntry value="com.sap.sailing.geocoding@default:default"/>
@@ -263,6 +267,7 @@
<setEntry value="com.sap.sailing.freg.resultimport@4:true"/>
<setEntry value="com.sap.sailing.barbados.resultimport@4:true"/>
<setEntry value="com.sap.sailing.sailwave.resultimport@4:true"/>
<setEntry value="com.sap.sailing.sailwave.html.resultimport@4:true"/>
<setEntry value="com.sap.sailing.manage2sail.resultimport@4:true"/>
<setEntry value="com.sap.sailing.sailti.resultimport@4:true"/>
<setEntry value="com.sap.sailing.yachtscoring.resultimport@4:true"/>
@@ -240,6 +240,10 @@
<setEntry value="com.diffplug.osgi.extension.sun.misc@default:default"/>
<setEntry value="com.google.protobuf@default:default"/>
<setEntry value="com.google.protobuf.source@default:default"/>
<setEntry value="com.sun.jna@default:default"/>
<setEntry value="com.sun.jna.source@default:default"/>
<setEntry value="com.sun.jna.platform@default:default"/>
<setEntry value="com.sun.jna.platform.source@default:default"/>
</setAttribute>
<setAttribute key="selected_workspace_bundles">
<setEntry value="com.sap.sailing.geocoding@default:default"/>
@@ -269,6 +273,7 @@
<setEntry value="com.sap.sailing.freg.resultimport@4:true"/>
<setEntry value="com.sap.sailing.barbados.resultimport@4:true"/>
<setEntry value="com.sap.sailing.sailwave.resultimport@4:true"/>
<setEntry value="com.sap.sailing.sailwave.html.resultimport@4:true"/>
<setEntry value="com.sap.sailing.manage2sail.resultimport@4:true"/>
<setEntry value="com.sap.sailing.sailti.resultimport@4:true"/>
<setEntry value="com.sap.sailing.yachtscoring.resultimport@4:true"/>
@@ -238,6 +238,10 @@
<setEntry value="com.diffplug.osgi.extension.sun.misc@default:default"/>
<setEntry value="com.google.protobuf@default:default"/>
<setEntry value="com.google.protobuf.source@default:default"/>
<setEntry value="com.sun.jna@default:default"/>
<setEntry value="com.sun.jna.source@default:default"/>
<setEntry value="com.sun.jna.platform@default:default"/>
<setEntry value="com.sun.jna.platform.source@default:default"/>
</setAttribute>
<setAttribute key="selected_workspace_bundles">
<setEntry value="com.sap.sailing.geocoding@default:default"/>
@@ -267,6 +271,7 @@
<setEntry value="com.sap.sailing.freg.resultimport@4:true"/>
<setEntry value="com.sap.sailing.barbados.resultimport@4:true"/>
<setEntry value="com.sap.sailing.sailwave.resultimport@4:true"/>
<setEntry value="com.sap.sailing.sailwave.html.resultimport@4:true"/>
<setEntry value="com.sap.sailing.manage2sail.resultimport@4:true"/>
<setEntry value="com.sap.sailing.sailti.resultimport@4:true"/>
<setEntry value="com.sap.sailing.yachtscoring.resultimport@4:true"/>
@@ -240,6 +240,10 @@
<setEntry value="com.diffplug.osgi.extension.sun.misc@default:default"/>
<setEntry value="com.google.protobuf@default:default"/>
<setEntry value="com.google.protobuf.source@default:default"/>
<setEntry value="com.sun.jna@default:default"/>
<setEntry value="com.sun.jna.source@default:default"/>
<setEntry value="com.sun.jna.platform@default:default"/>
<setEntry value="com.sun.jna.platform.source@default:default"/>
</setAttribute>
<setAttribute key="selected_workspace_bundles">
<setEntry value="com.sap.sailing.geocoding@default:default"/>
@@ -269,6 +273,7 @@
<setEntry value="com.sap.sailing.freg.resultimport@4:true"/>
<setEntry value="com.sap.sailing.barbados.resultimport@4:true"/>
<setEntry value="com.sap.sailing.sailwave.resultimport@4:true"/>
<setEntry value="com.sap.sailing.sailwave.html.resultimport@4:true"/>
<setEntry value="com.sap.sailing.manage2sail.resultimport@4:true"/>
<setEntry value="com.sap.sailing.sailti.resultimport@4:true"/>
<setEntry value="com.sap.sailing.yachtscoring.resultimport@4:true"/>
@@ -238,6 +238,10 @@
<setEntry value="com.diffplug.osgi.extension.sun.misc@default:default"/>
<setEntry value="com.google.protobuf@default:default"/>
<setEntry value="com.google.protobuf.source@default:default"/>
<setEntry value="com.sun.jna@default:default"/>
<setEntry value="com.sun.jna.source@default:default"/>
<setEntry value="com.sun.jna.platform@default:default"/>
<setEntry value="com.sun.jna.platform.source@default:default"/>
</setAttribute>
<setAttribute key="selected_workspace_bundles">
<setEntry value="com.sap.sailing.geocoding@default:default"/>
@@ -267,6 +271,7 @@
<setEntry value="com.sap.sailing.freg.resultimport@4:true"/>
<setEntry value="com.sap.sailing.barbados.resultimport@4:true"/>
<setEntry value="com.sap.sailing.sailwave.resultimport@4:true"/>
<setEntry value="com.sap.sailing.sailwave.html.resultimport@4:true"/>
<setEntry value="com.sap.sailing.manage2sail.resultimport@4:true"/>
<setEntry value="com.sap.sailing.sailti.resultimport@4:true"/>
<setEntry value="com.sap.sailing.yachtscoring.resultimport@4:true"/>
@@ -238,6 +238,10 @@
<setEntry value="com.diffplug.osgi.extension.sun.misc@default:default"/>
<setEntry value="com.google.protobuf@default:default"/>
<setEntry value="com.google.protobuf.source@default:default"/>
<setEntry value="com.sun.jna@default:default"/>
<setEntry value="com.sun.jna.source@default:default"/>
<setEntry value="com.sun.jna.platform@default:default"/>
<setEntry value="com.sun.jna.platform.source@default:default"/>
</setAttribute>
<setAttribute key="selected_workspace_bundles">
<setEntry value="com.sap.sailing.geocoding@default:default"/>
@@ -267,6 +271,7 @@
<setEntry value="com.sap.sailing.freg.resultimport@4:true"/>
<setEntry value="com.sap.sailing.barbados.resultimport@4:true"/>
<setEntry value="com.sap.sailing.sailwave.resultimport@4:true"/>
<setEntry value="com.sap.sailing.sailwave.html.resultimport@4:true"/>
<setEntry value="com.sap.sailing.manage2sail.resultimport@4:true"/>
<setEntry value="com.sap.sailing.sailti.resultimport@4:true"/>
<setEntry value="com.sap.sailing.yachtscoring.resultimport@4:true"/>
@@ -234,6 +234,10 @@
<setEntry value="com.diffplug.osgi.extension.sun.misc@default:default"/>
<setEntry value="com.google.protobuf@default:default"/>
<setEntry value="com.google.protobuf.source@default:default"/>
<setEntry value="com.sun.jna@default:default"/>
<setEntry value="com.sun.jna.source@default:default"/>
<setEntry value="com.sun.jna.platform@default:default"/>
<setEntry value="com.sun.jna.platform.source@default:default"/>
</setAttribute>
<setAttribute key="selected_workspace_bundles">
<setEntry value="com.sap.sailing.geocoding@default:default"/>
@@ -263,6 +267,7 @@
<setEntry value="com.sap.sailing.freg.resultimport@4:true"/>
<setEntry value="com.sap.sailing.barbados.resultimport@4:true"/>
<setEntry value="com.sap.sailing.sailwave.resultimport@4:true"/>
<setEntry value="com.sap.sailing.sailwave.html.resultimport@4:true"/>
<setEntry value="com.sap.sailing.manage2sail.resultimport@4:true"/>
<setEntry value="com.sap.sailing.sailti.resultimport@4:true"/>
<setEntry value="com.sap.sailing.yachtscoring.resultimport@4:true"/>
@@ -242,6 +242,10 @@
<setEntry value="com.diffplug.osgi.extension.sun.misc@default:default"/>
<setEntry value="com.google.protobuf@default:default"/>
<setEntry value="com.google.protobuf.source@default:default"/>
<setEntry value="com.sun.jna@default:default"/>
<setEntry value="com.sun.jna.source@default:default"/>
<setEntry value="com.sun.jna.platform@default:default"/>
<setEntry value="com.sun.jna.platform.source@default:default"/>
</setAttribute>
<setAttribute key="selected_workspace_bundles">
<setEntry value="com.sap.sailing.geocoding@default:default"/>
@@ -271,6 +275,7 @@
<setEntry value="com.sap.sailing.freg.resultimport@4:true"/>
<setEntry value="com.sap.sailing.barbados.resultimport@4:true"/>
<setEntry value="com.sap.sailing.sailwave.resultimport@4:true"/>
<setEntry value="com.sap.sailing.sailwave.html.resultimport@4:true"/>
<setEntry value="com.sap.sailing.manage2sail.resultimport@4:true"/>
<setEntry value="com.sap.sailing.sailti.resultimport@4:true"/>
<setEntry value="com.sap.sailing.yachtscoring.resultimport@4:true"/>
@@ -240,6 +240,10 @@
<setEntry value="com.diffplug.osgi.extension.sun.misc@default:default"/>
<setEntry value="com.google.protobuf@default:default"/>
<setEntry value="com.google.protobuf.source@default:default"/>
<setEntry value="com.sun.jna@default:default"/>
<setEntry value="com.sun.jna.source@default:default"/>
<setEntry value="com.sun.jna.platform@default:default"/>
<setEntry value="com.sun.jna.platform.source@default:default"/>
</setAttribute>
<setAttribute key="selected_workspace_bundles">
<setEntry value="com.sap.sailing.geocoding@default:default"/>
@@ -269,6 +273,7 @@
<setEntry value="com.sap.sailing.freg.resultimport@4:true"/>
<setEntry value="com.sap.sailing.barbados.resultimport@4:true"/>
<setEntry value="com.sap.sailing.sailwave.resultimport@4:true"/>
<setEntry value="com.sap.sailing.sailwave.html.resultimport@4:true"/>
<setEntry value="com.sap.sailing.manage2sail.resultimport@4:true"/>
<setEntry value="com.sap.sailing.sailti.resultimport@4:true"/>
<setEntry value="com.sap.sailing.yachtscoring.resultimport@4:true"/>
@@ -240,6 +240,10 @@
<setEntry value="com.diffplug.osgi.extension.sun.misc@default:default"/>
<setEntry value="com.google.protobuf@default:default"/>
<setEntry value="com.google.protobuf.source@default:default"/>
<setEntry value="com.sun.jna@default:default"/>
<setEntry value="com.sun.jna.source@default:default"/>
<setEntry value="com.sun.jna.platform@default:default"/>
<setEntry value="com.sun.jna.platform.source@default:default"/>
</setAttribute>
<setAttribute key="selected_workspace_bundles">
<setEntry value="com.sap.sailing.geocoding@default:default"/>
@@ -269,6 +273,7 @@
<setEntry value="com.sap.sailing.freg.resultimport@4:true"/>
<setEntry value="com.sap.sailing.barbados.resultimport@4:true"/>
<setEntry value="com.sap.sailing.sailwave.resultimport@4:true"/>
<setEntry value="com.sap.sailing.sailwave.html.resultimport@4:true"/>
<setEntry value="com.sap.sailing.manage2sail.resultimport@4:true"/>
<setEntry value="com.sap.sailing.sailti.resultimport@4:true"/>
<setEntry value="com.sap.sailing.yachtscoring.resultimport@4:true"/>
@@ -245,6 +245,10 @@
<setEntry value="com.diffplug.osgi.extension.sun.misc@default:default"/>
<setEntry value="com.google.protobuf@default:default"/>
<setEntry value="com.google.protobuf.source@default:default"/>
<setEntry value="com.sun.jna@default:default"/>
<setEntry value="com.sun.jna.source@default:default"/>
<setEntry value="com.sun.jna.platform@default:default"/>
<setEntry value="com.sun.jna.platform.source@default:default"/>
</setAttribute>
<setAttribute key="selected_workspace_bundles">
<setEntry value="com.sap.sailing.geocoding@default:default"/>
@@ -274,6 +278,7 @@
<setEntry value="com.sap.sailing.freg.resultimport@4:true"/>
<setEntry value="com.sap.sailing.barbados.resultimport@4:true"/>
<setEntry value="com.sap.sailing.sailwave.resultimport@4:true"/>
<setEntry value="com.sap.sailing.sailwave.html.resultimport@4:true"/>
<setEntry value="com.sap.sailing.manage2sail.resultimport@4:true"/>
<setEntry value="com.sap.sailing.sailti.resultimport@4:true"/>
<setEntry value="com.sap.sailing.yachtscoring.resultimport@4:true"/>
@@ -241,6 +241,10 @@
<setEntry value="com.diffplug.osgi.extension.sun.misc@default:default"/>
<setEntry value="com.google.protobuf@default:default"/>
<setEntry value="com.google.protobuf.source@default:default"/>
<setEntry value="com.sun.jna@default:default"/>
<setEntry value="com.sun.jna.source@default:default"/>
<setEntry value="com.sun.jna.platform@default:default"/>
<setEntry value="com.sun.jna.platform.source@default:default"/>
</setAttribute>
<setAttribute key="selected_workspace_bundles">
<setEntry value="com.sap.sailing.geocoding@default:default"/>
@@ -270,6 +274,7 @@
<setEntry value="com.sap.sailing.freg.resultimport@4:true"/>
<setEntry value="com.sap.sailing.barbados.resultimport@4:true"/>
<setEntry value="com.sap.sailing.sailwave.resultimport@4:true"/>
<setEntry value="com.sap.sailing.sailwave.html.resultimport@4:true"/>
<setEntry value="com.sap.sailing.manage2sail.resultimport@4:true"/>
<setEntry value="com.sap.sailing.sailti.resultimport@4:true"/>
<setEntry value="com.sap.sailing.yachtscoring.resultimport@4:true"/>
@@ -36,6 +36,10 @@
<li>When upgrading an application replica set through the Landscape Management panel,
other target groups such as those from an NLB will be considered correctly for adding
and removing temporary upgrade replicas and the primary/master node.</li>
<li>Added a result importer for the typical Sailwave HTML output, such as
<a href="https://www.rhkyc.org.hk/storage/app/media/Sailing/result/HONG-KONG-RACE-WEEK/2025/ILCA4.htm">https://www.rhkyc.org.hk/storage/app/media/Sailing/result/HONG-KONG-RACE-WEEK/2025/ILCA4.htm</a>.
You can add these types of URLs now in the <a href="/gwt/AdminConsole.html#ResultImportUrlsPlace:">Result import URLs</a> tab
of the Connectors category in the administration console.</li>
</ul>
<h2 class="articleSubheadline">January 2025</h2>
<ul class="bulletList">
@@ -1101,7 +1101,9 @@ public class Util {
}
/**
* Checks whether a given String is <code>null</code> or empty.
* Checks whether a given String is <code>null</code> or empty. Note that the string is not trimmed
* before performing the "empty" check, so for example " " (three spaces) <em>will</em> be considered
* having a length and <tt>true</tt> would be returned in this case.
*
* @param str
* String to check
+2
View File
@@ -252,6 +252,8 @@
<module>com.sap.sailing.velum.resultimport.test</module>
<module>com.sap.sailing.sailwave.resultimport</module>
<module>com.sap.sailing.sailwave.resultimport.test</module>
<module>com.sap.sailing.sailwave.html.resultimport</module>
<module>com.sap.sailing.sailwave.html.resultimport.test</module>
<module>com.sap.sailing.manage2sail</module>
<module>com.sap.sailing.manage2sail.resultimport</module>
<module>com.sap.sailing.manage2sail.resultimport.test</module>