diff --git a/java/com.sap.sailing.declination/src/com/sap/sailing/declination/impl/DeclinationServiceImpl.java b/java/com.sap.sailing.declination/src/com/sap/sailing/declination/impl/DeclinationServiceImpl.java index 31d584ae85b..f367854c858 100755 --- a/java/com.sap.sailing.declination/src/com/sap/sailing/declination/impl/DeclinationServiceImpl.java +++ b/java/com.sap.sailing.declination/src/com/sap/sailing/declination/impl/DeclinationServiceImpl.java @@ -57,7 +57,10 @@ public class DeclinationServiceImpl implements DeclinationService { int year = cal.get(Calendar.YEAR); QuadTree set; while ((set = getYearStore(year)) != null) { - Declination resultForYear = set.get(position); + Declination resultForYear; + synchronized (set) { + resultForYear = set.get(position); + } Distance spatialDistance = resultForYear.getPosition().getDistance(position); // consider result only if it's closer than maxDistance if (spatialDistance.compareTo(maxDistance) <= 0) { @@ -73,7 +76,9 @@ public class DeclinationServiceImpl implements DeclinationService { if (result == null) { QuadTree importerCacheForYear = importerCache.get(year); if (importerCacheForYear != null) { - result = importerCacheForYear.get(position); + synchronized (importerCacheForYear) { + result = importerCacheForYear.get(position); + } if (result.getPosition().getDistance(position).compareTo(maxDistance) <= 0) { return result; // else it's further away from the requested position as demanded by maxDistance @@ -85,7 +90,9 @@ public class DeclinationServiceImpl implements DeclinationService { importerCacheForYear = new QuadTree(); importerCache.put(year, importerCacheForYear); } - importerCacheForYear.put(result.getPosition(), result); + synchronized (importerCacheForYear) { + importerCacheForYear.put(result.getPosition(), result); + } } } return result; diff --git a/java/com.sap.sailing.declination/src/com/sap/sailing/declination/impl/DeclinationStore.java b/java/com.sap.sailing.declination/src/com/sap/sailing/declination/impl/DeclinationStore.java index cdf3330c584..c7765a741dc 100755 --- a/java/com.sap.sailing.declination/src/com/sap/sailing/declination/impl/DeclinationStore.java +++ b/java/com.sap.sailing.declination/src/com/sap/sailing/declination/impl/DeclinationStore.java @@ -49,7 +49,9 @@ public class DeclinationStore { result = new QuadTree(); BufferedReader in = new BufferedReader(new InputStreamReader(is)); while ((record = readExternal(in)) != null) { - result.put(record.getPosition(), record); + synchronized (result) { + result.put(record.getPosition(), record); + } } } return result; @@ -150,7 +152,10 @@ public class DeclinationStore { System.out.println("Date: " + year + "/" + (month + 1) + ", Latitude: " + lat); for (double lng = 0; lng < 180; lng += grid) { Position point = new DegreePosition(lat, lng); - Declination existingDeclinationRecord = storedDeclinations.get(point); + Declination existingDeclinationRecord; + synchronized (storedDeclinations) { + existingDeclinationRecord = storedDeclinations.get(point); + } if (existingDeclinationRecord == null || DeclinationServiceImpl.timeAndSpaceDistance(existingDeclinationRecord .getPosition().getDistance(point), timePoint, existingDeclinationRecord @@ -161,7 +166,10 @@ public class DeclinationStore { } for (double lng = -grid; lng > -180; lng -= grid) { Position point = new DegreePosition(lat, lng); - Declination existingDeclinationRecord = storedDeclinations.get(point); + Declination existingDeclinationRecord; + synchronized (storedDeclinations) { + existingDeclinationRecord = storedDeclinations.get(point); + } if (existingDeclinationRecord == null || DeclinationServiceImpl.timeAndSpaceDistance(existingDeclinationRecord .getPosition().getDistance(point), timePoint, existingDeclinationRecord @@ -175,7 +183,10 @@ public class DeclinationStore { System.out.println("Date: " + year + "/" + (month + 1) + ", Latitude: " + lat); for (double lng = 0; lng < 180; lng += grid) { Position point = new DegreePosition(lat, lng); - Declination existingDeclinationRecord = storedDeclinations.get(point); + Declination existingDeclinationRecord; + synchronized (storedDeclinations) { + existingDeclinationRecord = storedDeclinations.get(point); + } if (DeclinationServiceImpl.timeAndSpaceDistance(existingDeclinationRecord.getPosition().getDistance(point), timePoint, existingDeclinationRecord.getTimePoint()) > 0.1) { // less than ~6 nautical miles and/or ~.6 months off @@ -184,7 +195,10 @@ public class DeclinationStore { } for (double lng = -grid; lng > -180; lng -= grid) { Position point = new DegreePosition(lat, lng); - Declination existingDeclinationRecord = storedDeclinations.get(point); + Declination existingDeclinationRecord; + synchronized (storedDeclinations) { + existingDeclinationRecord = storedDeclinations.get(point); + } if (DeclinationServiceImpl.timeAndSpaceDistance(existingDeclinationRecord.getPosition().getDistance(point), timePoint, existingDeclinationRecord.getTimePoint()) > 0.1) { // less than ~6 nautical miles and/or ~.6 months off diff --git a/java/com.sap.sailing.domain.common/META-INF/MANIFEST.MF b/java/com.sap.sailing.domain.common/META-INF/MANIFEST.MF index 83d845f6947..359e07e54bd 100755 --- a/java/com.sap.sailing.domain.common/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.domain.common/META-INF/MANIFEST.MF @@ -7,4 +7,5 @@ Bundle-Vendor: SAP Bundle-RequiredExecutionEnvironment: JavaSE-1.6 Export-Package: com.sap.sailing.domain.common, com.sap.sailing.domain.common.impl, - com.sap.sailing.domain.common.quadtree + com.sap.sailing.domain.common.quadtree, + com.sap.sailing.domain.common.quadtree.impl;x-friends:="com.sap.sailing.domain.test" diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/AbstractPosition.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/AbstractPosition.java index fe458483bbb..82c85f99dd5 100755 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/AbstractPosition.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/AbstractPosition.java @@ -12,9 +12,12 @@ public class AbstractPosition implements Position { } public boolean equals(Object o) { - return o instanceof Position && - getLatRad() == ((Position) o).getLatRad() && - getLngRad() == ((Position) o).getLngRad(); + if (o == null) { + return false; + } else { + return o instanceof Position && getLatRad() == ((Position) o).getLatRad() + && getLngRad() == ((Position) o).getLngRad(); + } } @Override diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/quadtree/QuadTree.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/quadtree/QuadTree.java index 477b1a2ba40..51f42e5f100 100755 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/quadtree/QuadTree.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/quadtree/QuadTree.java @@ -32,11 +32,16 @@ import com.sap.sailing.domain.common.quadtree.impl.Bounds; import com.sap.sailing.domain.common.quadtree.impl.QuadTreeNode; /** - * The QuadTree lets you organize objects in a grid, that redefines - * itself and focuses more gridding when more objects appear in a - * certain area. + * The QuadTree lets you organize objects in a grid, that redefines itself and focuses more gridding when more objects + * appear in a certain area. + *

* - * @param type of object stored by coordinates + * Note that this class is not thread safe. If multiple threads can access the same instance concurrently, callers have + * to ensure proper synchronization. Concurrent reads are permissible while any write should block all other operations. + * + * @param + * type of object stored by coordinates + * @author Axel Uhl (D043530) */ public class QuadTree implements Serializable { diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/quadtree/impl/QuadTreeNode.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/quadtree/impl/QuadTreeNode.java index 1de0b986ff7..ee6229d64a6 100644 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/quadtree/impl/QuadTreeNode.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/quadtree/impl/QuadTreeNode.java @@ -110,7 +110,7 @@ public class QuadTreeNode implements Serializable { * into the children. */ @SuppressWarnings("unchecked") - protected void split() { + protected void split() { // Make sure we're bigger than the minimum, if we care, if (minSize != NO_MIN_SIZE) { if (Math.abs(bounds.getNorthEast().getLatDeg() - bounds.getSouthWest().getLatDeg()) < minSize @@ -121,7 +121,6 @@ public class QuadTreeNode implements Serializable { double nsHalf = (bounds.getNorthEast().getLatDeg() + bounds.getSouthWest().getLatDeg()) / 2.0; double ewHalf = (bounds.getNorthEast().getLngDeg() + bounds.getSouthWest().getLngDeg()) / 2.0; children = new QuadTreeNode[4]; - children[NORTHWEST] = new QuadTreeNode(new Bounds(new DegreePosition(nsHalf, bounds.getSouthWest().getLngDeg()), new DegreePosition(bounds.getNorthEast().getLatDeg(), ewHalf)), maxItems); children[NORTHEAST] = new QuadTreeNode(new Bounds(new DegreePosition(nsHalf, ewHalf), bounds.getNorthEast()), maxItems); children[SOUTHEAST] = new QuadTreeNode(new Bounds(new DegreePosition(bounds.getSouthWest().getLatDeg(), ewHalf), new DegreePosition(nsHalf, bounds.getNorthEast().getLngDeg())), maxItems); @@ -131,7 +130,6 @@ public class QuadTreeNode implements Serializable { for (Iterator> i=temp.iterator(); i.hasNext(); ) { put(i.next()); } - //items.removeAllElements(); } /** @@ -208,9 +206,9 @@ public class QuadTreeNode implements Serializable { this.allTheSamePoint = false; } } - - if (this.items.size() > maxItems && !this.allTheSamePoint) + if (this.items.size() > maxItems && !this.allTheSamePoint) { split(); + } } else { QuadTreeNode node = getChild(leaf.getPoint()); if (node != null) { diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/DomainObjectFactoryImpl.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/DomainObjectFactoryImpl.java index 26015070d85..a441924f954 100644 --- a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/DomainObjectFactoryImpl.java +++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/DomainObjectFactoryImpl.java @@ -51,6 +51,7 @@ import com.sap.sailing.domain.leaderboard.LeaderboardGroup; import com.sap.sailing.domain.leaderboard.SettableScoreCorrection; import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule; import com.sap.sailing.domain.leaderboard.impl.LeaderboardGroupImpl; +import com.sap.sailing.domain.leaderboard.impl.LowerScoreIsBetter; import com.sap.sailing.domain.leaderboard.impl.ResultDiscardingRuleImpl; import com.sap.sailing.domain.leaderboard.impl.ScoreCorrectionImpl; import com.sap.sailing.domain.persistence.DomainObjectFactory; @@ -170,7 +171,7 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { } ThresholdBasedResultDiscardingRule resultDiscardingRule = new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces); FlexibleLeaderboardImplWithDelayedCarriedPoints result = new FlexibleLeaderboardImplWithDelayedCarriedPoints( - (String) o.get(FieldNames.LEADERBOARD_NAME.name()), scoreCorrection, resultDiscardingRule); + (String) o.get(FieldNames.LEADERBOARD_NAME.name()), scoreCorrection, resultDiscardingRule, new LowerScoreIsBetter()); BasicDBList dbRaceColumns = (BasicDBList) o.get(FieldNames.LEADERBOARD_COLUMNS.name()); // For a FlexibleLeaderboard, fleets are owned by the leaderboard's RaceColumn objects. We need to manage them here: Map fleetsByName = new HashMap(); diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/FlexibleLeaderboardImplWithDelayedCarriedPoints.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/FlexibleLeaderboardImplWithDelayedCarriedPoints.java index 66305f59168..6c44451f865 100755 --- a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/FlexibleLeaderboardImplWithDelayedCarriedPoints.java +++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/FlexibleLeaderboardImplWithDelayedCarriedPoints.java @@ -1,5 +1,6 @@ package com.sap.sailing.domain.persistence.impl; +import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; import java.util.Map; @@ -60,8 +61,8 @@ public class FlexibleLeaderboardImplWithDelayedCarriedPoints extends FlexibleLea } public FlexibleLeaderboardImplWithDelayedCarriedPoints(String name, SettableScoreCorrection scoreCorrection, - ThresholdBasedResultDiscardingRule resultDiscardingRule) { - super(name, scoreCorrection, resultDiscardingRule); + ThresholdBasedResultDiscardingRule resultDiscardingRule, Comparator scoreComparator) { + super(name, scoreCorrection, resultDiscardingRule, scoreComparator); carriedPointsByCompetitorName = new HashMap(); maxPointsReasonsByCompetitorName = new HashMap>(); correctedScoresByCompetitorName = new HashMap>(); diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/CourseTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/CourseTest.java index 9cf27561c61..5bb04aa475f 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/CourseTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/CourseTest.java @@ -1,8 +1,10 @@ package com.sap.sailing.domain.test; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -80,4 +82,46 @@ public class CourseTest { assertEquals(0, Util.size(course.getWaypoints())); assertEquals(0, Util.size(course.getLegs())); } + + @Test + public void testInsertWaypointToCourseWithTwoWaypoints() { + List waypoints = new ArrayList(); + final WaypointImpl wp1 = new WaypointImpl(new BuoyImpl("Test Buoy 1")); + waypoints.add(wp1); + final WaypointImpl wp2 = new WaypointImpl(new BuoyImpl("Test Buoy 2")); + waypoints.add(wp2); + Course course = new CourseImpl("Test Course", waypoints); + assertEquals(2, Util.size(course.getWaypoints())); + assertEquals(1, Util.size(course.getLegs())); + final WaypointImpl wp1_5 = new WaypointImpl(new BuoyImpl("Test Buoy 1.5")); + course.addWaypoint(1, wp1_5); + assertEquals(3, Util.size(course.getWaypoints())); + assertEquals(2, Util.size(course.getLegs())); + assertTrue(Util.equals(Arrays.asList(new Waypoint[] { wp1, wp1_5, wp2 }), course.getWaypoints())); + assertEquals(0, course.getIndexOfWaypoint(wp1)); + assertEquals(1, course.getIndexOfWaypoint(wp1_5)); + assertEquals(2, course.getIndexOfWaypoint(wp2)); + } + + @Test + public void testRemovetWaypointFromCourseWithThreeWaypoints() { + List waypoints = new ArrayList(); + final WaypointImpl wp1 = new WaypointImpl(new BuoyImpl("Test Buoy 1")); + waypoints.add(wp1); + final WaypointImpl wp2 = new WaypointImpl(new BuoyImpl("Test Buoy 2")); + waypoints.add(wp2); + final WaypointImpl wp3 = new WaypointImpl(new BuoyImpl("Test Buoy 3")); + waypoints.add(wp3); + Course course = new CourseImpl("Test Course", waypoints); + assertEquals(3, Util.size(course.getWaypoints())); + assertEquals(2, Util.size(course.getLegs())); + course.removeWaypoint(1); + assertEquals(2, Util.size(course.getWaypoints())); + assertEquals(1, Util.size(course.getLegs())); + assertTrue(Util.equals(Arrays.asList(new Waypoint[] { wp1, wp3 }), course.getWaypoints())); + assertEquals(0, course.getIndexOfWaypoint(wp1)); + assertEquals(-1, course.getIndexOfWaypoint(wp2)); + assertEquals(1, course.getIndexOfWaypoint(wp3)); + } + } diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LeaderboardForKielWeekTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LeaderboardForKielWeekTest.java index b14faa6a43f..bb5a2211a1d 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LeaderboardForKielWeekTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LeaderboardForKielWeekTest.java @@ -20,6 +20,7 @@ import com.sap.sailing.domain.common.impl.DegreeBearingImpl; import com.sap.sailing.domain.common.impl.Util.Pair; import com.sap.sailing.domain.common.impl.WindSourceImpl; import com.sap.sailing.domain.leaderboard.impl.FlexibleLeaderboardImpl; +import com.sap.sailing.domain.leaderboard.impl.LowerScoreIsBetter; import com.sap.sailing.domain.leaderboard.impl.ResultDiscardingRuleImpl; import com.sap.sailing.domain.leaderboard.impl.ScoreCorrectionImpl; import com.sap.sailing.domain.tracking.impl.WindImpl; @@ -35,7 +36,8 @@ public class LeaderboardForKielWeekTest extends OnlineTracTracBasedTest { @Test public void leaderboardWithOneRaceTest() throws URISyntaxException, NoWindException, IOException, InterruptedException { - leaderboard = new FlexibleLeaderboardImpl("Kiel Week 2011 505s", new ScoreCorrectionImpl(), new ResultDiscardingRuleImpl(new int[] { 3, 6 })); + leaderboard = new FlexibleLeaderboardImpl("Kiel Week 2011 505s", new ScoreCorrectionImpl(), + new ResultDiscardingRuleImpl(new int[] { 3, 6 }), new LowerScoreIsBetter()); Fleet defaultFleet = leaderboard.getFleet(null); MillisecondsTimePoint now = MillisecondsTimePoint.now(); loadRace("357c700a-9d9a-11e0-85be-406186cbf87c"); // 505 Race 2 diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LeaderboardOfflineTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LeaderboardOfflineTest.java index 45cc683e36d..62638d0fc45 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LeaderboardOfflineTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LeaderboardOfflineTest.java @@ -25,6 +25,7 @@ import com.sap.sailing.domain.common.impl.Util; import com.sap.sailing.domain.common.impl.Util.Pair; import com.sap.sailing.domain.leaderboard.FlexibleLeaderboard; import com.sap.sailing.domain.leaderboard.impl.FlexibleLeaderboardImpl; +import com.sap.sailing.domain.leaderboard.impl.LowerScoreIsBetter; import com.sap.sailing.domain.leaderboard.impl.ResultDiscardingRuleImpl; import com.sap.sailing.domain.leaderboard.impl.ScoreCorrectionImpl; import com.sap.sailing.domain.tracking.TrackedRace; @@ -85,7 +86,7 @@ public class LeaderboardOfflineTest extends AbstractLeaderboardTest { @Test public void ensureMedalRaceParamIsIgnoredIfRaceColumnAlreadyExists() { FlexibleLeaderboard leaderboard = new FlexibleLeaderboardImpl("Test Leaderboard", new ScoreCorrectionImpl(), new ResultDiscardingRuleImpl( - new int[] { 5, 8 })); + new int[] { 5, 8 }), new LowerScoreIsBetter()); Fleet defaultFleet = leaderboard.getFleet(null); final String columnName = "abc"; setupRaces(1, 0); @@ -108,7 +109,7 @@ public class LeaderboardOfflineTest extends AbstractLeaderboardTest { } ScoreCorrectionImpl scoreCorrection = new ScoreCorrectionImpl(); FlexibleLeaderboard leaderboard = new FlexibleLeaderboardImpl("Test Leaderboard", scoreCorrection, new ResultDiscardingRuleImpl( - new int[] { 1 })); + new int[] { 1 }), new LowerScoreIsBetter()); Fleet defaultFleet = leaderboard.getFleet(null); int i=0; int bestScore = Integer.MAX_VALUE; @@ -144,7 +145,7 @@ public class LeaderboardOfflineTest extends AbstractLeaderboardTest { } ScoreCorrectionImpl scoreCorrection = new ScoreCorrectionImpl(); FlexibleLeaderboard leaderboard = new FlexibleLeaderboardImpl("Test Leaderboard", scoreCorrection, new ResultDiscardingRuleImpl( - new int[] { 1 })); + new int[] { 1 }), new LowerScoreIsBetter()); Fleet defaultFleet = leaderboard.getFleet(null); int i=0; int bestScore = Integer.MAX_VALUE; @@ -171,7 +172,7 @@ public class LeaderboardOfflineTest extends AbstractLeaderboardTest { int secondDiscardingThreshold, Integer carry, boolean addOneMedalRace, int numberOfUntrackedRaces) throws NoWindException { setupRaces(numberOfStartedRaces, numberOfNotStartedRaces); FlexibleLeaderboard leaderboard = new FlexibleLeaderboardImpl("Test Leaderboard", new ScoreCorrectionImpl(), new ResultDiscardingRuleImpl( - new int[] { firstDiscardingThreshold, secondDiscardingThreshold })); + new int[] { firstDiscardingThreshold, secondDiscardingThreshold }), new LowerScoreIsBetter()); Fleet defaultFleet = leaderboard.getFleet(null); int i=0; for (TrackedRace race : testRaces) { diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LeaderboardScoringAndRankingTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LeaderboardScoringAndRankingTest.java index bc7fcd4a150..9473a6b4b9f 100644 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LeaderboardScoringAndRankingTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LeaderboardScoringAndRankingTest.java @@ -22,9 +22,11 @@ import com.sap.sailing.domain.base.impl.FleetImpl; import com.sap.sailing.domain.base.impl.MillisecondsTimePoint; import com.sap.sailing.domain.base.impl.RegattaImpl; import com.sap.sailing.domain.base.impl.SeriesImpl; +import com.sap.sailing.domain.common.MaxPointsReason; import com.sap.sailing.domain.common.NoWindException; import com.sap.sailing.domain.common.TimePoint; import com.sap.sailing.domain.leaderboard.Leaderboard; +import com.sap.sailing.domain.leaderboard.impl.LowerScoreIsBetter; import com.sap.sailing.domain.leaderboard.impl.RegattaLeaderboardImpl; import com.sap.sailing.domain.leaderboard.impl.ResultDiscardingRuleImpl; import com.sap.sailing.domain.leaderboard.impl.ScoreCorrectionImpl; @@ -36,7 +38,7 @@ public class LeaderboardScoringAndRankingTest extends AbstractLeaderboardTest { private Leaderboard createLeaderboard(Regatta regatta, int[] discardingThresholds) { ScoreCorrectionImpl scoreCorrections = new ScoreCorrectionImpl(); ResultDiscardingRuleImpl discardingRules = new ResultDiscardingRuleImpl(discardingThresholds); - return new RegattaLeaderboardImpl(regatta, scoreCorrections, discardingRules); + return new RegattaLeaderboardImpl(regatta, scoreCorrections, discardingRules, new LowerScoreIsBetter()); } @Test @@ -56,6 +58,33 @@ public class LeaderboardScoringAndRankingTest extends AbstractLeaderboardTest { assertEquals(competitors, rankedCompetitors); } + /** + * Asserts that the competitors ranking worse than the disqualified competitor advance by one + */ + @Test + public void testOneStartedRaceWithDifferentScoresAndDisqualification() { + List competitors = createCompetitors(10); + Regatta regatta = createRegatta(/* qualifying */0, new String[] { "Default" }, /* final */1, + new String[] { "Default" }, + /* medal */false, "testOneStartedRaceWithDifferentScores", + DomainFactory.INSTANCE.getOrCreateBoatClass("49er", /* typicallyStartsUpwind */true)); + Leaderboard leaderboard = createLeaderboard(regatta, /* discarding thresholds */ new int[0]); + Series finalSeries; + Iterator seriesIter = regatta.getSeries().iterator(); + seriesIter.next(); + finalSeries = seriesIter.next(); + leaderboard.getScoreCorrection().setMaxPointsReason(competitors.get(5), finalSeries.getRaceColumnByName("F1"), MaxPointsReason.DSQ); + TimePoint now = MillisecondsTimePoint.now(); + TimePoint later = new MillisecondsTimePoint(now.asMillis()+1000); + TrackedRace f1 = new MockedTrackedRaceWithStartTimeAndRanks(now, competitors); + RaceColumn f1Column = series.get(1).getRaceColumnByName("F1"); + f1Column.setTrackedRace(f1Column.getFleets().iterator().next(), f1); + List rankedCompetitors = leaderboard.getCompetitorsFromBestToWorst(later); + assertEquals(competitors.subList(0, 5), rankedCompetitors.subList(0, 5)); + assertEquals(competitors.subList(6, 10), rankedCompetitors.subList(5, 9)); + assertEquals(competitors.get(5), rankedCompetitors.get(9)); + } + @Test public void testDistributionAcrossQualifyingFleetsWithDifferentScores() { List competitors = createCompetitors(10); diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/MockedTrackedRaceWithFixedRank.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/MockedTrackedRaceWithFixedRank.java index 631bfdd75ca..0754d2849d7 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/MockedTrackedRaceWithFixedRank.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/MockedTrackedRaceWithFixedRank.java @@ -1,6 +1,7 @@ package com.sap.sailing.domain.test; import java.util.Collections; +import java.util.List; import com.sap.sailing.domain.base.BoatClass; import com.sap.sailing.domain.base.Competitor; @@ -43,6 +44,11 @@ public class MockedTrackedRaceWithFixedRank extends MockedTrackedRace { } + @Override + public List getCompetitorsFromBestToWorst(TimePoint timePoint) { + return Collections.singletonList(competitor); + } + @Override public boolean hasStarted(TimePoint at) { return started; diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/MockedTrackedRaceWithStartTimeAndRanks.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/MockedTrackedRaceWithStartTimeAndRanks.java index 0256502dbe2..38324869486 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/MockedTrackedRaceWithStartTimeAndRanks.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/MockedTrackedRaceWithStartTimeAndRanks.java @@ -412,4 +412,9 @@ public class MockedTrackedRaceWithStartTimeAndRanks implements TrackedRace { return null; } + @Override + public List getCompetitorsFromBestToWorst(TimePoint timePoint) { + return competitorsFromBestToWorst; + } + } diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/QuadTreeTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/QuadTreeTest.java index 2577b05f536..d557698c362 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/QuadTreeTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/QuadTreeTest.java @@ -1,12 +1,14 @@ package com.sap.sailing.domain.test; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import org.junit.Test; import com.sap.sailing.domain.common.Position; import com.sap.sailing.domain.common.impl.DegreePosition; import com.sap.sailing.domain.common.quadtree.QuadTree; +import com.sap.sailing.domain.common.quadtree.impl.QuadTreeNode; public class QuadTreeTest { private class GLatLngQuadTree extends QuadTree { @@ -21,6 +23,53 @@ public class QuadTreeTest { } } + private static class QuadTreeWithPublicGetTop extends QuadTree { + private static final long serialVersionUID = -783622065160380333L; + @Override + public QuadTreeNode getTop() { + return super.getTop(); + } + } + + @Test + public void testNoNPEDuringSecondPutInSameLeaf() { + final QuadTreeWithPublicGetTop qt = new QuadTreeWithPublicGetTop(); + final Position p = new DegreePosition(0, 0); + final NullPointerException[] npe = new NullPointerException[1]; + final boolean[] stop = new boolean[1]; + Runnable r = new Runnable() { + @Override + public void run() { + while (!stop[0]) { + synchronized (qt) { + try { + qt.wait(); + // if the following try/catch is moved outside the synchronized block, occasional NPEs result + try { + qt.put(p, p); + } catch (NullPointerException e) { + npe[0] = e; + } + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + } + } + }; + new Thread(r, "1").start(); + new Thread(r, "2").start(); + new Thread(r, "3").start(); + new Thread(r, "4").start(); + for (int i=0; i<10000000; i++) { + synchronized(qt) { + qt.notifyAll(); + } + assertNull("NullPointerException "+(npe[0]==null?"":npe[0].getMessage())+" during iteration "+i, npe[0]); + } + stop[0] = true; + } + @Test public void testDistance() { GLatLngQuadTree quadtree = new GLatLngQuadTree(new DegreePosition(49.29, diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRace.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRace.java index f09d95cd292..9021276cf12 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRace.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRace.java @@ -620,4 +620,10 @@ public class MockedTrackedRace implements DynamicTrackedRace { // TODO Auto-generated method stub } + + @Override + public List getCompetitorsFromBestToWorst(TimePoint timePoint) { + // TODO Auto-generated method stub + return null; + } } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/RaceColumn.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/RaceColumn.java index 8858280caf6..bed5701d6f7 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/RaceColumn.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/RaceColumn.java @@ -23,7 +23,8 @@ import com.sap.sailing.domain.tracking.TrackedRace; */ public interface RaceColumn extends Named { /** - * @return the fleets for each of which this column has a single race and therefore optionally a {@link TrackedRace} + * @return the fleets for each of which this column has a single race and therefore optionally a {@link TrackedRace}, in + * ascending order; best fleets first */ Iterable getFleets(); diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/Series.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/Series.java index 798eabf0f8d..0763d2c4d9d 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/Series.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/Series.java @@ -15,6 +15,9 @@ import com.sap.sailing.domain.common.Named; * */ public interface Series extends Named { + /** + * Returns the fleets of this series, on ascending order, better fleets first. + */ Iterable getFleets(); /** diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/CourseImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/CourseImpl.java index a46ed7dc970..2724ce1f4aa 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/CourseImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/CourseImpl.java @@ -87,6 +87,14 @@ public class CourseImpl extends NamedImpl implements Course { @Override public synchronized void addWaypoint(int zeroBasedPosition, Waypoint waypointToAdd) { waypoints.add(zeroBasedPosition, waypointToAdd); + Map updatesToWaypointIndexes = new HashMap(); + updatesToWaypointIndexes.put(waypointToAdd, zeroBasedPosition); + for (Map.Entry e : waypointIndexes.entrySet()) { + if (e.getValue() >= zeroBasedPosition) { + updatesToWaypointIndexes.put(e.getKey(), e.getValue()+1); + } + } + waypointIndexes.putAll(updatesToWaypointIndexes); int legStartWaypointIndex; if (zeroBasedPosition == waypoints.size()-1) { // added to end legStartWaypointIndex = zeroBasedPosition-1; @@ -104,6 +112,14 @@ public class CourseImpl extends NamedImpl implements Course { if (zeroBasedPosition >= 0) { boolean isLast = zeroBasedPosition == waypoints.size()-1; Waypoint removedWaypoint = waypoints.remove(zeroBasedPosition); + waypointIndexes.remove(removedWaypoint); + Map updatesToWaypointIndexes = new HashMap(); + for (Map.Entry e : waypointIndexes.entrySet()) { + if (e.getValue() > zeroBasedPosition) { // only > because the entry with == was just removed + updatesToWaypointIndexes.put(e.getKey(), e.getValue()-1); + } + } + waypointIndexes.putAll(updatesToWaypointIndexes); if (isLast) { if (waypoints.size() > 0) { // if we had only one waypoint, we didn't have any legs // last waypoint was removed; remove last leg @@ -167,7 +183,7 @@ public class CourseImpl extends NamedImpl implements Course { } @Override - public int getIndexOfWaypoint(Waypoint waypoint) { + public synchronized int getIndexOfWaypoint(Waypoint waypoint) { int result = -1; Integer indexEntry = waypointIndexes.get(waypoint); if (indexEntry != null) { diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/SeriesImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/SeriesImpl.java index adb79e8f521..b9d3c5440e4 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/SeriesImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/SeriesImpl.java @@ -1,6 +1,7 @@ package com.sap.sailing.domain.base.impl; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -13,6 +14,7 @@ import com.sap.sailing.domain.common.impl.NamedImpl; public class SeriesImpl extends NamedImpl implements Series { private static final long serialVersionUID = -1640404303144907381L; private final Map fleetsByName; + private final List fleetsInAscendingOrder; private final Iterable raceColumns; private boolean isMedal; @@ -22,6 +24,8 @@ public class SeriesImpl extends NamedImpl implements Series { for (Fleet fleet : fleets) { this.fleetsByName.put(fleet.getName(), fleet); } + fleetsInAscendingOrder = new ArrayList(fleetsByName.values()); + Collections.sort(fleetsInAscendingOrder); List myRaceColumns = new ArrayList(); for (String raceColumnName : raceColumnNames) { RaceColumnInSeriesImpl raceColumn = new RaceColumnInSeriesImpl(raceColumnName, this); @@ -32,7 +36,7 @@ public class SeriesImpl extends NamedImpl implements Series { } public Iterable getFleets() { - return fleetsByName.values(); + return fleetsInAscendingOrder; } @Override diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractLeaderboardImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractLeaderboardImpl.java index cb6856f2d62..f95827ce6a4 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractLeaderboardImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractLeaderboardImpl.java @@ -5,6 +5,7 @@ import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; @@ -18,6 +19,7 @@ import com.sap.sailing.domain.common.TimePoint; import com.sap.sailing.domain.common.impl.Util; import com.sap.sailing.domain.common.impl.Util.Pair; import com.sap.sailing.domain.leaderboard.Leaderboard; +import com.sap.sailing.domain.leaderboard.ScoreCorrection; import com.sap.sailing.domain.leaderboard.ScoreCorrection.Result; import com.sap.sailing.domain.leaderboard.SettableScoreCorrection; import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule; @@ -46,6 +48,8 @@ public abstract class AbstractLeaderboardImpl implements Leaderboard { * provided by this map are considered by {@link #getTotalPoints(Competitor, TimePoint)}. */ private final Map carriedPoints; + + private final Comparator scoreComparator; /** * A leaderboard entry representing a snapshot of a cell at a given time point for a single race/competitor. @@ -96,13 +100,16 @@ public abstract class AbstractLeaderboardImpl implements Leaderboard { } /** + * @param scoreComparator the comparator to use to compare basic scores, such as net points * @param name must not be null */ - public AbstractLeaderboardImpl(SettableScoreCorrection scoreCorrection, ThresholdBasedResultDiscardingRule resultDiscardingRule) { + public AbstractLeaderboardImpl(SettableScoreCorrection scoreCorrection, + ThresholdBasedResultDiscardingRule resultDiscardingRule, Comparator scoreComparator) { this.carriedPoints = new HashMap(); this.scoreCorrection = scoreCorrection; this.displayNames = new HashMap(); this.resultDiscardingRule = resultDiscardingRule; + this.scoreComparator = scoreComparator; } @Override @@ -186,7 +193,38 @@ public abstract class AbstractLeaderboardImpl implements Leaderboard { @Override public int getTrackedRank(Competitor competitor, RaceColumn race, TimePoint timePoint) throws NoWindException { final TrackedRace trackedRace = race.getTrackedRace(competitor); - return trackedRace == null ? 0 : trackedRace.hasStarted(timePoint) ? trackedRace.getRank(competitor, timePoint) : 0; + return trackedRace == null ? 0 + : trackedRace.hasStarted(timePoint) ? improveByDisqualificationsOfBetterRankedCompetitors(race, trackedRace, timePoint, trackedRace + .getRank(competitor, timePoint)) : 0; + } + + /** + * Per competitor disqualified ({@link ScoreCorrection} has a {@link MaxPointsReason} for the competitor), all + * competitors ranked worse by the tracking system need to have their rank corrected by one. + * @param trackedRace the race to which the rank refers; look for disqualifications / max points reasons in this column + * @param timePoint + * time point at which to consider disqualifications (not used yet because currently we don't remember + * when a competitor was disqualified) + * @param rank a competitors rank according to the tracking system + * + * @return the unmodified rank if no disqualifications for better-ranked competitors exist for race, + * or otherwise a rank improved (lowered) by the number of disqualifications of competitors whose tracked rank is better (lower) + * than rank. + */ + private int improveByDisqualificationsOfBetterRankedCompetitors(RaceColumn raceColumn, TrackedRace trackedRace, TimePoint timePoint, int rank) { + int correctedRank = rank; + List competitorsFromBestToWorst = trackedRace.getCompetitorsFromBestToWorst(timePoint); + int betterCompetitorRank=1; + Iterator ci = competitorsFromBestToWorst.iterator(); + while (betterCompetitorRank < rank && ci.hasNext()) { + Competitor betterTrackedCompetitor = ci.next(); + MaxPointsReason maxPointsReasonForBetterCompetitor = getScoreCorrection().getMaxPointsReason(betterTrackedCompetitor, raceColumn); + if (maxPointsReasonForBetterCompetitor != null && maxPointsReasonForBetterCompetitor != MaxPointsReason.NONE) { + correctedRank--; + } + betterCompetitorRank++; + } + return correctedRank; } @Override @@ -233,7 +271,8 @@ public abstract class AbstractLeaderboardImpl implements Leaderboard { boolean discarded = isDiscarded(competitor, race, timePoint); return new EntryImpl(trackedPoints, correctedResults.getCorrectedScore(), correctedResults.isCorrected(), discarded ? 0 - : correctedResults.getCorrectedScore() * (race.isMedalRace() ? 2 : 1), correctedResults.getMaxPointsReason(), discarded); + : correctedResults.getCorrectedScore() * (race.isMedalRace() ? MEDAL_RACE_FACTOR : 1), + correctedResults.getMaxPointsReason(), discarded); } @Override @@ -345,7 +384,7 @@ public abstract class AbstractLeaderboardImpl implements Leaderboard { } else { comparisonResult = netPointsAndFleet.get(o1).getB().compareTo(netPointsAndFleet.get(o2).getB()); if (comparisonResult == 0) { - comparisonResult = netPointsAndFleet.get(o1).getA() - netPointsAndFleet.get(o2).getA(); + comparisonResult = scoreComparator.compare(netPointsAndFleet.get(o1).getA(), netPointsAndFleet.get(o2).getA()); } } return comparisonResult; @@ -365,6 +404,6 @@ public abstract class AbstractLeaderboardImpl implements Leaderboard { } protected Comparator getTotalRankComparator(TimePoint timePoint) { - return new LeaderboardTotalRankComparator(this, timePoint); + return new LeaderboardTotalRankComparator(this, timePoint, scoreComparator); } } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/FlexibleLeaderboardImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/FlexibleLeaderboardImpl.java index 338e7a7a643..a8a990aae49 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/FlexibleLeaderboardImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/FlexibleLeaderboardImpl.java @@ -3,6 +3,7 @@ package com.sap.sailing.domain.leaderboard.impl; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.Comparator; import java.util.List; import java.util.logging.Logger; @@ -31,8 +32,8 @@ public class FlexibleLeaderboardImpl extends AbstractLeaderboardImpl implements private String name; public FlexibleLeaderboardImpl(String name, SettableScoreCorrection scoreCorrection, - ThresholdBasedResultDiscardingRule resultDiscardingRule) { - super(scoreCorrection, resultDiscardingRule); + ThresholdBasedResultDiscardingRule resultDiscardingRule, Comparator scoreComparator) { + super(scoreCorrection, resultDiscardingRule, scoreComparator); if (name == null) { throw new IllegalArgumentException("A leaderboard's name must not be null"); } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/LeaderboardTotalRankComparator.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/LeaderboardTotalRankComparator.java index 8e250308c5a..f4133e8c6d7 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/LeaderboardTotalRankComparator.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/LeaderboardTotalRankComparator.java @@ -46,11 +46,13 @@ import com.sap.sailing.domain.leaderboard.Leaderboard; public class LeaderboardTotalRankComparator implements Comparator { private final Leaderboard leaderboard; private final TimePoint timePoint; + private final Comparator scoreComparator; - public LeaderboardTotalRankComparator(Leaderboard leaderboard, TimePoint timePoint) { + public LeaderboardTotalRankComparator(Leaderboard leaderboard, TimePoint timePoint, Comparator scoreComparator) { super(); this.leaderboard = leaderboard; this.timePoint = timePoint; + this.scoreComparator = scoreComparator; } protected Leaderboard getLeaderboard() { @@ -202,19 +204,14 @@ public class LeaderboardTotalRankComparator implements Comparator { * the integer numbers by their natural ordering. */ protected Comparator getScoreComparator() { - return new Comparator() { - @Override - public int compare(Integer o1, Integer o2) { - return o1-o2; - } - }; + return scoreComparator; } /** * This implementation ranks a competitor better (lesser) if it has the lower score sum */ protected int compareByScoreSum(int o1ScoreSum, int o2ScoreSum) { - return o1ScoreSum - o2ScoreSum; + return getScoreComparator().compare(o1ScoreSum, o2ScoreSum); } /** diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/LowerScoreIsBetter.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/LowerScoreIsBetter.java new file mode 100755 index 00000000000..a3003932f1d --- /dev/null +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/LowerScoreIsBetter.java @@ -0,0 +1,20 @@ +package com.sap.sailing.domain.leaderboard.impl; + +import java.io.Serializable; +import java.util.Comparator; + +/** + * The score comparator as used by the ISAF standard scoring scheme. Lower scores are better. + * + * @author Axel Uhl (D043530) + * + */ +public class LowerScoreIsBetter implements Comparator, Serializable { + private static final long serialVersionUID = -2767385186133743330L; + + @Override + public int compare(Integer o1, Integer o2) { + return o1-o2; + } + +} diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/RaceColumnImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/RaceColumnImpl.java index f4051d853c0..a051df320a9 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/RaceColumnImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/RaceColumnImpl.java @@ -1,5 +1,9 @@ package com.sap.sailing.domain.leaderboard.impl; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + import com.sap.sailing.domain.base.Fleet; import com.sap.sailing.domain.base.impl.AbstractRaceColumn; import com.sap.sailing.domain.leaderboard.FlexibleRaceColumn; @@ -17,7 +21,12 @@ public class RaceColumnImpl extends AbstractRaceColumn implements FlexibleRaceCo public RaceColumnImpl(String name, boolean medalRace, Iterable fleets) { super(name); this.medalRace = medalRace; - this.fleets = fleets; + List myFleets = new ArrayList(); + for (Fleet fleet : fleets) { + myFleets.add(fleet); + } + Collections.sort(myFleets); + this.fleets = myFleets; } @Override diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/RegattaLeaderboardImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/RegattaLeaderboardImpl.java index f5d90cdbd10..912d26fce6f 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/RegattaLeaderboardImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/RegattaLeaderboardImpl.java @@ -1,6 +1,7 @@ package com.sap.sailing.domain.leaderboard.impl; import java.util.ArrayList; +import java.util.Comparator; import java.util.List; import com.sap.sailing.domain.base.Fleet; @@ -22,8 +23,9 @@ public class RegattaLeaderboardImpl extends AbstractLeaderboardImpl { private static final long serialVersionUID = 2370461218294770084L; private final Regatta regatta; - public RegattaLeaderboardImpl(Regatta regatta, SettableScoreCorrection scoreCorrection, ThresholdBasedResultDiscardingRule resultDiscardingRule) { - super(scoreCorrection, resultDiscardingRule); + public RegattaLeaderboardImpl(Regatta regatta, SettableScoreCorrection scoreCorrection, + ThresholdBasedResultDiscardingRule resultDiscardingRule, Comparator scoreComparator) { + super(scoreCorrection, resultDiscardingRule, scoreComparator); this.regatta = regatta; } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRace.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRace.java index 7d73617d284..dc3fc5692d8 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRace.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRace.java @@ -391,4 +391,10 @@ public interface TrackedRace extends Serializable { WindStore getWindStore(); Competitor getOverallLeader(TimePoint timePoint) throws NoWindException; + + /** + * Returns the competitors of this tracked race, according to their ranking. Competitors whose {@link #getRank(Competitor)} is 0 will + * be sorted "worst". + */ + List getCompetitorsFromBestToWorst(TimePoint timePoint); } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedLegImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedLegImpl.java index 0935ca786c5..807c0e16c7a 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedLegImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedLegImpl.java @@ -104,6 +104,7 @@ public class TrackedLegImpl implements TrackedLeg, RaceChangeListener { } // ensure that race isn't updated by events as we're tying to sort the competitors synchronized (getTrackedRace()) { + // TODO See bug 469; competitors already disqualified may need to be ranked worst Collections.sort(rankedCompetitorList, new WindwardToGoComparator(this, timePoint)); rankedCompetitorList = Collections.unmodifiableList(rankedCompetitorList); synchronized (competitorTracksOrderedByRank) { diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java index 72ee763abd4..d1e31d9b4b7 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java @@ -610,7 +610,7 @@ public abstract class TrackedRaceImpl implements TrackedRace, CourseListener { public synchronized Competitor getOverallLeader(TimePoint timePoint) throws NoWindException { try { Competitor result = null; - List ranks = getRanks(timePoint); + List ranks = getCompetitorsFromBestToWorst(timePoint); if (ranks != null && !ranks.isEmpty()) { result = ranks.iterator().next(); } @@ -623,13 +623,20 @@ public abstract class TrackedRaceImpl implements TrackedRace, CourseListener { @Override public synchronized int getRank(Competitor competitor, TimePoint timePoint) throws NoWindException { try { - return getRanks(timePoint).indexOf(competitor) + 1; + int result; + if (getMarkPassings(competitor).isEmpty()) { + result = 0; + } else { + result = getCompetitorsFromBestToWorst(timePoint).indexOf(competitor) + 1; + } + return result; } catch (NoWindError e) { throw e.getCause(); } } - private List getRanks(TimePoint timePoint) { + @Override + public List getCompetitorsFromBestToWorst(TimePoint timePoint) { synchronized (competitorRankings) { List rankedCompetitors = competitorRankings.get(timePoint); if (rankedCompetitors == null) { diff --git a/java/com.sap.sailing.feature.p2build/raceanalysis.product b/java/com.sap.sailing.feature.p2build/raceanalysis.product index eeae58e0148..b512bd4c1aa 100644 --- a/java/com.sap.sailing.feature.p2build/raceanalysis.product +++ b/java/com.sap.sailing.feature.p2build/raceanalysis.product @@ -44,7 +44,7 @@ - + diff --git a/java/com.sap.sailing.geocoding/src/com/sap/sailing/geocoding/impl/ReverseGeocoderImpl.java b/java/com.sap.sailing.geocoding/src/com/sap/sailing/geocoding/impl/ReverseGeocoderImpl.java index 4a6351fded5..5ae8488de53 100644 --- a/java/com.sap.sailing.geocoding/src/com/sap/sailing/geocoding/impl/ReverseGeocoderImpl.java +++ b/java/com.sap.sailing.geocoding/src/com/sap/sailing/geocoding/impl/ReverseGeocoderImpl.java @@ -194,7 +194,9 @@ public class ReverseGeocoderImpl implements ReverseGeocoder { private void cachePlacemarks(Position position, Double radius, List placemarks) { Collections.sort(placemarks, new Placemark.ByDistance(position)); if (position != null) { - cache.put(position, new Triple>(position, radius, placemarks)); + synchronized (cache) { + cache.put(position, new Triple>(position, radius, placemarks)); + } } } @@ -211,7 +213,9 @@ public class ReverseGeocoderImpl implements ReverseGeocoder { */ private void updateCachedPlacemarks(Position cachedPoint, Double newRadius, List newPlacemarks) { if (cachedPoint != null) { - cache.replace(cachedPoint, new Triple>(cachedPoint, newRadius, newPlacemarks)); + synchronized (cache) { + cache.replace(cachedPoint, new Triple>(cachedPoint, newRadius, newPlacemarks)); + } } } @@ -223,7 +227,9 @@ public class ReverseGeocoderImpl implements ReverseGeocoder { * {@link ReverseGeocoderImpl#POSITION_CACHE_DISTANCE_LIMIT the distance limit} */ private Triple> checkCache(Position position) { - return cache.get(position, POSITION_CACHE_DISTANCE_LIMIT); + synchronized (cache) { + return cache.get(position, POSITION_CACHE_DISTANCE_LIMIT); + } } private JSONArray callNearestService(Position position) throws MalformedURLException, IOException, ParseException { diff --git a/java/com.sap.sailing.gwt.ui/CommonControls.css b/java/com.sap.sailing.gwt.ui/CommonControls.css index 331a5f35522..b1628e024ac 100644 --- a/java/com.sap.sailing.gwt.ui/CommonControls.css +++ b/java/com.sap.sailing.gwt.ui/CommonControls.css @@ -143,15 +143,23 @@ color: #fff; } -.titleLabel { +.titleLabelWrapper { position: absolute; - width: 400px; - text-align: right; - font-family: 'UbuntuMedium', Arial, Verdana, sans-serif; - font-size: 16px; - color: #fff; - right: 5px; - top: 41px; + min-width: 400px; + text-align: right; + height: 68px; + right: 0; + top: 0; + font-family: 'UbuntuMedium', Arial, Verdana, sans-serif; + font-size: 16px; + color: #fff; + //background: url(images/titleLabelWrapper.png) right center no-repeat; +} + +.titleLabel { + background: url(images/titleLabelInner.png) left center no-repeat; + height: 26px; + padding: 41px 7px 0 130px; } @@ -297,9 +305,9 @@ position: fixed; width: 100%; top: 0; - background-image: url(images/header_bg_modern_eventname.png), url(images/white_gradient_header.png), url(images/header_bg_modern.png); - background-position: right top, center top, 0 0; - background-repeat: no-repeat, no-repeat, repeat-x; + background-image: url(images/white_gradient_header.png), url(images/header_bg_modern.png); + background-position: center top, 0 0; + background-repeat: no-repeat, repeat-x; box-shadow: 0 0 20px #858585; -moz-box-shadow: 0 0 20px #858585; -webkit-box-shadow: 0 0 20px #858585; diff --git a/java/com.sap.sailing.gwt.ui/RaceBoard.css b/java/com.sap.sailing.gwt.ui/RaceBoard.css index ff15fdeede2..1d21072b8b7 100644 --- a/java/com.sap.sailing.gwt.ui/RaceBoard.css +++ b/java/com.sap.sailing.gwt.ui/RaceBoard.css @@ -111,7 +111,45 @@ input.opencoloumn { right:0; } -.globalNavigation-home { +.globalNavigation-home { + border-radius: 0px 0px 4px 4px; + -moz-border-radius: 0px 0px 4px 4px; + -webkit-border-radius: 0px 0px 4px 4px; + margin: 0 10px 0 0; + float: left; +} + +.globalNavigation-home a{ + color: #fff; + text-decoration: none; + display: block; + background: url(images/globalNavigation-home_bg.png) 5px center no-repeat #7c7b7c; + padding: 5px 6px 5px 25px; + border-radius: 0px 0px 4px 4px; + -moz-border-radius: 0px 0px 4px 4px; + -webkit-border-radius: 0px 0px 4px 4px; +} + +.globalNavigation-leaderBoard { + border-radius: 0px 0px 4px 4px; + -moz-border-radius: 0px 0px 4px 4px; + -webkit-border-radius: 0px 0px 4px 4px; + margin: 0 5px 0 0; + float: left; +} + +.globalNavigation-leaderBoard a { + color: #fff; + text-decoration: none; + display: block; + background: url(images/globalNavigation-leaderBoard_bg.png) 5px center no-repeat #7c7b7c; + border-radius: 0px 0px 4px 4px; + -moz-border-radius: 0px 0px 4px 4px; + -webkit-border-radius: 0px 0px 4px 4px; + padding: 5px 6px 5px 25px; +} + +.globalNavigation-leaderBoardGroup { background: url(images/globalNavigation-home_bg.png) 5px center no-repeat #7c7b7c; border-radius: 0px 0px 4px 4px; -moz-border-radius: 0px 0px 4px 4px; @@ -121,33 +159,14 @@ input.opencoloumn { float: left; } -.globalNavigation-home a{ - color: #fff; - text-decoration: none; -} - -.globalNavigation-leaderBoard { - background: url(images/globalNavigation-leaderBoard_bg.png) 5px center no-repeat #7c7b7c; - border-radius: 0px 0px 4px 4px; - -moz-border-radius: 0px 0px 4px 4px; - -webkit-border-radius: 0px 0px 4px 4px; - padding: 5px 6px 5px 25px; - margin: 0 10px 0 0; - float: left; -} - -.globalNavigation-leaderBoard a { - color: #fff; - text-decoration: none; -} - -.globalNavigation-leaderBoardGroup { - background-color: gray; +.globalNavigation-leaderBoardGroup a { + color: #fff; + text-decoration: none; } .raceBoardNavigation { float: left; - margin: 3px;; + margin: 9px 0 0 7px; font-family: 'UbuntuLight', Arial, Verdana, sans-serif; } @@ -196,6 +215,40 @@ input.opencoloumn { .raceBoardNavigation-settingsButtonPanel { float: right; - margin: 3px;; + margin: 9px 12px 0 0; } +.raceBoardNavigation-settingsButton { + background: url(images/settings_white.png), url(images/btn-cta-orangeleft.png); + background-position: center, center 0; + background-repeat: no-repeat, repeat-x; + width: 27px; + height: 23px; + margin: 0 5px 0 1px; + -webkit-border-radius: 0px 3px 3px 0px; + border-radius: 0px 3px 3px 0px; +} + +.raceBoardNavigation-settingsButton:hover { + background-image: url(images/settings_white.png), url(images/btn-cta-orangeleft.png); + background-position: center, center -30px; + background-repeat: no-repeat, repeat-x; + cursor: pointer; +} + +.raceBoardNavigation-innerElement { + background: url("images/btn-cta-orangeleft.png") repeat-x scroll center 0 transparent; + border-bottom-left-radius: 3px; + border-top-left-radius: 3px; + color: #FFFFFF; + cursor: pointer; + font-family: UbuntuBold,Verdana,Arial,sans-serif; + font-size: 14px; + margin: 0 0 0 0; + padding: 2px 7px 2px 3px; + height: 19px; +} +.raceBoardNavigation-innerElement:hover { + background: url("images/btn-cta-orangeleft.png") repeat-x scroll center -30px transparent; + cursor: pointer; +} diff --git a/java/com.sap.sailing.gwt.ui/images/settings_new.png b/java/com.sap.sailing.gwt.ui/images/settings_new.png new file mode 100644 index 00000000000..bffe7fc9bdc Binary files /dev/null and b/java/com.sap.sailing.gwt.ui/images/settings_new.png differ diff --git a/java/com.sap.sailing.gwt.ui/images/settings_white.png b/java/com.sap.sailing.gwt.ui/images/settings_white.png new file mode 100755 index 00000000000..a2f50722abf Binary files /dev/null and b/java/com.sap.sailing.gwt.ui/images/settings_white.png differ diff --git a/java/com.sap.sailing.gwt.ui/images/titleLabelInner.png b/java/com.sap.sailing.gwt.ui/images/titleLabelInner.png new file mode 100644 index 00000000000..04c78e98c3c Binary files /dev/null and b/java/com.sap.sailing.gwt.ui/images/titleLabelInner.png differ diff --git a/java/com.sap.sailing.gwt.ui/images/titleLabelWrapper.png b/java/com.sap.sailing.gwt.ui/images/titleLabelWrapper.png new file mode 100644 index 00000000000..3881f73df40 Binary files /dev/null and b/java/com.sap.sailing.gwt.ui/images/titleLabelWrapper.png differ diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/LogoAndTitlePanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/LogoAndTitlePanel.java index 61dcb4b0b20..6df3aab5d9c 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/LogoAndTitlePanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/LogoAndTitlePanel.java @@ -22,9 +22,13 @@ public class LogoAndTitlePanel extends FlowPanel { sailingAnalyticsLabel.addStyleName("sailingAnalyticsLabel boldLabel"); this.add(labelPanel); + FlowPanel titleLabelWrapper = new FlowPanel(); + titleLabelWrapper.addStyleName("titleLabelWrapper"); titleLabel = new Label(title); titleLabel.addStyleName("titleLabel"); - this.add(titleLabel); + titleLabelWrapper.add(titleLabel); + this.add(titleLabelWrapper); + } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardPanel.java index 893cf08e8b3..91abf9b3b57 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardPanel.java @@ -45,9 +45,9 @@ import com.google.gwt.view.client.SelectionChangeEvent; import com.google.gwt.view.client.SelectionChangeEvent.Handler; import com.google.gwt.view.client.SelectionModel; import com.sap.sailing.domain.common.DetailType; -import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; import com.sap.sailing.domain.common.MaxPointsReason; import com.sap.sailing.domain.common.RaceIdentifier; +import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; import com.sap.sailing.gwt.ui.actions.AsyncActionsExecutor; import com.sap.sailing.gwt.ui.actions.GetLeaderboardByNameAction; import com.sap.sailing.gwt.ui.client.Collator; @@ -63,7 +63,6 @@ import com.sap.sailing.gwt.ui.client.Timer.PlayModes; import com.sap.sailing.gwt.ui.client.Timer.PlayStates; import com.sap.sailing.gwt.ui.client.UserAgentChecker.UserAgentTypes; import com.sap.sailing.gwt.ui.leaderboard.LegDetailColumn.LegDetailField; -import com.sap.sailing.gwt.ui.leaderboard.NetPointsComparator.SortOrderCalculator; import com.sap.sailing.gwt.ui.shared.CompetitorDTO; import com.sap.sailing.gwt.ui.shared.LeaderboardDTO; import com.sap.sailing.gwt.ui.shared.LeaderboardEntryDTO; @@ -478,17 +477,18 @@ public class LeaderboardPanel extends FormPanel implements TimeListener, PlaySta @Override public Comparator getComparator() { - if (race.isMedalRace()) { - return getLeaderboard().getMedalRaceComparator(race.getRaceColumnName()); - } else { - SortOrderCalculator sortOrderCalculator = new SortOrderCalculator() { - @Override - public boolean isAscending() { - return isSortedAscendingForThisColumn(getLeaderboardPanel().getLeaderboardTable()); - } - }; - return new NetPointsComparator(sortOrderCalculator, race.getRaceColumnName()); - } + return new Comparator() { + @Override + public int compare(LeaderboardRowDTO o1, LeaderboardRowDTO o2) { + List competitorsFromBestToWorst = getLeaderboard().getCompetitorsFromBestToWorst(race); + boolean ascending = isSortedAscendingForThisColumn(getLeaderboardPanel().getLeaderboardTable()); + int o1Rank = competitorsFromBestToWorst.indexOf(o1.competitor) + 1; + int o2Rank = competitorsFromBestToWorst.indexOf(o2.competitor) + 1; + return o1Rank == 0 ? o2Rank == 0 ? 0 + : ascending ? 1 : -1 : o2Rank == 0 ? ascending ? -1 : 1 + : o1Rank - o2Rank; + } + }; } @Override diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LegColumn.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LegColumn.java index d851e07deb3..49980317811 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LegColumn.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LegColumn.java @@ -326,7 +326,7 @@ public class LegColumn extends ExpandableSortableColumn { @Override public String getValue(LeaderboardRowDTO row) { LegEntryDTO legEntry = getLegEntry(row); - if (legEntry != null) { + if (legEntry != null && legEntry.rank != 0) { return ""+legEntry.rank; } else { return ""; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/NetPointsComparator.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/NetPointsComparator.java deleted file mode 100755 index 9935ba572b3..00000000000 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/NetPointsComparator.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.sap.sailing.gwt.ui.leaderboard; - -import java.util.Comparator; - -import com.sap.sailing.gwt.ui.shared.LeaderboardEntryDTO; -import com.sap.sailing.gwt.ui.shared.LeaderboardRowDTO; - -public class NetPointsComparator implements Comparator { - private final SortOrderCalculator sortOrderCalculator; - private final String raceName; - - public interface SortOrderCalculator { - boolean isAscending(); - } - - public NetPointsComparator(SortOrderCalculator sortOrderCalculator, String raceName) { - super(); - this.sortOrderCalculator = sortOrderCalculator; - this.raceName = raceName; - } - - /** - * sorts a meaningless 0 value to the bottom always, regardless whether ascending/descending sorting is - * requested - */ - @Override - public int compare(LeaderboardRowDTO o1, LeaderboardRowDTO o2) { - boolean ascending = sortOrderCalculator.isAscending(); - LeaderboardEntryDTO o1Entry = o1.fieldsByRaceName.get(raceName); - LeaderboardEntryDTO o2Entry = o2.fieldsByRaceName.get(raceName); - return (o1Entry == null || o1Entry.netPoints == 0) ? (o2Entry == null || o2Entry.netPoints == 0) ? 0 - : ascending ? 1 : -1 : (o2Entry == null || o2Entry.netPoints == 0) ? ascending ? -1 : 1 - : o1Entry.netPoints - o2Entry.netPoints; - } -} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/GlobalNavigationPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/GlobalNavigationPanel.java index 80bed38226f..6c196e09185 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/GlobalNavigationPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/GlobalNavigationPanel.java @@ -34,12 +34,12 @@ public class GlobalNavigationPanel extends FlowPanel { String homeLink = spectatorViewLink; if(showHomeNavigation) { - addNavigationLink(stringMessages.home(), homeLink, "home"); - } - - if (leaderboardGroupName != null && !leaderboardGroupName.isEmpty()) { - String leaderBoardGroupLink = spectatorViewLink + "?leaderboardGroupName=" + leaderboardGroupName; - addNavigationLink(leaderboardGroupName, leaderBoardGroupLink, "leaderBoardGroup"); + if (leaderboardGroupName != null && !leaderboardGroupName.isEmpty()) { + String leaderBoardGroupLink = spectatorViewLink + "?leaderboardGroupName=" + leaderboardGroupName; + addNavigationLink(leaderboardGroupName, leaderBoardGroupLink, "leaderBoardGroup", "Go to the Event overview."); + } else { + addNavigationLink(stringMessages.home(), homeLink, "home", "Go to the Event overview."); + } } if (leaderboardName != null && !leaderboardName.isEmpty()) { @@ -47,11 +47,12 @@ public class GlobalNavigationPanel extends FlowPanel { if (leaderboardGroupName != null && !leaderboardGroupName.isEmpty()) { leaderBoardLink += "&leaderboardGroupName=" + leaderboardGroupName; } - addNavigationLink(leaderboardName, leaderBoardLink, "leaderBoard"); + addNavigationLink(leaderboardName, leaderBoardLink, "leaderBoard", "Go to the overview and see all Races in one Leaderboard"); } } - private void addNavigationLink(String linkName, String linkUrl, String styleNameExtension) { + private void addNavigationLink(String linkName, String linkUrl, String styleNameExtension, String htmlTitle) { + String setHtmlTitle = htmlTitle; String url = linkUrl; if(debugParam != null && !debugParam.isEmpty()) { url += url.contains("?") ? "&" : "?"; @@ -60,6 +61,7 @@ public class GlobalNavigationPanel extends FlowPanel { HTML linkHtml = new HTML(ANCHORTEMPLATE.anchor(URLFactory.INSTANCE.encode(url), linkName)); linkHtml.addStyleName(STYLE_NAME_PREFIX + styleNameExtension); + linkHtml.setTitle(setHtmlTitle); add(linkHtml); } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardEntryPoint.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardEntryPoint.java index 8b3c62b6f8c..b93fbd3d7e0 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardEntryPoint.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardEntryPoint.java @@ -183,9 +183,7 @@ public class RaceBoardEntryPoint extends AbstractEntryPoint { FlowPanel toolbarPanel = new FlowPanel(); - //TODO Quickfix for touch devices toolbarPanel.add(raceBoardPanel.getNavigationWidget()); - toolbarPanel.add(raceBoardPanel.getSettingsWidget()); FlowPanel logoAndTitlePanel = createLogoAndTitlePanel(raceBoardPanel); FlowPanel timePanel = createTimePanel(raceBoardPanel); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardPanel.java index 28b72996238..cee8672da8a 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardPanel.java @@ -17,7 +17,6 @@ import com.google.gwt.user.client.ui.Button; import com.google.gwt.user.client.ui.CheckBox; import com.google.gwt.user.client.ui.FlowPanel; import com.google.gwt.user.client.ui.FormPanel; -import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.MenuBar; import com.google.gwt.user.client.ui.ToggleButton; import com.google.gwt.user.client.ui.Widget; @@ -175,23 +174,10 @@ public class RaceBoardPanel extends FormPanel implements RegattaDisplayer, RaceS mainPanel.add(componentViewer.getViewerWidget()); } - settingsPanel = new FlowPanel(); - settingsPanel.addStyleName("raceBoardNavigation-settingsButtonPanel"); - Label settingsLabel = new Label("Settings: "); - settingsLabel.addStyleName("raceBoardNavigation-settingsLabel"); - settingsLabel.getElement().getStyle().setFloat(Style.Float.LEFT); - settingsLabel.getElement().getStyle().setPadding(3, Style.Unit.PX); - settingsPanel.add(settingsLabel); - - addSettingsMenuButton(settingsPanel, leaderboardPanel); - addSettingsMenuButton(settingsPanel, raceMap); - addSettingsMenuButton(settingsPanel, windChart); - addSettingsMenuButton(settingsPanel, competitorChart); - - addComponentAsCheckboxToNavigationMenu(leaderboardAndMapViewer, leaderboardPanel); - addComponentAsCheckboxToNavigationMenu(leaderboardAndMapViewer, windChart); - addComponentAsCheckboxToNavigationMenu(leaderboardAndMapViewer, competitorChart); - + addComponentToNavigationMenu(leaderboardAndMapViewer, leaderboardPanel, true); + addComponentToNavigationMenu(leaderboardAndMapViewer, windChart, true); + addComponentToNavigationMenu(leaderboardAndMapViewer, competitorChart, true); + addComponentToNavigationMenu(leaderboardAndMapViewer, raceMap, false); } @SuppressWarnings("unused") @@ -205,23 +191,6 @@ public class RaceBoardPanel extends FormPanel implements RegattaDisplayer, RaceS } } - private void addSettingsMenuButton(FlowPanel settingsPanel, final Component component) { - if(component.hasSettings()) { - Button settingsButton = new Button(component.getLocalizedShortName()); - settingsButton.addClickHandler(new ClickHandler() { - @Override - public void onClick(ClickEvent event) { - new SettingsDialog(component, stringMessages).show(); - } - }); - settingsButton.addStyleName("raceBoardNavigation-settingsButton"); - settingsButton.getElement().getStyle().setFloat(Style.Float.LEFT); - settingsButton.getElement().getStyle().setPadding(3, Style.Unit.PX); - - settingsPanel.add(settingsButton); - } - } - private LeaderboardPanel createLeaderboardPanel(String leaderboardName, String leaderboardGroupName) { LeaderboardSettings leaderBoardSettings = LeaderboardSettingsFactory.getInstance() .createNewSettingsForPlayMode(timer.getPlayMode(), /* nameOfRaceToSort */ @@ -232,13 +201,15 @@ public class RaceBoardPanel extends FormPanel implements RegattaDisplayer, RaceS userAgentType); } - private void addComponentAsCheckboxToNavigationMenu(final ComponentViewer componentViewer, - final Component component) { + private void addComponentToNavigationMenu(final ComponentViewer componentViewer, + final Component component, boolean withCheckbox) { final CheckBox checkBox= new CheckBox(component.getLocalizedShortName()); checkBox.getElement().getStyle().setFloat(Style.Float.LEFT); - + + checkBox.setEnabled(withCheckbox); checkBox.setValue(component.isVisible()); checkBox.setTitle(stringMessages.showHideComponent(component.getLocalizedShortName())); + checkBox.addStyleName("raceBoardNavigation-innerElement"); checkBox.addValueChangeHandler(new ValueChangeHandler() { @Override @@ -259,6 +230,22 @@ public class RaceBoardPanel extends FormPanel implements RegattaDisplayer, RaceS }); componentsNavigationPanel.add(checkBox); + + if(component.hasSettings()) { + Button settingsButton = new Button(""); + settingsButton.addClickHandler(new ClickHandler() { + @Override + public void onClick(ClickEvent event) { + new SettingsDialog(component, stringMessages).show(); + } + }); + settingsButton.addStyleName("raceBoardNavigation-settingsButton"); + settingsButton.getElement().getStyle().setFloat(Style.Float.LEFT); + settingsButton.setTitle(stringMessages.settingsForComponent(component.getLocalizedShortName())); + + componentsNavigationPanel.add(settingsButton); + } + } public void addComponentAsToogleButtonToNavigationMenu(final ComponentViewer componentViewer, diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java index 6417a74b14b..c1231aea193 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java @@ -19,6 +19,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.WeakHashMap; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; @@ -214,8 +215,11 @@ public class SailingServiceImpl extends RemoteServiceServlet implements SailingS private final Executor executor; + private final WeakHashMap weakCompetitorDTOCache; + public SailingServiceImpl() { BundleContext context = Activator.getDefault(); + weakCompetitorDTOCache = new WeakHashMap(); racingEventServiceTracker = createAndOpenRacingEventServiceTracker(context); replicationServiceTracker = createAndOpenReplicationServiceTracker(context); mongoObjectFactory = MongoFactory.INSTANCE.getDefaultMongoObjectFactory(); @@ -258,7 +262,7 @@ public class SailingServiceImpl extends RemoteServiceServlet implements SailingS result.name = leaderboard.getName(); result.competitorDisplayNames = new HashMap(); for (RaceColumn raceColumn : leaderboard.getRaceColumns()) { - result.createEmptyRaceColumn(raceColumn.getName(), raceColumn.isMedalRace()); + RaceColumnDTO raceColumnDTO = result.createEmptyRaceColumn(raceColumn.getName(), raceColumn.isMedalRace()); for (Fleet fleet : raceColumn.getFleets()) { RegattaAndRaceIdentifier raceIdentifier = null; TrackedRace trackedRace = raceColumn.getTrackedRace(fleet); @@ -269,6 +273,7 @@ public class SailingServiceImpl extends RemoteServiceServlet implements SailingS result.addRace(raceColumn.getName(), fleet.getName(), raceColumn.isMedalRace(), raceIdentifier, /* StrippedRaceDTO */ null); } + result.setCompetitorsFromBestToWorst(raceColumnDTO, getCompetitorDTOList(leaderboard.getCompetitorsFromBestToWorst(raceColumn, timePoint))); } result.rows = new HashMap(); result.hasCarriedPoints = leaderboard.hasCarriedPoints(); @@ -285,15 +290,15 @@ public class SailingServiceImpl extends RemoteServiceServlet implements SailingS final Entry entry = leaderboard.getEntry(competitor, raceColumn, timePoint); RunnableFuture future = new FutureTask(new Callable() { @Override - public LeaderboardEntryDTO call() { - try { - return getLeaderboardEntryDTO(entry, raceColumn.getTrackedRace(competitor), competitor, timePoint, - namesOfRaceColumnsForWhichToLoadLegDetails != null - && namesOfRaceColumnsForWhichToLoadLegDetails.contains(raceColumn.getName())); - } catch (NoWindException e) { - throw new NoWindError(e); - } - } + public LeaderboardEntryDTO call() { + try { + return getLeaderboardEntryDTO(entry, raceColumn.getTrackedRace(competitor), competitor, timePoint, + namesOfRaceColumnsForWhichToLoadLegDetails != null + && namesOfRaceColumnsForWhichToLoadLegDetails.contains(raceColumn.getName())); + } catch (NoWindException e) { + throw new NoWindError(e); + } + } }); executor.execute(future); futuresForColumnName.put(raceColumn.getName(), future); @@ -319,6 +324,14 @@ public class SailingServiceImpl extends RemoteServiceServlet implements SailingS return result; } + private List getCompetitorDTOList(List competitorsFromBestToWorst) { + List result = new ArrayList(); + for (Competitor competitor : competitorsFromBestToWorst) { + result.add(getCompetitorDTO(competitor)); + } + return result; + } + @Override public void stressTestLeaderboardByName(String leaderboardName, int times) throws Exception { Leaderboard leaderboard = getService().getLeaderboardByName(leaderboardName); @@ -474,12 +487,17 @@ public class SailingServiceImpl extends RemoteServiceServlet implements SailingS } private CompetitorDTO getCompetitorDTO(Competitor c) { - CountryCode countryCode = c.getTeam().getNationality().getCountryCode(); - CompetitorDTO competitorDTO = new CompetitorDTO(c.getName(), countryCode == null ? "" - : countryCode.getTwoLetterISOCode(), countryCode == null ? "" : countryCode.getThreeLetterIOCCode(), - countryCode == null ? "" : countryCode.getName(), c.getBoat().getSailID(), c.getId().toString(), - new BoatClassDTO(c.getBoat().getBoatClass().getName(), c.getBoat().getBoatClass().getHullLength() - .getMeters())); + CompetitorDTO competitorDTO = weakCompetitorDTOCache.get(c); + if (competitorDTO == null) { + CountryCode countryCode = c.getTeam().getNationality().getCountryCode(); + competitorDTO = new CompetitorDTO(c.getName(), countryCode == null ? "" + : countryCode.getTwoLetterISOCode(), + countryCode == null ? "" : countryCode.getThreeLetterIOCCode(), countryCode == null ? "" + : countryCode.getName(), c.getBoat().getSailID(), c.getId().toString(), + new BoatClassDTO(c.getBoat().getBoatClass().getName(), c.getBoat().getBoatClass().getHullLength() + .getMeters())); + weakCompetitorDTOCache.put(c, competitorDTO); + } return competitorDTO; } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/AbstractLeaderboardDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/AbstractLeaderboardDTO.java index 5c400e5be3c..7cbb5a1e0f3 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/AbstractLeaderboardDTO.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/AbstractLeaderboardDTO.java @@ -2,7 +2,6 @@ package com.sap.sailing.gwt.ui.shared; import java.util.ArrayList; import java.util.Arrays; -import java.util.Comparator; import java.util.Date; import java.util.List; import java.util.Map; @@ -32,10 +31,6 @@ public abstract class AbstractLeaderboardDTO implements IsSerializable { } } - public Comparator getMedalRaceComparator(String medalRaceName) { - return new MedalRaceComparator(medalRaceName); - } - /** * If the race whose name is specified in raceName has any competitor who has valid * {@link LeaderboardEntryDTO#legDetails} for that race, the number of entries in the leg details is returned, @@ -72,39 +67,6 @@ public abstract class AbstractLeaderboardDTO implements IsSerializable { return totalPoints; } - private class MedalRaceComparator implements Comparator { - private final String medalRaceName; - - public MedalRaceComparator(String medalRaceName) { - this.medalRaceName = medalRaceName; - } - - @Override - public int compare(LeaderboardRowDTO o1, LeaderboardRowDTO o2) { - int result; - if (scoredInMedalRace(o1.competitor)) { - if (scoredInMedalRace(o2.competitor)) { - // both scored in medal race - result = o1.fieldsByRaceName.get(medalRaceName).netPoints - - o2.fieldsByRaceName.get(medalRaceName).netPoints; - } else { - // only o1 scored in medal race, so o1 scores better = "less" - result = -1; - } - } else { - if (scoredInMedalRace(o2.competitor)) { - // only o2 scored in medal race, so o2 scores better, o1 scores worse = "greater" - result = 1; - } else { - // neither one scored in any medal race; to be considered equal for medal race comparison - result = 0; - } - - } - return result; - } - } - public int getNetPoints(CompetitorDTO competitor, String nameOfLastRaceSoFar) { int result = 0; LeaderboardRowDTO row = rows.get(competitor); @@ -186,11 +148,12 @@ public abstract class AbstractLeaderboardDTO implements IsSerializable { races.add(index, raceInLeaderboardDTO); } - public void createEmptyRaceColumn(String raceColumnName, boolean medalRace) { - RaceColumnDTO raceInLeaderboardDTO = new RaceColumnDTO(); - raceInLeaderboardDTO.setRaceColumnName(raceColumnName); - raceInLeaderboardDTO.setMedalRace(medalRace); - races.add(raceInLeaderboardDTO); + public RaceColumnDTO createEmptyRaceColumn(String raceColumnName, boolean medalRace) { + RaceColumnDTO raceColumn = new RaceColumnDTO(); + raceColumn.setRaceColumnName(raceColumnName); + raceColumn.setMedalRace(medalRace); + races.add(raceColumn); + return raceColumn; } protected RaceColumnDTO getRaceInLeaderboardByName(String raceColumnName) { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/LeaderboardDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/LeaderboardDTO.java index 1c83bdd6c7b..7a45bcf2224 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/LeaderboardDTO.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/LeaderboardDTO.java @@ -1,6 +1,8 @@ package com.sap.sailing.gwt.ui.shared; +import java.util.HashMap; import java.util.List; +import java.util.Map; import com.google.gwt.user.client.rpc.IsSerializable; @@ -17,8 +19,20 @@ public class LeaderboardDTO extends AbstractLeaderboardDTO implements IsSerializ * The competitor list, ordered ascending by total rank */ public List competitors; + + private Map> competitorOrderingPerRace; - public LeaderboardDTO() {} + public LeaderboardDTO() { + competitorOrderingPerRace = new HashMap>(); + } + + public void setCompetitorsFromBestToWorst(RaceColumnDTO raceColumn, List orderedCompetitors) { + competitorOrderingPerRace.put(raceColumn, orderedCompetitors); + } + + public List getCompetitorsFromBestToWorst(RaceColumnDTO raceColumn) { + return competitorOrderingPerRace.get(raceColumn); + } public int getRank(CompetitorDTO competitor) { return competitors.indexOf(competitor) + 1; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/LegEntryDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/LegEntryDTO.java index f657164a402..0a18f9310b0 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/LegEntryDTO.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/LegEntryDTO.java @@ -13,7 +13,7 @@ import com.google.gwt.user.client.rpc.IsSerializable; public class LegEntryDTO implements IsSerializable { public Double distanceTraveledInMeters; public Double averageSpeedOverGroundInKnots; - public Integer rank; + public int rank; public Double gapToLeaderInSeconds; public Double estimatedTimeToNextWaypointInSeconds; @@ -57,7 +57,7 @@ public class LegEntryDTO implements IsSerializable { result = prime * result + ((numberOfJibes == null) ? 0 : numberOfJibes.hashCode()); result = prime * result + ((numberOfPenaltyCircles == null) ? 0 : numberOfPenaltyCircles.hashCode()); result = prime * result + ((numberOfTacks == null) ? 0 : numberOfTacks.hashCode()); - result = prime * result + ((rank == null) ? 0 : rank.hashCode()); + result = prime * result + rank; result = prime * result + (started ? 1231 : 1237); result = prime * result + (int) (timeInMilliseconds ^ (timeInMilliseconds >>> 32)); result = prime * result + ((velocityMadeGoodInKnots == null) ? 0 : velocityMadeGoodInKnots.hashCode()); @@ -116,10 +116,7 @@ public class LegEntryDTO implements IsSerializable { return false; } else if (!numberOfTacks.equals(other.numberOfTacks)) return false; - if (rank == null) { - if (other.rank != null) - return false; - } else if (!rank.equals(other.rank)) + if (rank != other.rank) return false; if (started != other.started) return false; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/charts/AbstractChartPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/charts/AbstractChartPanel.java index 5083b037901..aeb57d5d35d 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/charts/AbstractChartPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/charts/AbstractChartPanel.java @@ -79,6 +79,12 @@ implements CompetitorSelectionChangeListener, RequiresResize { private long stepSize = 5000; private DetailType dataToShow; private final CompetitorSelectionProvider competitorSelectionProvider; + + /** + * When the chart is not visible and the competitor selection changes, no loading is performed. However, in this case + * we need to remember that loading will be necessary as soon as the chart becomes visible. + */ + private boolean needToLoadWhenMadeVisible; public AbstractChartPanel(SailingServiceAsync sailingService, AsyncActionsExecutor asyncActionsExecutor, CompetitorSelectionProvider competitorSelectionProvider, RaceSelectionProvider raceSelectionProvider, @@ -104,6 +110,14 @@ implements CompetitorSelectionChangeListener, RequiresResize { timer.addTimeListener(this); timeZoomProvider.addTimeZoomChangeListener(this); } + + @Override + public void setVisible(boolean visible) { + super.setVisible(visible); + if (needToLoadWhenMadeVisible) { + loadData(/* showBusyIndicator */ true); + } + } /** * Creates a new chart for the given {@link DetailType} dataToShow and also @@ -120,7 +134,6 @@ implements CompetitorSelectionChangeListener, RequiresResize { .setMarginRight(65) .setBorderColor(new Color("#A6A6A6")) .setBorderWidth(1) -// .setBackgroundColor(new Color("#C6C6C6")) .setChartSubtitle(new ChartSubtitle().setText(stringMessages.clickAndDragToZoomIn())) .setLinePlotOptions(new LinePlotOptions().setLineWidth(LINE_WIDTH).setMarker(new Marker().setEnabled(false).setHoverState( new Marker().setEnabled(true).setRadius(4))).setShadow(false) @@ -211,60 +224,70 @@ implements CompetitorSelectionChangeListener, RequiresResize { * @param showBusyIndicator If true is the busy indicator shown while loading the data from the server. */ protected void loadData(boolean showBusyIndicator) { - if (needsDataLoading()) { - if (showBusyIndicator) { - setWidget(chart); - showLoading("Loading competitor data..."); - } - if (chartData == null || chartData.getDetailType() != getDataToShow()) { - chartData = new MultiCompetitorRaceDataDTO(getDataToShow()); - } - -// Date toDate = new Date(System.currentTimeMillis() - timer.getLivePlayDelayInMillis()); - final ArrayList> dataQuery = new ArrayList>(); - for (CompetitorDTO competitor : getVisibleCompetitors()) { - Date chartDataDateOfNewestData = chartData.getDateOfNewestData(); - Date competitorDateOfNewestData = chartData.contains(competitor) ? chartData.getCompetitorData(competitor).getDateOfNewestData() : null; - if (!chartData.contains(competitor)) { - dataQuery.add(new Pair(new Date(0), competitor)); - } else if (competitorDateOfNewestData.before(chartDataDateOfNewestData) || competitorDateOfNewestData.before(maxTimepoint)) { - dataQuery.add(new Pair(new Date(competitorDateOfNewestData.getTime() + getStepSize()), competitor)); + if (isVisible()) { + if (hasVisibleCompetitors()) { + needToLoadWhenMadeVisible = false; // we're loading it now + if (showBusyIndicator) { + setWidget(chart); + showLoading("Loading competitor data..."); + } + if (chartData == null || chartData.getDetailType() != getDataToShow()) { + chartData = new MultiCompetitorRaceDataDTO(getDataToShow()); } - } - - GetCompetitorsRaceDataAction getCompetitorsRaceDataAction = new GetCompetitorsRaceDataAction(sailingService, - selectedRaceIdentifier, dataQuery, maxTimepoint, getStepSize(), getDataToShow(), - new AsyncCallback() { - @Override - public void onFailure(Throwable caught) { - errorReporter.reportError(getStringMessages().failedToLoadRaceData() + ": " + caught.toString(), + // Date toDate = new Date(System.currentTimeMillis() - timer.getLivePlayDelayInMillis()); + final ArrayList> dataQuery = new ArrayList>(); + for (CompetitorDTO competitor : getVisibleCompetitors()) { + Date chartDataDateOfNewestData = chartData.getDateOfNewestData(); + Date competitorDateOfNewestData = chartData.contains(competitor) ? chartData.getCompetitorData( + competitor).getDateOfNewestData() : null; + if (!chartData.contains(competitor)) { + dataQuery.add(new Pair(new Date(0), competitor)); + } else if (competitorDateOfNewestData.before(chartDataDateOfNewestData) + || competitorDateOfNewestData.before(maxTimepoint)) { + dataQuery.add(new Pair(new Date(competitorDateOfNewestData.getTime() + + getStepSize()), competitor)); + } + } + + GetCompetitorsRaceDataAction getCompetitorsRaceDataAction = new GetCompetitorsRaceDataAction( + sailingService, selectedRaceIdentifier, dataQuery, maxTimepoint, getStepSize(), + getDataToShow(), new AsyncCallback() { + + @Override + public void onFailure(Throwable caught) { + errorReporter.reportError( + getStringMessages().failedToLoadRaceData() + ": " + caught.toString(), timer.getPlayMode() == PlayModes.Live); hideLoading(); - } + } - @Override - public void onSuccess(MultiCompetitorRaceDataDTO result) { - if (result != null) { - for (CompetitorRaceDataDTO competitorData : result.getAllRaceData()) { - if (chartData.contains(competitorData.getCompetitor())) { - chartData.addCompetitorRaceData(competitorData); - chartData.setCompetitorMarkPassingsData(competitorData); - } else { - chartData.setCompetitorData(competitorData.getCompetitor(), competitorData); + @Override + public void onSuccess(MultiCompetitorRaceDataDTO result) { + if (result != null) { + for (CompetitorRaceDataDTO competitorData : result.getAllRaceData()) { + if (chartData.contains(competitorData.getCompetitor())) { + chartData.addCompetitorRaceData(competitorData); + chartData.setCompetitorMarkPassingsData(competitorData); + } else { + chartData.setCompetitorData(competitorData.getCompetitor(), competitorData); + } } } - } - chart.getXAxis().setMin(minTimepoint.getTime()); - chart.getXAxis().setMax(maxTimepoint.getTime()); + chart.getXAxis().setMin(minTimepoint.getTime()); + chart.getXAxis().setMax(maxTimepoint.getTime()); - drawChartData(); - chart.hideLoading(); - } - }); - asyncActionsExecutor.execute(getCompetitorsRaceDataAction); + drawChartData(); + hideLoading(); + } + }); + asyncActionsExecutor.execute(getCompetitorsRaceDataAction); + } else { + setWidget(noCompetitorsSelectedLabel); + } } else { - setWidget(noCompetitorsSelectedLabel); + // not visible; don't load data but remember that data needs to be loaded as soon as the chart becomes visible + needToLoadWhenMadeVisible = true; } } @@ -570,10 +593,6 @@ implements CompetitorSelectionChangeListener, RequiresResize { } } - private boolean needsDataLoading() { - return hasVisibleCompetitors() && isVisible(); - } - @Override public void onResize() { if(getChartData() != null) { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/charts/RaceChart.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/charts/RaceChart.java index 9874c48b250..25dac11633d 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/charts/RaceChart.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/charts/RaceChart.java @@ -107,7 +107,7 @@ public abstract class RaceChart extends SimplePanel implements RaceTimesInfoProv } protected void hideLoading() { - if(timer.getPlayMode() != PlayModes.Live) { + if (timer.getPlayMode() != PlayModes.Live) { chart.hideLoading(); } isLoading = false; @@ -134,14 +134,9 @@ public abstract class RaceChart extends SimplePanel implements RaceTimesInfoProv } protected boolean onClick(ChartClickEvent chartClickEvent) { - if(!isLoading && !isZoomed) { - // when the user zooms into the chart first a AxisSetExtremesEvent is sent and a ChartClickEvent afterwards -// if (ignoreClickOnce) { -// ignoreClickOnce = false; -// } else { - timer.setPlayMode(PlayModes.Replay); - timer.setTime(chartClickEvent.getXAxisValueAsLong()); -// } + if (!isLoading && !isZoomed) { + timer.setPlayMode(PlayModes.Replay); + timer.setTime(chartClickEvent.getXAxisValueAsLong()); } return true; } @@ -168,7 +163,7 @@ public abstract class RaceChart extends SimplePanel implements RaceTimesInfoProv * visibility state. */ protected void useCheckboxesToShowAndHide(final Chart chart) { - chart.setLegend(new Legend().setEnabled(true).setBorderWidth(0).setSymbolPadding(20)); // make room for checkbox + chart.setLegend(new Legend().setEnabled(true).setBorderWidth(0).setSymbolPadding(25)); // make room for checkbox chart.setSeriesPlotOptions(new SeriesPlotOptions().setSeriesCheckboxClickEventHandler(new SeriesCheckboxClickEventHandler() { @Override public boolean onClick(SeriesCheckboxClickEvent seriesCheckboxClickEvent) { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/controls/slider/SliderBar.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/controls/slider/SliderBar.java index d52af6c4db7..a0a9fdc12b3 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/controls/slider/SliderBar.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/controls/slider/SliderBar.java @@ -103,7 +103,7 @@ public class SliderBar extends FocusPanel implements RequiresResize, HasValue leaderboards = new ArrayList<>(); Leaderboard leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[0], new ScoreCorrectionImpl(), - new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces)); + new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowerScoreIsBetter()); leaderboards.add(leaderboard); leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[1], new ScoreCorrectionImpl(), - new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces)); + new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowerScoreIsBetter()); leaderboards.add(leaderboard); leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[2], new ScoreCorrectionImpl(), - new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces)); + new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowerScoreIsBetter()); leaderboards.add(leaderboard); leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[3], new ScoreCorrectionImpl(), - new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces)); + new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowerScoreIsBetter()); leaderboards.add(leaderboard); final LeaderboardGroup leaderboardGroup = new LeaderboardGroupImpl(groupName, groupDescription, leaderboards); @@ -80,10 +81,10 @@ public class TestStoringAndRetrievingLeaderboardGroups extends AbstractMongoDBTe final ArrayList leaderboards = new ArrayList<>(); Leaderboard leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[0], new ScoreCorrectionImpl(), - new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces)); + new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowerScoreIsBetter()); leaderboards.add(leaderboard); leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[1], new ScoreCorrectionImpl(), - new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces)); + new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowerScoreIsBetter()); leaderboards.add(leaderboard); final LeaderboardGroup leaderboardGroup = new LeaderboardGroupImpl(groupName, groupDescription, leaderboards); @@ -93,11 +94,11 @@ public class TestStoringAndRetrievingLeaderboardGroups extends AbstractMongoDBTe final Leaderboard[] ungroupedLeaderboards = { new FlexibleLeaderboardImpl(ungroupedLeaderboardNames[0], new ScoreCorrectionImpl(), - new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces)), + new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowerScoreIsBetter()), new FlexibleLeaderboardImpl(ungroupedLeaderboardNames[1], new ScoreCorrectionImpl(), - new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces)), + new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowerScoreIsBetter()), new FlexibleLeaderboardImpl(ungroupedLeaderboardNames[2], new ScoreCorrectionImpl(), - new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces)) }; + new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowerScoreIsBetter()) }; mongoObjectFactory.storeLeaderboard(ungroupedLeaderboards[0]); mongoObjectFactory.storeLeaderboard(ungroupedLeaderboards[1]); mongoObjectFactory.storeLeaderboard(ungroupedLeaderboards[2]); @@ -133,7 +134,7 @@ public class TestStoringAndRetrievingLeaderboardGroups extends AbstractMongoDBTe final String groupDescription = "A leaderboard group"; final ArrayList leaderboards = new ArrayList<>(); - final FlexibleLeaderboard leaderboard = new FlexibleLeaderboardImpl(leaderboardName, new ScoreCorrectionImpl(), new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces)); + final FlexibleLeaderboard leaderboard = new FlexibleLeaderboardImpl(leaderboardName, new ScoreCorrectionImpl(), new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowerScoreIsBetter()); final Fleet fleet = leaderboard.getFleet(null); final RaceColumn race = leaderboard.addRaceColumn(columnName, false, fleet); leaderboards.add(leaderboard); diff --git a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndRetrievingLeaderboards.java b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndRetrievingLeaderboards.java index f9a899ce780..d90a6701062 100755 --- a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndRetrievingLeaderboards.java +++ b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndRetrievingLeaderboards.java @@ -22,6 +22,7 @@ import com.sap.sailing.domain.common.impl.Util; import com.sap.sailing.domain.leaderboard.FlexibleLeaderboard; import com.sap.sailing.domain.leaderboard.Leaderboard; import com.sap.sailing.domain.leaderboard.impl.FlexibleLeaderboardImpl; +import com.sap.sailing.domain.leaderboard.impl.LowerScoreIsBetter; import com.sap.sailing.domain.leaderboard.impl.ResultDiscardingRuleImpl; import com.sap.sailing.domain.leaderboard.impl.ScoreCorrectionImpl; import com.sap.sailing.domain.persistence.impl.DomainObjectFactoryImpl; @@ -35,7 +36,7 @@ public class TestStoringAndRetrievingLeaderboards extends AbstractMongoDBTest { final String leaderboardName = "TestLeaderboard"; final int[] discardIndexResultsStartingWithHowManyRaces = new int[] { 5, 8 }; FlexibleLeaderboardImpl leaderboard = new FlexibleLeaderboardImpl(leaderboardName, new ScoreCorrectionImpl(), - new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces)); + new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowerScoreIsBetter()); new MongoObjectFactoryImpl(db).storeLeaderboard(leaderboard); Leaderboard loadedLeaderboard = new DomainObjectFactoryImpl(db).loadLeaderboard(leaderboardName); assertEquals(leaderboardName, loadedLeaderboard.getName()); @@ -50,7 +51,7 @@ public class TestStoringAndRetrievingLeaderboards extends AbstractMongoDBTest { final int carriedPointsForWolfgangHunger = 3; final int[] discardIndexResultsStartingWithHowManyRaces = new int[] { 5, 8 }; FlexibleLeaderboardImpl leaderboard = new FlexibleLeaderboardImpl(leaderboardName, new ScoreCorrectionImpl(), - new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces)); + new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowerScoreIsBetter()); Competitor competitor = createCompetitor(); TrackedRace raceWithOneCompetitor = new MockedTrackedRaceWithFixedRank(competitor, /* rank */ 1, /* started */ true); leaderboard.addRace(raceWithOneCompetitor, raceColumnName, /* medalRace */ false, leaderboard.getFleet(null)); @@ -75,7 +76,7 @@ public class TestStoringAndRetrievingLeaderboards extends AbstractMongoDBTest { final int correctedPoints = 2; final int[] discardIndexResultsStartingWithHowManyRaces = new int[] { 5, 8 }; FlexibleLeaderboardImpl leaderboard = new FlexibleLeaderboardImpl(leaderboardName, new ScoreCorrectionImpl(), - new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces)); + new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowerScoreIsBetter()); Competitor competitor = createCompetitor(); TrackedRace raceWithOneCompetitor1 = new MockedTrackedRaceWithFixedRank(competitor, /* rank */ 1, /* started */ true); TrackedRace raceWithOneCompetitor2 = new MockedTrackedRaceWithFixedRank(competitor, /* rank */ 2, /* started */ true); @@ -101,7 +102,7 @@ public class TestStoringAndRetrievingLeaderboards extends AbstractMongoDBTest { final MaxPointsReason maxPointsReason = MaxPointsReason.DNF; final int[] discardIndexResultsStartingWithHowManyRaces = new int[] { 5, 8 }; FlexibleLeaderboardImpl leaderboard = new FlexibleLeaderboardImpl(leaderboardName, new ScoreCorrectionImpl(), - new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces)); + new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowerScoreIsBetter()); Competitor competitor = createCompetitor(); TrackedRace raceWithOneCompetitor1 = new MockedTrackedRaceWithFixedRank(competitor, /* rank */ 1, /* started */ true); TrackedRace raceWithOneCompetitor2 = new MockedTrackedRaceWithFixedRank(competitor, /* rank */ 2, /* started */ true); @@ -128,7 +129,7 @@ public class TestStoringAndRetrievingLeaderboards extends AbstractMongoDBTest { final MaxPointsReason maxPointsReason = MaxPointsReason.DNF; final int[] discardIndexResultsStartingWithHowManyRaces = new int[] { 5, 8 }; FlexibleLeaderboardImpl leaderboard = new FlexibleLeaderboardImpl(leaderboardName, new ScoreCorrectionImpl(), - new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces)); + new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowerScoreIsBetter()); Competitor competitor = createCompetitor(); TrackedRace raceWithOneCompetitor1 = new MockedTrackedRaceWithFixedRank(competitor, /* rank */ 1, /* started */ true); TrackedRace raceWithOneCompetitor2 = new MockedTrackedRaceWithFixedRank(competitor, /* rank */ 2, /* started */ true); diff --git a/java/com.sap.sailing.server/SailingServer (No Proxy).launch b/java/com.sap.sailing.server/SailingServer (No Proxy).launch index d203e975d89..776f66b7a4f 100755 --- a/java/com.sap.sailing.server/SailingServer (No Proxy).launch +++ b/java/com.sap.sailing.server/SailingServer (No Proxy).launch @@ -24,5 +24,5 @@ - + diff --git a/java/com.sap.sailing.server/WEB-INF/web.xml b/java/com.sap.sailing.server/WEB-INF/web.xml index 25cb9aa5d97..f0f15a9d2a8 100644 --- a/java/com.sap.sailing.server/WEB-INF/web.xml +++ b/java/com.sap.sailing.server/WEB-INF/web.xml @@ -31,4 +31,22 @@ ExpeditionThroughHttpPostServlet /expedition + + + ListLeaderboards + com.sap.sailing.server.impl.ListLeaderboards + + + ListLeaderboards + /listleaderboards + + + + LeaderboardJsonExport + com.sap.sailing.server.impl.LeaderboardJsonExport + + + LeaderboardJsonExport + /leaderboard + diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/LeaderboardJsonExport.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/LeaderboardJsonExport.java new file mode 100755 index 00000000000..8f46ce787ae --- /dev/null +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/LeaderboardJsonExport.java @@ -0,0 +1,92 @@ +package com.sap.sailing.server.impl; + +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; + +import com.sap.sailing.domain.base.Competitor; +import com.sap.sailing.domain.base.Fleet; +import com.sap.sailing.domain.base.RaceColumn; +import com.sap.sailing.domain.base.impl.MillisecondsTimePoint; +import com.sap.sailing.domain.common.NoWindException; +import com.sap.sailing.domain.common.TimePoint; +import com.sap.sailing.domain.leaderboard.Leaderboard; +import com.sap.sailing.server.Servlet; + +public class LeaderboardJsonExport extends Servlet { + private static final long serialVersionUID = -2460691283231361152L; + private static final String PARAM_NAME_LEADERBOARDNAME = "leaderboardName"; + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + TimePoint timePoint = MillisecondsTimePoint.now(); + String leaderboardName = req.getParameter(PARAM_NAME_LEADERBOARDNAME); + if (leaderboardName == null) { + resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Need to specify a leaderboard name using the "+ + PARAM_NAME_LEADERBOARDNAME+" parameter"); + } else { + Leaderboard leaderboard = getService().getLeaderboardByName(leaderboardName); + if (leaderboard == null) { + resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Leaderboard "+leaderboardName+" not found"); + } else { + try { + List competitorsFromBestToWorstAccordingToTotalRank = leaderboard + .getCompetitorsFromBestToWorst(timePoint); + Map> rankedCompetitorsPerColumn = new HashMap>(); + JSONObject jsonLeaderboard = new JSONObject(); + jsonLeaderboard.put("name", leaderboard.getName()); + JSONArray jsonColumnNames = new JSONArray(); + jsonLeaderboard.put("columnNames", jsonColumnNames); + for (RaceColumn raceColumn : leaderboard.getRaceColumns()) { + jsonColumnNames.add(raceColumn.getName()); + } + JSONArray jsonCompetitorEntries = new JSONArray(); + jsonLeaderboard.put("competitors", jsonCompetitorEntries); + for (Competitor competitor : competitorsFromBestToWorstAccordingToTotalRank) { + JSONObject jsonCompetitor = new JSONObject(); + jsonCompetitor.put("name", competitor.getName()); + final String displayName = leaderboard.getDisplayName(competitor); + jsonCompetitor.put("displayName", displayName==null?competitor.getName():displayName); + jsonCompetitor.put("id", competitor.getId().toString()); + jsonCompetitor.put("sailID", competitor.getBoat().getSailID()); + jsonCompetitor.put("nationality", competitor.getTeam().getNationality() + .getThreeLetterIOCAcronym()); + jsonCompetitor.put("rank", + competitorsFromBestToWorstAccordingToTotalRank.indexOf(competitor) + 1); + jsonCompetitor.put("totalPoints", leaderboard.getTotalPoints(competitor, timePoint)); + jsonCompetitorEntries.add(jsonCompetitor); + JSONObject jsonRaceColumns = new JSONObject(); + jsonCompetitor.put("raceScores", jsonRaceColumns); + for (RaceColumn raceColumn : leaderboard.getRaceColumns()) { + List rankedCompetitorsForColumn = rankedCompetitorsPerColumn.get(raceColumn); + if (rankedCompetitorsForColumn == null) { + rankedCompetitorsForColumn = leaderboard.getCompetitorsFromBestToWorst(raceColumn, timePoint); + rankedCompetitorsPerColumn.put(raceColumn, rankedCompetitorsForColumn); + } + JSONObject jsonEntry = new JSONObject(); + jsonRaceColumns.put(raceColumn.getName(), jsonEntry); + final Fleet fleetOfCompetitor = raceColumn.getFleetOfCompetitor(competitor); + jsonEntry.put("fleet", fleetOfCompetitor==null?"":fleetOfCompetitor.getName()); + jsonEntry.put("netPoints", leaderboard.getNetPoints(competitor, raceColumn, timePoint)); + jsonEntry.put("totalPoints", leaderboard.getTotalPoints(competitor, raceColumn, timePoint)); + jsonEntry.put("maxPointsReason", leaderboard.getMaxPointsReason(competitor, raceColumn, timePoint)); + jsonEntry.put("rank", rankedCompetitorsForColumn.indexOf(competitor)+1); + jsonEntry.put("isDiscarded", leaderboard.isDiscarded(competitor, raceColumn, timePoint)); + } + } + jsonLeaderboard.writeJSONString(resp.getWriter()); + } catch (NoWindException e) { + resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage()); + } + } + } + } +} diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/ListLeaderboards.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/ListLeaderboards.java new file mode 100755 index 00000000000..c65fb6b0eb8 --- /dev/null +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/ListLeaderboards.java @@ -0,0 +1,28 @@ +package com.sap.sailing.server.impl; + +import java.io.IOException; +import java.util.Map; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.json.simple.JSONArray; + +import com.sap.sailing.domain.leaderboard.Leaderboard; +import com.sap.sailing.server.Servlet; + +public class ListLeaderboards extends Servlet { + private static final long serialVersionUID = -2193421590275280102L; + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + JSONArray jsonLeaderboards = new JSONArray(); + Map leaderboards = getService().getLeaderboards(); + for (String leaderboardName : leaderboards.keySet()) { + jsonLeaderboards.add(leaderboardName); + } + jsonLeaderboards.writeJSONString(resp.getWriter()); + } + +} diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java index adbd443653f..66609932de2 100755 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java @@ -51,6 +51,7 @@ import com.sap.sailing.domain.leaderboard.Leaderboard; import com.sap.sailing.domain.leaderboard.LeaderboardGroup; import com.sap.sailing.domain.leaderboard.impl.FlexibleLeaderboardImpl; import com.sap.sailing.domain.leaderboard.impl.LeaderboardGroupImpl; +import com.sap.sailing.domain.leaderboard.impl.LowerScoreIsBetter; import com.sap.sailing.domain.leaderboard.impl.ResultDiscardingRuleImpl; import com.sap.sailing.domain.leaderboard.impl.ScoreCorrectionImpl; import com.sap.sailing.domain.persistence.DomainObjectFactory; @@ -207,7 +208,7 @@ public class RacingEventServiceImpl implements RacingEventService, RegattaListen @Override public Leaderboard addFlexibleLeaderboard(String name, int[] discardThresholds) { Leaderboard result = new FlexibleLeaderboardImpl(name, new ScoreCorrectionImpl(), new ResultDiscardingRuleImpl( - discardThresholds)); + discardThresholds), new LowerScoreIsBetter()); synchronized (leaderboardsByName) { if (leaderboardsByName.containsKey(name)) { throw new IllegalArgumentException("Leaderboard with name "+name+" already exists"); diff --git a/java/org.moxieapps.gwt.highcharts/src/org/moxieapps/gwt/highcharts/client/BaseChart.java b/java/org.moxieapps.gwt.highcharts/src/org/moxieapps/gwt/highcharts/client/BaseChart.java index 2084705329c..e26b9a34a66 100644 --- a/java/org.moxieapps.gwt.highcharts/src/org/moxieapps/gwt/highcharts/client/BaseChart.java +++ b/java/org.moxieapps.gwt.highcharts/src/org/moxieapps/gwt/highcharts/client/BaseChart.java @@ -1495,7 +1495,6 @@ public abstract class BaseChart extends Widget { // Once we're rendered, we're maintaining the point state in the DOM, so we can dump our internal list to save memory series.clearInternalPointsList(); - nativeAdjustCheckboxPosition(get(series.getId())); } return returnThis(); @@ -1818,6 +1817,7 @@ public abstract class BaseChart extends Widget { public T redraw() { if (isRendered()) { nativeRedraw(chart); + nativeAdjustCheckboxPosition(chart); } return returnThis(); } @@ -1969,7 +1969,6 @@ public abstract class BaseChart extends Widget { for (Series series : seriesList) { series.clearInternalPointsList(); series.setRendered(true); - nativeAdjustCheckboxPosition(get(series.getId())); } } @@ -2489,20 +2488,22 @@ public abstract class BaseChart extends Widget { /** * Fixing a bug currently in the base Highcharts implementation regarding the placement of the checkbox in the legend. - * Requires the symbol padding (see {@link Legend#setSymbolPadding(Number)}) to be set to 20. Invoked - * whenever a series is added / displayed in a rendered chart.

+ * Requires the symbol padding (see {@link Legend#setSymbolPadding(Number)}) to be set to 25. Invoked + * whenever the chart is rendered.

* - * The implementation moves the legend item text 15px to the left, using the space made by setting the symbol - * padding to 20px; the checkbox is moved 18px to the left with the top margin set to 0px. + * The implementation moves the checkbox between the legend symbol and the legend item, using the space made by setting the symbol + * padding to 25px. */ - private static native JavaScriptObject nativeAdjustCheckboxPosition(JavaScriptObject series) /*-{ - if (series.legendItem && series.checkbox) { - series.legendItem.translate(-15, 0); - series.checkbox.style.marginLeft = '-18px' - series.checkbox.style.marginTop = '0px' + private static native JavaScriptObject nativeAdjustCheckboxPosition(JavaScriptObject chart) /*-{ + var series = chart.series; + for(var i= 0; i