Merge remote-tracking branch 'origin/master' into jspsupport

This commit is contained in:
fmittag
2012-10-04 13:37:21 +02:00
106 changed files with 4512 additions and 3587 deletions
Binary file not shown.
@@ -1,5 +1,5 @@
package com.sap.sailing.domain.common;
public enum ScoringSchemeType {
LOW_POINT, HIGH_POINT, HIGH_POINT_ESS_OVERALL
LOW_POINT, HIGH_POINT, HIGH_POINT_ESS_OVERALL, HIGH_POINT_LAST_BREAKS_TIE
}
@@ -185,6 +185,11 @@ public class DelayedLeaderboardCorrectionsImpl implements DelayedLeaderboardCorr
public void isMedalRaceChanged(RaceColumn raceColumn, boolean newIsMedalRace) {
}
@Override
public boolean canAddRaceColumnToContainer(RaceColumn raceColumn) {
return true;
}
@Override
public void raceColumnAddedToContainer(RaceColumn raceColumn) {
}
@@ -22,11 +22,12 @@ import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.ControlPoint;
import com.sap.sailing.domain.base.Course;
import com.sap.sailing.domain.base.CourseListener;
import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.base.Leg;
import com.sap.sailing.domain.base.RaceDefinition;
import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.base.Waypoint;
import com.sap.sailing.domain.base.impl.BuoyImpl;
import com.sap.sailing.domain.base.impl.CourseImpl;
import com.sap.sailing.domain.base.impl.WaypointImpl;
import com.sap.sailing.domain.common.impl.Util;
import com.sap.sailing.domain.common.impl.Util.Triple;
@@ -235,9 +236,14 @@ public class CourseUpdateTest extends AbstractTracTracLiveTest {
System.out.println("waypointRemoved " + zeroBasedIndex + " / " + waypointThatGotRemoved);
}
});
domainFactory.updateCourseWaypoints(course, controlPoints);
assertTrue(result[0]);
testLegStructure(3);
((CourseImpl) course).lockForWrite(); // without the lock it's possible that another race course update removes the additional waypoint again
try {
domainFactory.updateCourseWaypoints(course, controlPoints);
assertTrue(result[0]);
testLegStructure(3);
} finally {
((CourseImpl) course).unlockAfterWrite();
}
}
@Test
@@ -250,16 +250,19 @@ public class LeaderboardOfflineTest extends AbstractLeaderboardTest {
}
List<Integer> ranksOfNonMedalStartedRaces = new ArrayList<Integer>();
TimePoint now = MillisecondsTimePoint.now();
int numberOfRacesToCompareToDiscardThresholds = 0;
for (RaceColumn column : raceColumnsInLeaderboard.values()) {
if (!column.isMedalRace() && column.getTrackedRace(defaultFleet) != null && column.getTrackedRace(defaultFleet).hasStarted(now)) {
ranksOfNonMedalStartedRaces.add(column.getTrackedRace(defaultFleet).getRank(competitor, now));
if (column.getTrackedRace(defaultFleet) != null && column.getTrackedRace(defaultFleet).hasStarted(now)) {
numberOfRacesToCompareToDiscardThresholds++;
if (!column.isMedalRace()) {
ranksOfNonMedalStartedRaces.add(column.getTrackedRace(defaultFleet).getRank(competitor, now));
}
}
}
Collections.sort(ranksOfNonMedalStartedRaces);
int carryInt = (carry == null ? 0 : carry);
int totalPoints = carryInt;
int medalRacePoints = getMedalRacePoints(competitor, now, defaultFleet);
int numberOfRacesFromWhichToDiscard = ranksOfNonMedalStartedRaces.size();
for (TrackedRace race : testRaces) {
RaceColumn raceColumn = raceColumnsInLeaderboard.get(race);
Pair<Competitor, RaceColumn> key = new Pair<Competitor, RaceColumn>(competitor, raceColumn);
@@ -274,8 +277,8 @@ public class LeaderboardOfflineTest extends AbstractLeaderboardTest {
// One race is discarded because four races were started, and for [3-6) one race can be discarded.
// The discarded race is the worst of those started, so the one with rank 4.
int expectedNumberOfDiscardedRaces =
numberOfRacesFromWhichToDiscard < firstDiscardingThreshold ? 0 :
numberOfRacesFromWhichToDiscard < secondDiscardingThreshold ? 1 : 2;
numberOfRacesToCompareToDiscardThresholds < firstDiscardingThreshold ? 0 :
numberOfRacesToCompareToDiscardThresholds < secondDiscardingThreshold ? 1 : 2;
boolean discarded = ranksOfNonMedalStartedRaces.indexOf(rank) >= ranksOfNonMedalStartedRaces.size()-expectedNumberOfDiscardedRaces;
int expected = discarded ? 0 : rank==medalRacePoints?2*rank:rank;
assertEquals(expected, leaderboard.getTotalPoints(competitor, raceColumn, now), 0.000000001);
@@ -7,6 +7,7 @@ import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
@@ -107,6 +108,209 @@ public class LeaderboardScoringAndRankingTest extends AbstractLeaderboardTest {
}
}
/**
* Regarding bug 961, test scoring in a leaderboard that has a qualification series with two unordered groups where for one
* column only one group has raced (expressed by a mocked TrackedRace attached to the column). Ensure that the column doesn't
* count for neither the total points sum nor the competitor ordering.
*/
@Test
public void testUnorderedGroupsWithOneGroupNotHavingRacedInAColumn() throws NoWindException {
List<Competitor> competitors = createCompetitors(10);
Regatta regatta = createRegatta(/* qualifying */ 2, new String[] { "Yellow", "Blue" }, /* final */0,
new String[] { "Default" },
/* medal */false, "testUnorderedGroupsWithOneGroupNotHavingRacedInAcolumn",
DomainFactory.INSTANCE.getOrCreateBoatClass("49er", /* typicallyStartsUpwind */true),
DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT));
Leaderboard leaderboard = createLeaderboard(regatta, /* discarding thresholds */ new int[0]);
Series qualificationSeries;
Iterator<? extends Series> seriesIter = regatta.getSeries().iterator();
qualificationSeries = seriesIter.next();
TimePoint now = MillisecondsTimePoint.now();
TimePoint later = new MillisecondsTimePoint(now.asMillis()+1000);
TrackedRace q1Yellow = new MockedTrackedRaceWithStartTimeAndRanks(now, competitors.subList(0, 5));
TrackedRace q1Blue = new MockedTrackedRaceWithStartTimeAndRanks(now, competitors.subList(5, 10));
RaceColumn q1Column = qualificationSeries.getRaceColumnByName("Q1");
q1Column.setTrackedRace(q1Column.getFleetByName("Yellow"), q1Yellow);
q1Column.setTrackedRace(q1Column.getFleetByName("Blue"), q1Blue);
List<Competitor> rankedCompetitors = leaderboard.getCompetitorsFromBestToWorst(later);
for (int i=0; i<5; i++) {
assertTrue(competitors.get(i) == rankedCompetitors.get(2*i) || competitors.get(i) == rankedCompetitors.get(2*i+1));
assertTrue(competitors.get(i+5) == rankedCompetitors.get(2*i) || competitors.get(i+5) == rankedCompetitors.get(2*i+1));
assertEquals((double) (i+1), leaderboard.getTotalPoints(competitors.get(i), later), 0.000000001);
assertEquals((double) (i+1), leaderboard.getTotalPoints(competitors.get(i+5), later), 0.0000000001);
}
// now add one race for yellow fleet and test that it doesn't count because blue fleet is still missing its race for Q2
TrackedRace q2Yellow = new MockedTrackedRaceWithStartTimeAndRanks(now, competitors.subList(3, 8));
RaceColumn q2Column = qualificationSeries.getRaceColumnByName("Q2");
q2Column.setTrackedRace(q2Column.getFleetByName("Yellow"), q2Yellow);
List<Competitor> rankedCompetitorsWithOneRaceMissingInQ2 = leaderboard.getCompetitorsFromBestToWorst(later);
for (int i=0; i<5; i++) {
assertTrue(competitors.get(i) == rankedCompetitorsWithOneRaceMissingInQ2.get(2*i) ||
competitors.get(i) == rankedCompetitorsWithOneRaceMissingInQ2.get(2*i+1));
assertTrue(competitors.get(i+5) == rankedCompetitorsWithOneRaceMissingInQ2.get(2*i) ||
competitors.get(i+5) == rankedCompetitorsWithOneRaceMissingInQ2.get(2*i+1));
assertEquals((double) (i+1), leaderboard.getTotalPoints(competitors.get(i), later), 0.000000001);
assertEquals((double) (i+1), leaderboard.getTotalPoints(competitors.get(i+5), later), 0.0000000001);
}
// now add a tracked race for the blue fleet for Q2 and assert that the Q2 scores count for the total points sum
TrackedRace q2Blue = new MockedTrackedRaceWithStartTimeAndRanks(now,
Arrays.asList(new Competitor[] { competitors.get(0), competitors.get(1), competitors.get(2),
competitors.get(8), competitors.get(9) }));
q2Column.setTrackedRace(q2Column.getFleetByName("Blue"), q2Blue);
// the new order in Q2 expected to be { (0, 3), (1, 4), (2, 5), (8, 6), (9, 7) }
// therefore the new total points, in ascending order, are expected to be:
// { 0: 1+1=2; 5: 1+3=4; 1: 2+2=4; 3: 4+1=5; 2: 3+3=6; 6: 2+4=6; 4: 5+2=7; 7: 3+5=8; 8: 4+4=8; 9: 5+5=10 }
double[] expectedTotalPoints = new double[] { 2, 4, 6, 5, 7, 4, 6, 8, 8, 10 };
int[] expectedOrderAfterTwoFullRacesPlusMinusOne = new int[] { 0, 5, 1, 3, 2, 6, 4, 7, 8, 9 };
List<Competitor> rankedCompetitorsWithAllRacesInQ2 = leaderboard.getCompetitorsFromBestToWorst(later);
for (int i=0; i<5; i++) {
assertTrue(competitors.get(expectedOrderAfterTwoFullRacesPlusMinusOne[2*i]) == rankedCompetitorsWithAllRacesInQ2.get(2*i) ||
competitors.get(expectedOrderAfterTwoFullRacesPlusMinusOne[2*i]) == rankedCompetitorsWithAllRacesInQ2.get(2*i+1));
assertTrue(competitors.get(expectedOrderAfterTwoFullRacesPlusMinusOne[2*i+1]) == rankedCompetitorsWithAllRacesInQ2.get(2*i) ||
competitors.get(expectedOrderAfterTwoFullRacesPlusMinusOne[2*i+1]) == rankedCompetitorsWithAllRacesInQ2.get(2*i+1));
assertEquals(expectedTotalPoints[2*i], leaderboard.getTotalPoints(competitors.get(2*i), later), 0.000000001);
assertEquals(expectedTotalPoints[2*i+1], leaderboard.getTotalPoints(competitors.get(2*i+1), later), 0.000000001);
}
}
/**
* Regarding bug 961, test scoring in a leaderboard that has a qualification series with two unordered groups where for one
* column only one group has raced (expressed by a mocked TrackedRace attached to the column). Then add the second tracked
* race for the second column but such that it hasn't started yet at the time point of the query. Then add a score correction,
* simulating the use of a proxy race (that doesn't ever start) and a manual score entry.
*/
@Test
public void testUnorderedGroupsWithOneGroupNotHavingStartedRacedInAcolumnAndThenCorrectingScore() throws NoWindException {
List<Competitor> competitors = createCompetitors(10);
Regatta regatta = createRegatta(/* qualifying */ 2, new String[] { "Yellow", "Blue" }, /* final */0,
new String[] { "Default" },
/* medal */false, "testUnorderedGroupsWithOneGroupNotHavingRacedInAcolumn",
DomainFactory.INSTANCE.getOrCreateBoatClass("49er", /* typicallyStartsUpwind */true),
DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT));
Leaderboard leaderboard = createLeaderboard(regatta, /* discarding thresholds */ new int[0]);
Series qualificationSeries;
Iterator<? extends Series> seriesIter = regatta.getSeries().iterator();
qualificationSeries = seriesIter.next();
TimePoint now = MillisecondsTimePoint.now();
TimePoint later = new MillisecondsTimePoint(now.asMillis()+1000);
TrackedRace q1Yellow = new MockedTrackedRaceWithStartTimeAndRanks(now, competitors.subList(0, 5));
TrackedRace q1Blue = new MockedTrackedRaceWithStartTimeAndRanks(now, competitors.subList(5, 10));
RaceColumn q1Column = qualificationSeries.getRaceColumnByName("Q1");
q1Column.setTrackedRace(q1Column.getFleetByName("Yellow"), q1Yellow);
q1Column.setTrackedRace(q1Column.getFleetByName("Blue"), q1Blue);
// now add one race for yellow fleet and test that it doesn't count because blue fleet is still missing its race for Q2
TrackedRace q2Yellow = new MockedTrackedRaceWithStartTimeAndRanks(now, competitors.subList(3, 8));
RaceColumn q2Column = qualificationSeries.getRaceColumnByName("Q2");
q2Column.setTrackedRace(q2Column.getFleetByName("Yellow"), q2Yellow);
// now add a tracked race for the blue fleet for Q2 that hasn't started yet and assert that the Q2 scores still don't count for the total points sum
TimePoint muchLater = later.plus(10000000000l);
TrackedRace q2Blue = new MockedTrackedRaceWithStartTimeAndRanks(muchLater,
Arrays.asList(new Competitor[] { competitors.get(0), competitors.get(1), competitors.get(2),
competitors.get(8), competitors.get(9) }));
q2Column.setTrackedRace(q2Column.getFleetByName("Blue"), q2Blue);
List<Competitor> rankedCompetitorsWithOneRaceMissingInQ2 = leaderboard.getCompetitorsFromBestToWorst(later);
for (int i=0; i<5; i++) {
assertTrue(competitors.get(i) == rankedCompetitorsWithOneRaceMissingInQ2.get(2*i) ||
competitors.get(i) == rankedCompetitorsWithOneRaceMissingInQ2.get(2*i+1));
assertTrue(competitors.get(i+5) == rankedCompetitorsWithOneRaceMissingInQ2.get(2*i) ||
competitors.get(i+5) == rankedCompetitorsWithOneRaceMissingInQ2.get(2*i+1));
assertEquals((double) (i+1), leaderboard.getTotalPoints(competitors.get(i), later), 0.000000001);
assertEquals((double) (i+1), leaderboard.getTotalPoints(competitors.get(i+5), later), 0.0000000001);
}
assertFalse(leaderboard.getScoringScheme().isValidInTotalScore(leaderboard, q2Column, later));
// now add a score correction for Q2/Blue to make it count:
leaderboard.getScoreCorrection().correctScore(competitors.get(9), q2Column, 42.);
assertTrue(leaderboard.getScoringScheme().isValidInTotalScore(leaderboard, q2Column, later));
// the new order in Q2 expected to be { (9, 3), ... } (we don't know about any competitor in q2Blue but #9
// therefore the new total points for #9 are
// { 9: 5+42=47 }
assertEquals(47.0, leaderboard.getTotalPoints(competitors.get(9), later), 0.00000001);
}
/**
* Regarding bug 961, test scoring in a leaderboard that has a qualification series with two unordered groups where for one
* column only one group has raced (expressed by a mocked TrackedRace attached to the column). Ensure that the column doesn't
* count for computing the number of discards.
*/
@Test
public void testDiscardsForUnorderedGroupsWithOneGroupNotHavingRacedInAColumn() throws NoWindException {
List<Competitor> competitors = createCompetitors(10);
Regatta regatta = createRegatta(/* qualifying */ 2, new String[] { "Yellow", "Blue" }, /* final */0,
new String[] { "Default" },
/* medal */false, "testDiscardsForUnorderedGroupsWithOneGroupNotHavingRacedInAColumn",
DomainFactory.INSTANCE.getOrCreateBoatClass("49er", /* typicallyStartsUpwind */true),
DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT));
Leaderboard leaderboard = createLeaderboard(regatta, /* discarding thresholds */ new int[]{2}); // one discard for two or more races
Series qualificationSeries;
Iterator<? extends Series> seriesIter = regatta.getSeries().iterator();
qualificationSeries = seriesIter.next();
TimePoint now = MillisecondsTimePoint.now();
TimePoint later = new MillisecondsTimePoint(now.asMillis()+1000);
TrackedRace q1Yellow = new MockedTrackedRaceWithStartTimeAndRanks(now, competitors.subList(0, 5));
TrackedRace q1Blue = new MockedTrackedRaceWithStartTimeAndRanks(now, competitors.subList(5, 10));
RaceColumn q1Column = qualificationSeries.getRaceColumnByName("Q1");
q1Column.setTrackedRace(q1Column.getFleetByName("Yellow"), q1Yellow);
q1Column.setTrackedRace(q1Column.getFleetByName("Blue"), q1Blue);
// now add one race for yellow fleet and test that there are no discards still because blue fleet is still missing its race for Q2
TrackedRace q2Yellow = new MockedTrackedRaceWithStartTimeAndRanks(now, competitors.subList(3, 8));
RaceColumn q2Column = qualificationSeries.getRaceColumnByName("Q2");
q2Column.setTrackedRace(q2Column.getFleetByName("Yellow"), q2Yellow);
for (Competitor competitor : competitors) {
assertFalse("Competitor "+competitor+" has a discard in Q1 but shouldn't", leaderboard.isDiscarded(competitor, q1Column, later));
assertFalse("Competitor "+competitor+" has a discard in Q2 but shouldn't", leaderboard.isDiscarded(competitor, q2Column, later));
}
// now add a tracked race for the blue fleet for Q2 and assert that all competitors have one discard
TrackedRace q2Blue = new MockedTrackedRaceWithStartTimeAndRanks(now,
Arrays.asList(new Competitor[] { competitors.get(0), competitors.get(1), competitors.get(2),
competitors.get(8), competitors.get(9) }));
q2Column.setTrackedRace(q2Column.getFleetByName("Blue"), q2Blue);
for (Competitor competitor : competitors) {
assertTrue("Competitor "+competitor+" has no discard but should",
leaderboard.isDiscarded(competitor, q1Column, later) || leaderboard.isDiscarded(competitor, q2Column, later));
}
}
/**
* Regarding bug 961, test scoring in a leaderboard that has a qualification series with two ordered groups where for one
* column only one group has raced (expressed by a mocked TrackedRace attached to the column). Ensure that the competitors in
* that column get their discard.
*/
@Test
public void testDiscardsForOrderedGroupsWithOneGroupNotHavingRacedInAColumn() throws NoWindException {
List<Competitor> competitors = createCompetitors(10);
Regatta regatta = createRegatta(/* qualifying */ 0, new String[] { "Default" }, /* final */2,
new String[] { "Gold", "Silver" },
/* medal */false, "testDiscardsForOrderedGroupsWithOneGroupNotHavingRacedInAColumn",
DomainFactory.INSTANCE.getOrCreateBoatClass("49er", /* typicallyStartsUpwind */true),
DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT));
Leaderboard leaderboard = createLeaderboard(regatta, /* discarding thresholds */ new int[]{2}); // one discard for two or more races
Series finalSeries;
Iterator<? extends Series> seriesIter = regatta.getSeries().iterator();
seriesIter.next();
finalSeries = seriesIter.next();
TimePoint now = MillisecondsTimePoint.now();
TimePoint later = new MillisecondsTimePoint(now.asMillis()+1000);
TrackedRace f1Gold = new MockedTrackedRaceWithStartTimeAndRanks(now, competitors.subList(0, 5));
TrackedRace f1Silver = new MockedTrackedRaceWithStartTimeAndRanks(now, competitors.subList(5, 10));
RaceColumn f1Column = finalSeries.getRaceColumnByName("F1");
f1Column.setTrackedRace(f1Column.getFleetByName("Gold"), f1Gold);
f1Column.setTrackedRace(f1Column.getFleetByName("Silver"), f1Silver);
// now add one race for yellow fleet and test that there are no discards still because blue fleet is still missing its race for Q2
TrackedRace f2Gold = new MockedTrackedRaceWithStartTimeAndRanks(now, competitors.subList(0, 5));
RaceColumn f2Column = finalSeries.getRaceColumnByName("F2");
f2Column.setTrackedRace(f2Column.getFleetByName("Gold"), f2Gold);
for (int i=0; i<5; i++) {
assertTrue("Competitor "+competitors.get(i)+" has no discard in F1 or F2 but should",
leaderboard.isDiscarded(competitors.get(i), f1Column, later) ||
leaderboard.isDiscarded(competitors.get(i), f2Column, later));
}
for (int i=5; i<10; i++) {
assertFalse("Competitor "+competitors.get(i)+" has a discard in F1 or F2 but shouldn't",
leaderboard.isDiscarded(competitors.get(i), f1Column, later) ||
leaderboard.isDiscarded(competitors.get(i), f2Column, later));
}
}
/**
* Asserts that the competitors ranking worse than the disqualified competitor advance by one in the
@@ -118,7 +322,7 @@ public class LeaderboardScoringAndRankingTest extends AbstractLeaderboardTest {
List<Competitor> competitors = createCompetitors(10);
Regatta regatta = createRegatta(/* qualifying */0, new String[] { "Default" }, /* final */1,
new String[] { "Default" },
/* medal */false, "testOneStartedRaceWithDifferentScores",
/* medal */false, "testOneStartedRaceWithDifferentScoresAndDisqualification",
DomainFactory.INSTANCE.getOrCreateBoatClass("49er", /* typicallyStartsUpwind */true), DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT));
Leaderboard leaderboard = createLeaderboard(regatta, /* discarding thresholds */ new int[0]);
Series finalSeries;
@@ -157,7 +361,7 @@ public class LeaderboardScoringAndRankingTest extends AbstractLeaderboardTest {
}
Regatta regatta = createRegatta(/* qualifying */1, new String[] { "Yellow", "Blue" }, /* final */0,
new String[] { "Default" },
/* medal */false, "testAllTrackedAndStartedWithDifferentScores",
/* medal */false, "testDistributionAcrossQualifyingFleetsWithDifferentScores",
DomainFactory.INSTANCE.getOrCreateBoatClass("49er", /* typicallyStartsUpwind */true), DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT));
Leaderboard leaderboard = createLeaderboard(regatta, /* discarding thresholds */ new int[0]);
TimePoint now = MillisecondsTimePoint.now();
@@ -280,6 +484,34 @@ public class LeaderboardScoringAndRankingTest extends AbstractLeaderboardTest {
}
}
@Test
public void testLastRaceTakesPrecedenceWithHighPointLastBreaksTieScheme() throws NoWindException {
List<Competitor> competitors = createCompetitors(20);
Regatta regatta = createRegatta(/* qualifying */ 0, new String[] { "Default" }, /* final */ 2, new String[] { "Default" },
/* medal */ false, "testLastRaceTakesPrecedenceWithHighPointLastBreaksTieScheme",
DomainFactory.INSTANCE.getOrCreateBoatClass("49er", /* typicallyStartsUpwind */ true),
DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.HIGH_POINT_LAST_BREAKS_TIE));
Leaderboard leaderboard = createLeaderboard(regatta, /* discarding thresholds */ new int[0]);
TimePoint now = MillisecondsTimePoint.now();
TimePoint later = new MillisecondsTimePoint(now.asMillis()+1000);
RaceColumn f1Column = series.get(1).getRaceColumnByName("F1");
TrackedRace f1Default = new MockedTrackedRaceWithStartTimeAndRanks(now, competitors);
f1Column.setTrackedRace(f1Column.getFleetByName("Default"), f1Default);
List<Competitor> reversedCompetitors = new ArrayList<Competitor>(competitors);
Collections.reverse(reversedCompetitors);
RaceColumn f2Column = series.get(1).getRaceColumnByName("F2");
TrackedRace f2Default = new MockedTrackedRaceWithStartTimeAndRanks(now, reversedCompetitors);
f2Column.setTrackedRace(f2Column.getFleetByName("Default"), f2Default);
// assert that all competitors have equal points now
Competitor firstCompetitor = competitors.iterator().next();
for (Competitor competitor : competitors) {
assertEquals(leaderboard.getTotalPoints(firstCompetitor, later), leaderboard.getTotalPoints(competitor, later));
}
// assert that the ordering of competitors equals that of the last race
assertEquals(reversedCompetitors, leaderboard.getCompetitorsFromBestToWorst(later));
}
@Test
public void testTieBreakWithTwoVersusOneWins() throws NoWindException {
Competitor[] c = createCompetitors(3).toArray(new Competitor[0]);
@@ -1,17 +1,108 @@
package com.sap.sailing.domain.test;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import org.junit.Ignore;
import org.junit.Test;
import com.sap.sailing.util.impl.NamedReentrantReadWriteLock;
public class LockTraceTest {
private static class LockingThread extends Thread {
private boolean ownsLock;
public LockingThread(String name) {
super(name);
}
public synchronized boolean ownsLock() {
return ownsLock;
}
public synchronized void obtainLockAndWait(Lock lock) throws InterruptedException {
lock.lock();
ownsLock = true;
notifyAll();
wait();
}
public synchronized void waitUntilLockIsObtained() throws InterruptedException {
while (!ownsLock()) {
wait();
}
}
}
@Ignore
@Test
public void testLockTrace() throws InterruptedException {
public void testLockTraceForMultipleReaders() throws InterruptedException {
NamedReentrantReadWriteLock lock1 = new NamedReentrantReadWriteLock("Lock1", /* fair */ true);
lock1.readLock().lock();
boolean itWorked = lock1.writeLock().tryLock(10, TimeUnit.MILLISECONDS);
Object o = createAndStartLockingThreadReturningObjectToNotifyInOrderToReleaseLockAndTerminateThread(lock1.readLock());
lock1.writeLock().lock();
boolean itWorked = lock1.writeLock().tryLock(1000000000000000000l, TimeUnit.MILLISECONDS);
System.out.println(itWorked);
lock1.readLock().unlock();
synchronized (o) {
o.notifyAll();
}
}
@Ignore
@Test
public void testDeadlockDetectionWithJMX() throws InterruptedException {
NamedReentrantReadWriteLock lock1 = new NamedReentrantReadWriteLock("Lock1", /* fair */ true);
NamedReentrantReadWriteLock lock2 = new NamedReentrantReadWriteLock("Lock2", /* fair */ true);
lock1.readLock().lock();
// let other thread get write lock on lock2
LockingThread o = createAndStartLockingThreadReturningObjectToNotifyInOrderToCauseDeadlock(lock2.writeLock(), lock1.writeLock());
o.waitUntilLockIsObtained();
// now let other thread continue so it tries to obtain the write lock on lock1 which it can't get because we own the read lock
synchronized (o) {
o.notifyAll();
}
// and complete the deadlock by asking lock2's read lock
lock2.readLock().lock();
lock1.readLock().unlock();
lock2.readLock().unlock();
}
private Object createAndStartLockingThreadReturningObjectToNotifyInOrderToReleaseLockAndTerminateThread(final Lock lock) {
final Thread thread = new Thread("Thread to lock "+lock) {
public void run() {
lock.lock();
synchronized (Thread.currentThread()) {
try {
Thread.currentThread().wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
lock.unlock();
}
};
thread.start();
return thread;
}
private LockingThread createAndStartLockingThreadReturningObjectToNotifyInOrderToCauseDeadlock(final Lock lock1, final Lock lock2) {
final LockingThread thread = new LockingThread("Thread to lock "+lock1+" and then "+lock2) {
public void run() {
try {
obtainLockAndWait(lock1);
} catch (InterruptedException e1) {
throw new RuntimeException(e1);
}
lock2.lock();
}
};
thread.start();
return thread;
}
public static void main(String[] args) throws InterruptedException {
new LockTraceTest().testDeadlockDetectionWithJMX();
}
}
@@ -1,6 +1,7 @@
package com.sap.sailing.domain.test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
@@ -482,7 +483,8 @@ public class TrackTest {
GPSFix outlier = new GPSFixImpl(outlierPosition, timePointForOutlier);
track.addGPSFix(outlier);
assertEquals(1, invalidationCalls.size());
assertEquals(timePointForOutlier, invalidationCalls.iterator().next()); // outlier doesn't turn its preceding element into an outlier
TimePoint timePointOfLastFixBeforeOutlier = track.getLastFixBefore(timePointForOutlier).getTimePoint();
assertTrue(invalidationCalls.iterator().next().after(timePointOfLastFixBeforeOutlier)); // outlier doesn't turn its preceding element into an outlier
assertNull(distanceCache.getEarliestFromAndDistanceAtOrAfterFrom(now, start)); // no more entry for "to"-value start in cache
invalidationCalls.clear();
final TimePoint timePointOfLastOriginalFix = track.getLastRawFix().getTimePoint();
@@ -498,7 +500,8 @@ public class TrackTest {
GPSFix lateOutlier = new GPSFixImpl(lateOutlierPosition, timePointForLateOutlier);
track.addGPSFix(lateOutlier);
assertEquals(1, invalidationCalls.size());
assertEquals(timePointForLateOutlier, invalidationCalls.iterator().next());
TimePoint timePointOfLastFixBeforeLateOutlier = track.getLastFixBefore(timePointForLateOutlier).getTimePoint();
assertTrue(invalidationCalls.iterator().next().after(timePointOfLastFixBeforeLateOutlier));
invalidationCalls.clear();
// expect the invalidation to have started after the single cache entry, so the cache entry still has to be there:
final Pair<TimePoint, Pair<TimePoint, Distance>> stillPresentFullIntervalCacheEntry = distanceCache
@@ -539,6 +542,57 @@ public class TrackTest {
assertEquals(speed.getMetersPerSecond()*steps, track.getDistanceTraveled(now, start).getMeters(), 0.01);
}
/**
* A test case for bug 968. If distances are requested for time intervals ending after the last GPS fix, the position
* is estimated to be at the last fix known. When another fix arrives, the positions for these time points can now be
* interpolated, leading to different results and hence to a cache inconsistency in case the cache isn't flushed for the
* interval between the last and the newest fix. This test case adds a few fixes, then asks a distance traveled for an
* interval ending after the last fix, then adds another fix at a later time point and asks the distance again.
*/
@Test
public void testMoreFrequentDistanceComputationThanGPSFixReception() {
DynamicGPSFixTrack<Object, GPSFix> track = new DynamicGPSFixTrackImpl<Object>(new Object(), /* millisecondsOverWhichToAverage */ 30000l);
final int timeBetweenFixesInMillis = 1000;
Bearing bearing = new DegreeBearingImpl(123);
Speed speed = new KnotSpeedImpl(7);
Position p = new DegreePosition(0, 0);
final MillisecondsTimePoint now = MillisecondsTimePoint.now();
TimePoint start = now;
final int steps = 10;
TimePoint next = null;
GPSFix fix = null;
for (int i=0; i<steps; i++) {
fix = new GPSFixImpl(p, start);
track.addGPSFix(fix);
next = start.plus(timeBetweenFixesInMillis);
p = p.translateGreatCircle(bearing, speed.travel(start, next));
start = next;
bearing = new DegreeBearingImpl(bearing.getDegrees() + 1);
}
Distance distance1 = track.getDistanceTraveled(now, next.minus(timeBetweenFixesInMillis));
Distance distance2 = track.getDistanceTraveled(now, next.minus(2*timeBetweenFixesInMillis/3));
Distance distance3 = track.getDistanceTraveled(now, next.minus(timeBetweenFixesInMillis/3));
Distance distance4 = track.getDistanceTraveled(now, next);
assertEquals(distance1, distance2); // no progress after time point "next" because no further fixes are known
assertEquals(distance1, distance3); // no progress after time point "next" because no further fixes are known
assertEquals(distance1, distance4); // no progress after time point "next" because no further fixes are known
// now add one more fix
fix = new GPSFixImpl(p, start);
track.addGPSFix(fix);
Distance distance1_new = track.getDistanceTraveled(now, next.minus(timeBetweenFixesInMillis));
Distance distance2_new = track.getDistanceTraveled(now, next.minus(2*timeBetweenFixesInMillis/3));
Distance distance3_new = track.getDistanceTraveled(now, next.minus(timeBetweenFixesInMillis/3));
Distance distance4_new = track.getDistanceTraveled(now, next);
assertEquals(distance1, distance1_new);
assertFalse(distance2.equals(distance2_new));
assertFalse(distance3.equals(distance3_new));
assertFalse(distance4.equals(distance4_new));
Distance toReachInOneThirdOfTimeBetweenFixesInMillis = speed.travel(next, next.plus(timeBetweenFixesInMillis/3));
assertEquals(toReachInOneThirdOfTimeBetweenFixesInMillis.getMeters(), distance2_new.getMeters()-distance1_new.getMeters(), 0.01);
assertEquals(toReachInOneThirdOfTimeBetweenFixesInMillis.getMeters(), distance3_new.getMeters()-distance2_new.getMeters(), 0.01);
assertEquals(toReachInOneThirdOfTimeBetweenFixesInMillis.getMeters(), distance4_new.getMeters()-distance3_new.getMeters(), 0.01);
}
@Test
public void testDistanceCacheAccessForPartialStrip() {
final Set<TimePoint> invalidationCalls = new HashSet<TimePoint>();
@@ -1,24 +1,49 @@
package com.sap.sailing.domain.test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import org.junit.Test;
import com.sap.sailing.domain.base.BoatClass;
import com.sap.sailing.domain.base.Buoy;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.ControlPoint;
import com.sap.sailing.domain.base.DomainFactory;
import com.sap.sailing.domain.base.Waypoint;
import com.sap.sailing.domain.base.impl.BoatClassImpl;
import com.sap.sailing.domain.base.impl.BoatImpl;
import com.sap.sailing.domain.base.impl.CompetitorImpl;
import com.sap.sailing.domain.base.impl.CourseImpl;
import com.sap.sailing.domain.base.impl.KnotSpeedWithBearingImpl;
import com.sap.sailing.domain.base.impl.MillisecondsTimePoint;
import com.sap.sailing.domain.base.impl.NationalityImpl;
import com.sap.sailing.domain.base.impl.PersonImpl;
import com.sap.sailing.domain.base.impl.RaceDefinitionImpl;
import com.sap.sailing.domain.base.impl.RegattaImpl;
import com.sap.sailing.domain.base.impl.TeamImpl;
import com.sap.sailing.domain.common.ScoringSchemeType;
import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
import com.sap.sailing.domain.common.impl.DegreePosition;
import com.sap.sailing.domain.common.impl.Util;
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
import com.sap.sailing.domain.tracking.MarkPassing;
import com.sap.sailing.domain.tracking.Wind;
import com.sap.sailing.domain.tracking.WindTrack;
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRaceImpl;
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRegattaImpl;
import com.sap.sailing.domain.tracking.impl.EmptyWindStore;
import com.sap.sailing.domain.tracking.impl.MarkPassingImpl;
import com.sap.sailing.domain.tracking.impl.WindImpl;
import com.sap.sailing.domain.tracking.impl.WindTrackImpl;
@@ -213,4 +238,46 @@ public class WindTest {
// TODO create a tracked race with corresponding WEB wind source; put leader in downwind, trailer in upwind and
// TODO test that windward distance includes the full reaching leg's length
}
/**
* See bug 943. Wind estimation should return <code>null</code> instead of causing exceptions in case there
* is no wind data to bootstrap the estimator with.
*/
@Test
public void testWindEstimationReturnsNullIfNoUpwindStartAndNoOtherWindDataAvailable() {
DomainFactory domainFactory = DomainFactory.INSTANCE;
Buoy startFinishLeft = domainFactory.getOrCreateBuoy("Start/Finish left");
Buoy startFinishRight = domainFactory.getOrCreateBuoy("Start/Finish right");
ControlPoint startFinish = domainFactory.createGate(startFinishLeft, startFinishRight, "Start/Finish");
ControlPoint top = domainFactory.getOrCreateBuoy("Top");
Waypoint w1 = domainFactory.createWaypoint(startFinish);
Waypoint w2 = domainFactory.createWaypoint(top);
Waypoint w3 = domainFactory.createWaypoint(startFinish);
Competitor competitor = new CompetitorImpl(123, "Test Competitor", new TeamImpl("STG", Collections.singleton(
new PersonImpl("Test Competitor", new NationalityImpl("GER"),
/* dateOfBirth */null, "This is famous " + "Test Competitor")), new PersonImpl("Rigo van Maas",
new NationalityImpl("NED"),
/* dateOfBirth */null, "This is Rigo, the coach")), new BoatImpl("Test Competitor" + "'s boat",
new BoatClassImpl("505", /* typicallyStartsUpwind */true), null));
final BoatClass boatClass = domainFactory.getOrCreateBoatClass("ESS40");
DynamicTrackedRace trackedRace = new DynamicTrackedRaceImpl(new DynamicTrackedRegattaImpl(
new RegattaImpl("Test Regatta", boatClass,
/* trackedRegattaRegistry */ null, domainFactory.createScoringScheme(ScoringSchemeType.LOW_POINT))),
new RaceDefinitionImpl("Test Race",
new CourseImpl("Test Course", Arrays.asList(new Waypoint[] { w1, w2, w3 })),
boatClass, Collections.singleton(competitor)),
EmptyWindStore.INSTANCE, /* delayToLiveInMillis */ 1000,
/* millisecondsOverWhichToAverageWind */ 30000,
/* millisecondsOverWhichToAverageSpeed */ 30000);
TimePoint start = MillisecondsTimePoint.now();
TimePoint topMarkRounding = start.plus(30000);
TimePoint finish = topMarkRounding.plus(30000);
trackedRace.updateMarkPassings(competitor, Arrays.asList(new MarkPassing[] {
new MarkPassingImpl(start, w1, competitor),
new MarkPassingImpl(topMarkRounding, w2, competitor),
new MarkPassingImpl(finish, w3, competitor)
}));
assertFalse(boatClass.typicallyStartsUpwind());
assertNull(trackedRace.getEstimatedWindDirection(new DegreePosition(0, 0), MillisecondsTimePoint.now()));
}
}
@@ -83,7 +83,8 @@ public class RaceCourseReceiver extends AbstractReceiverWithQueue<Route, RouteDa
Course course = getDomainFactory().createCourse(event.getA().getName(), event.getB().getPoints());
RaceDefinition existingRaceDefinitionForRace = getDomainFactory().getExistingRaceDefinitionForRace(event.getC());
if (existingRaceDefinitionForRace != null) {
logger.log(Level.INFO, "Received course update for existing race "+event.getC().getName());
logger.log(Level.INFO, "Received course update for existing race "+event.getC().getName()+": "+
event.getB().getPoints());
// race already exists; this means that we obviously found a course re-definition (yuck...)
// Therefore, don't create TrackedRace again because it already exists.
try {
@@ -1,7 +1,7 @@
package com.sap.sailing.domain.base;
import com.sap.sailing.domain.common.Named;
import com.sap.sailing.domain.common.RaceIdentifier;
import com.sap.sailing.domain.common.Renamable;
import com.sap.sailing.domain.common.impl.Util;
import com.sap.sailing.domain.common.impl.Util.Pair;
import com.sap.sailing.domain.leaderboard.Leaderboard;
@@ -25,7 +25,7 @@ import com.sap.sailing.util.impl.RaceColumnListeners;
* @author Axel Uhl (D043530)
*
*/
public interface RaceColumn extends Renamable {
public interface RaceColumn extends Named {
/**
* @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
@@ -11,6 +11,8 @@ public interface RaceColumnListener extends Serializable {
void isMedalRaceChanged(RaceColumn raceColumn, boolean newIsMedalRace);
boolean canAddRaceColumnToContainer(RaceColumn raceColumn);
void raceColumnAddedToContainer(RaceColumn raceColumn);
void raceColumnRemovedFromContainer(RaceColumn raceColumn);
@@ -14,10 +14,8 @@ public abstract class AbstractRaceColumn extends SimpleAbstractRaceColumn implem
private static final long serialVersionUID = -7801617988982540470L;
private final Map<Fleet, TrackedRace> trackedRaces;
private String name;
private final Map<Fleet, RaceIdentifier> raceIdentifiers;
public AbstractRaceColumn(String name) {
this.name = name;
public AbstractRaceColumn() {
this.trackedRaces = new HashMap<Fleet, TrackedRace>();
this.raceIdentifiers = new HashMap<Fleet, RaceIdentifier>();
}
@@ -49,16 +47,6 @@ public abstract class AbstractRaceColumn extends SimpleAbstractRaceColumn implem
}
}
@Override
public String getName() {
return name;
}
@Override
public void setName(String newName) {
this.name = newName;
}
@Override
public RaceIdentifier getRaceIdentifier(Fleet fleet) {
return raceIdentifiers.get(fleet);
@@ -76,6 +76,14 @@ public class CourseImpl extends NamedImpl implements Course {
public void unlockAfterRead() {
LockUtil.unlockAfterRead(lock);
}
public void lockForWrite() {
LockUtil.lockForWrite(lock);
}
public void unlockAfterWrite() {
LockUtil.unlockAfterWrite(lock);
}
private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
ois.defaultReadObject();
@@ -30,6 +30,7 @@ import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.leaderboard.ScoringScheme;
import com.sap.sailing.domain.leaderboard.impl.HighPoint;
import com.sap.sailing.domain.leaderboard.impl.HighPointExtremeSailingSeriesOverall;
import com.sap.sailing.domain.leaderboard.impl.HighPointLastBreaksTie;
import com.sap.sailing.domain.leaderboard.impl.LowPoint;
import com.sap.sailing.domain.tracking.MarkPassing;
import com.sap.sailing.domain.tracking.impl.MarkPassingImpl;
@@ -237,6 +238,8 @@ public class DomainFactoryImpl implements DomainFactory {
return new HighPoint();
case HIGH_POINT_ESS_OVERALL:
return new HighPointExtremeSailingSeriesOverall();
case HIGH_POINT_LAST_BREAKS_TIE:
return new HighPointLastBreaksTie();
default:
throw new RuntimeException("Unknown scoring scheme type "+scoringSchemeType.name());
}
@@ -18,6 +18,7 @@ import com.sap.sailing.domain.tracking.TrackedRegattaRegistry;
public class RaceColumnInSeriesImpl extends AbstractRaceColumn implements RaceColumnInSeries {
private static final long serialVersionUID = -2199678838624406645L;
private final Series series;
private final String name;
/**
* Making this transient will leave it <code>null</code> on a replica. This, however, is OK because
@@ -34,11 +35,16 @@ public class RaceColumnInSeriesImpl extends AbstractRaceColumn implements RaceCo
* carried out.
*/
public RaceColumnInSeriesImpl(String name, Series series, TrackedRegattaRegistry trackedRegattaRegistry) {
super(name);
this.name = name;
this.series = series;
this.trackedRegattaRegistry = trackedRegattaRegistry;
}
@Override
public String getName() {
return name;
}
@Override
public Regatta getRegatta() {
return getSeries().getRegatta();
@@ -22,6 +22,7 @@ import com.sap.sailing.domain.common.RegattaName;
import com.sap.sailing.domain.common.impl.NamedImpl;
import com.sap.sailing.domain.leaderboard.ScoringScheme;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.TrackedRegatta;
import com.sap.sailing.domain.tracking.TrackedRegattaRegistry;
import com.sap.sailing.util.impl.RaceColumnListeners;
@@ -48,6 +49,13 @@ public class RegattaImpl extends NamedImpl implements Regatta, RaceColumnListene
/**
* Constructs a regatta with a single default series with empty race column list, and a single default fleet which
* is not {@link #isPersistent() marked for persistence}.
*
* @param trackedRegattaRegistry
* used to find the {@link TrackedRegatta} for this column's series' {@link Series#getRegatta() regatta}
* in order to re-associate a {@link TrackedRace} passed to {@link #setTrackedRace(Fleet, TrackedRace)}
* with this column's series' {@link TrackedRegatta}, and the tracked race's {@link RaceDefinition} with
* this column's series {@link Regatta}, respectively. If <code>null</code>, the re-association won't be
* carried out.
*/
public RegattaImpl(String baseName, BoatClass boatClass, TrackedRegattaRegistry trackedRegattaRegistry, ScoringScheme scoringScheme) {
this(baseName, boatClass, Collections.singletonList(new SeriesImpl("Default", /* isMedal */false, Collections
@@ -207,6 +215,11 @@ public class RegattaImpl extends NamedImpl implements Regatta, RaceColumnListene
raceColumnListeners.notifyListenersAboutIsMedalRaceChanged(raceColumn, newIsMedalRace);
}
@Override
public boolean canAddRaceColumnToContainer(RaceColumn raceColumn) {
return raceColumnListeners.canAddRaceColumnToContainer(raceColumn);
}
@Override
public void raceColumnAddedToContainer(RaceColumn raceColumn) {
raceColumnListeners.notifyListenersAboutRaceColumnAddedToContainer(raceColumn);
@@ -10,11 +10,13 @@ import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.RaceColumn;
import com.sap.sailing.domain.base.RaceColumnInSeries;
import com.sap.sailing.domain.base.RaceColumnListener;
import com.sap.sailing.domain.base.RaceDefinition;
import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.base.Series;
import com.sap.sailing.domain.common.impl.NamedImpl;
import com.sap.sailing.domain.common.impl.Util;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.TrackedRegatta;
import com.sap.sailing.domain.tracking.TrackedRegattaRegistry;
import com.sap.sailing.util.impl.RaceColumnListeners;
@@ -28,7 +30,14 @@ public class SeriesImpl extends NamedImpl implements Series, RaceColumnListener
private final RaceColumnListeners raceColumnListeners;
/**
* @param fleets must be non-empty
* @param fleets
* must be non-empty
* @param trackedRegattaRegistry
* used to find the {@link TrackedRegatta} for this column's series' {@link Series#getRegatta() regatta}
* in order to re-associate a {@link TrackedRace} passed to {@link #setTrackedRace(Fleet, TrackedRace)}
* with this column's series' {@link TrackedRegatta}, and the tracked race's {@link RaceDefinition} with
* this column's series {@link Regatta}, respectively. If <code>null</code>, the re-association won't be
* carried out.
*/
public SeriesImpl(String name, boolean isMedal, Iterable<? extends Fleet> fleets, Iterable<String> raceColumnNames,
TrackedRegattaRegistry trackedRegattaRegistry) {
@@ -84,16 +93,36 @@ public class SeriesImpl extends NamedImpl implements Series, RaceColumnListener
public Iterable<? extends RaceColumnInSeries> getRaceColumns() {
return raceColumns;
}
/**
* @param trackedRegattaRegistry
* used to find the {@link TrackedRegatta} for this column's series' {@link Series#getRegatta() regatta}
* in order to re-associate a {@link TrackedRace} passed to {@link #setTrackedRace(Fleet, TrackedRace)}
* with this column's series' {@link TrackedRegatta}, and the tracked race's {@link RaceDefinition} with
* this column's series {@link Regatta}, respectively. If <code>null</code>, the re-association won't be
* carried out.
*/
@Override
public RaceColumnInSeries addRaceColumn(String raceColumnName, TrackedRegattaRegistry trackedRegattaRegistry) {
final RaceColumnInSeriesImpl result = createRaceColumn(raceColumnName, trackedRegattaRegistry);
result.addRaceColumnListener(this);
raceColumns.add(result);
raceColumnListeners.notifyListenersAboutRaceColumnAddedToContainer(result);
RaceColumnInSeriesImpl result = createRaceColumn(raceColumnName, trackedRegattaRegistry);
if (raceColumnListeners.canAddRaceColumnToContainer(result)) {
result.addRaceColumnListener(this);
raceColumns.add(result);
raceColumnListeners.notifyListenersAboutRaceColumnAddedToContainer(result);
} else {
result = null;
}
return result;
}
/**
* @param trackedRegattaRegistry
* used to find the {@link TrackedRegatta} for this column's series' {@link Series#getRegatta() regatta}
* in order to re-associate a {@link TrackedRace} passed to {@link #setTrackedRace(Fleet, TrackedRace)}
* with this column's series' {@link TrackedRegatta}, and the tracked race's {@link RaceDefinition} with
* this column's series {@link Regatta}, respectively. If <code>null</code>, the re-association won't be
* carried out.
*/
private RaceColumnInSeriesImpl createRaceColumn(String raceColumnName, TrackedRegattaRegistry trackedRegattaRegistry) {
RaceColumnInSeriesImpl result = new RaceColumnInSeriesImpl(raceColumnName, this, trackedRegattaRegistry);
return result;
@@ -174,6 +203,16 @@ public class SeriesImpl extends NamedImpl implements Series, RaceColumnListener
raceColumnListeners.notifyListenersAboutIsMedalRaceChanged(raceColumn, newIsMedalRace);
}
/**
* A series listens on its columns; individual columns, however, don't ask whether they can be added; the series itself does.
*
* @see #addRaceColumn(String, TrackedRegattaRegistry)
*/
@Override
public boolean canAddRaceColumnToContainer(RaceColumn raceColumn) {
return true;
}
@Override
public void raceColumnAddedToContainer(RaceColumn raceColumn) {
raceColumnListeners.notifyListenersAboutRaceColumnAddedToContainer(raceColumn);
@@ -3,5 +3,6 @@ package com.sap.sailing.domain.leaderboard;
import com.sap.sailing.domain.base.RaceColumn;
public interface FlexibleRaceColumn extends RaceColumn {
void setName(String newName);
void setIsMedalRace(boolean isMedalRace);
}
@@ -261,11 +261,13 @@ public interface Leaderboard extends Named {
* Tells if the column represented by <code>raceColumn</code> shall be considered when counting the number of "races
* so far" for discarding. Although medal races are never discarded themselves, they still count in determining the
* number of "races so far" which is then the basis for deciding how many races may be discarded. If a leaderboard
* has corrections for a column then that column shall be considered for discarding and counts for determining the
* number of races so far. Also, if a tracked race is connected to the column and has started already, the column is
* to be considered for discarding.
* has corrections for a column for the <code>competitor</code> then that column shall be considered for discarding
* and counts for determining the number of races so far. Also, if a tracked race is connected to the column and has
* started already, the column is to be considered for discarding unless the column has several unordered fleets and
* not all fleets have started their race yet (see
* {@link ScoringScheme#isValidInTotalScore(Leaderboard, RaceColumn, TimePoint)}).
*/
boolean considerForDiscarding(RaceColumn raceColumn, TimePoint timePoint);
boolean countRaceForComparisonWithDiscardingThresholds(Competitor competitor, RaceColumn raceColumn, TimePoint timePoint);
public void setResultDiscardingRule(ThresholdBasedResultDiscardingRule discardingRule);
@@ -9,6 +9,7 @@ import com.sap.sailing.domain.base.RaceColumn;
import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.common.MaxPointsReason;
import com.sap.sailing.domain.common.ScoringSchemeType;
import com.sap.sailing.domain.common.TimePoint;
/**
* A leaderboard has a scoring scheme that decides how race ranks map to scores, how penalties are to be scored,
@@ -79,7 +80,15 @@ public interface ScoringScheme extends Serializable {
/**
* Usually, when all other sorting criteria end up in a tie, the last race sailed is used to decide.
* @param nullScoresAreBetter TODO
*/
int compareByLastRace(List<Double> o1Scores, List<Double> o2Scores, boolean nullScoresAreBetter);
/**
* Under certain circumstances, a scoring scheme may decide that the scores of a column are not (yet) to be used
* for the leaderboard's total scores. This may, e.g., be the case if a column is split into more than one fleet and
* those fleets are unordered. In that case, scores need to be available for all fleets before the column counts
* for the total scores.
* @param at TODO
*/
boolean isValidInTotalScore(Leaderboard leaderboard, RaceColumn raceColumn, TimePoint at);
}
@@ -4,10 +4,20 @@ import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
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.common.NoWindException;
import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.common.impl.Util;
import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.leaderboard.ScoringScheme;
import com.sap.sailing.domain.tracking.TrackedRace;
public abstract class AbstractScoringSchemeImpl implements ScoringScheme {
private static final long serialVersionUID = 6830414905539642446L;
@@ -58,6 +68,53 @@ public abstract class AbstractScoringSchemeImpl implements ScoringScheme {
public Comparator<Double> getScoreComparator(boolean nullScoresAreBetter) {
return new ScoreComparator(nullScoresAreBetter);
}
@Override
public boolean isValidInTotalScore(Leaderboard leaderboard, RaceColumn raceColumn, TimePoint at) {
boolean result;
Iterable<? extends Fleet> fleets = raceColumn.getFleets();
if (Util.size(fleets) <= 1 || allFleetsOrdered(fleets)) {
result = true;
} else {
// multiple unordered fleets; ensure that the leaderboard has results for all of them
result = leaderboardHasResultsForAllFleets(leaderboard, raceColumn, at);
}
return result;
}
private boolean leaderboardHasResultsForAllFleets(Leaderboard leaderboard, RaceColumn raceColumn, TimePoint at) {
Set<Fleet> fleetsForWhichNoScoreWasFound = new HashSet<Fleet>();
for (Fleet fleet : raceColumn.getFleets()) {
final TrackedRace trackedRaceForFleet = raceColumn.getTrackedRace(fleet);
if (trackedRaceForFleet == null || !trackedRaceForFleet.hasStarted(at)) {
fleetsForWhichNoScoreWasFound.add(fleet);
}
}
for (Competitor competitor : leaderboard.getCompetitors()) {
Fleet fleet = raceColumn.getFleetOfCompetitor(competitor);
if (fleetsForWhichNoScoreWasFound.contains(fleet)) {
try {
if (leaderboard.getNetPoints(competitor, raceColumn, at) != null) {
fleetsForWhichNoScoreWasFound.remove(fleet);
}
} catch (NoWindException nwe) {
// can't occur here because no started tracked race exists yet for the competitor
}
}
}
return fleetsForWhichNoScoreWasFound.isEmpty();
}
private boolean allFleetsOrdered(Iterable<? extends Fleet> fleets) {
boolean allOrdered = true;
for (Fleet fleet : fleets) {
if (fleet.getOrdering() == 0) {
allOrdered = false;
break;
}
}
return allOrdered;
}
/**
* Assuming both competitors scored in the same number of races, compares the sorted scores.
@@ -91,6 +148,10 @@ public abstract class AbstractScoringSchemeImpl implements ScoringScheme {
@Override
public int compareByLastRace(List<Double> o1Scores, List<Double> o2Scores, boolean nullScoresAreBetter) {
return getScoreComparator(nullScoresAreBetter).compare(o1Scores.get(o1Scores.size()-1), o2Scores.get(o2Scores.size()-1));
int result = 0;
if (!o1Scores.isEmpty() && !o2Scores.isEmpty()) {
result = getScoreComparator(nullScoresAreBetter).compare(o1Scores.get(o1Scores.size()-1), o2Scores.get(o2Scores.size()-1));
}
return result;
}
}
@@ -242,9 +242,11 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
public Double getTotalPoints(Competitor competitor, TimePoint timePoint) throws NoWindException {
double result = getCarriedPoints(competitor);
for (RaceColumn r : getRaceColumns()) {
final Double totalPoints = getTotalPoints(competitor, r, timePoint);
if (totalPoints != null) {
result += totalPoints;
if (getScoringScheme().isValidInTotalScore(this, r, timePoint)) {
final Double totalPoints = getTotalPoints(competitor, r, timePoint);
if (totalPoints != null) {
result += totalPoints;
}
}
}
return result;
@@ -334,18 +336,12 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
}
@Override
public boolean considerForDiscarding(RaceColumn raceColumn, TimePoint timePoint) {
boolean result = getScoreCorrection().hasCorrectionFor(raceColumn);
if (!result && !raceColumn.isMedalRace()) {
for (Fleet fleet : raceColumn.getFleets()) {
TrackedRace trackedRace = raceColumn.getTrackedRace(fleet);
if (trackedRace != null && trackedRace.hasStarted(timePoint)) {
result = true;
break;
}
}
}
return result;
public boolean countRaceForComparisonWithDiscardingThresholds(Competitor competitor, RaceColumn raceColumn, TimePoint timePoint) {
TrackedRace trackedRaceForCompetitorInColumn;
return getScoringScheme().isValidInTotalScore(this, raceColumn, timePoint) &&
(getScoreCorrection().isScoreCorrected(competitor, raceColumn) ||
((trackedRaceForCompetitorInColumn=raceColumn.getTrackedRace(competitor)) != null &&
trackedRaceForCompetitorInColumn.hasStarted(timePoint)));
}
@Override
@@ -422,6 +418,21 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
getRaceColumnListeners().notifyListenersAboutIsMedalRaceChanged(raceColumn, newIsMedalRace);
}
/**
* A leaderboard will only accept the addition of a race column if the column's name is unique across the leaderboard.
*/
@Override
public boolean canAddRaceColumnToContainer(RaceColumn newRaceColumn) {
boolean result = true;
for (RaceColumn raceColumn : getRaceColumns()) {
if (raceColumn.getName().equals(newRaceColumn.getName())) {
result = false;
break;
}
}
return result;
}
@Override
public void raceColumnAddedToContainer(RaceColumn raceColumn) {
getRaceColumnListeners().notifyListenersAboutRaceColumnAddedToContainer(raceColumn);
@@ -0,0 +1,24 @@
package com.sap.sailing.domain.leaderboard.impl;
import java.util.List;
import com.sap.sailing.domain.common.ScoringSchemeType;
/**
* In this variant of the {@link HighPoint}
* @author Axel Uhl (d043530)
*
*/
public class HighPointLastBreaksTie extends HighPoint {
private static final long serialVersionUID = -5338636946886101669L;
@Override
public int compareByBetterScore(List<Double> o1Scores, List<Double> o2Scores, boolean nullScoresAreBetter) {
return 0;
}
@Override
public ScoringSchemeType getType() {
return ScoringSchemeType.HIGH_POINT_LAST_BREAKS_TIE;
}
}
@@ -50,11 +50,13 @@ public class LeaderboardTotalRankComparator implements Comparator<Competitor> {
private final ScoringScheme scoringScheme;
private final Map<Pair<Competitor, RaceColumn>, Double> totalPointsCache;
private final boolean nullScoresAreBetter;
private final TimePoint timePoint;
public LeaderboardTotalRankComparator(Leaderboard leaderboard, TimePoint timePoint,
ScoringScheme scoringScheme, boolean nullScoresAreBetter) throws NoWindException {
super();
this.leaderboard = leaderboard;
this.timePoint = timePoint;
this.scoringScheme = scoringScheme;
this.nullScoresAreBetter = nullScoresAreBetter;
totalPointsCache = new HashMap<Pair<Competitor, RaceColumn>, Double>();
@@ -79,29 +81,31 @@ public class LeaderboardTotalRankComparator implements Comparator<Competitor> {
Double o1MedalRaceScore = 0.0;
Double o2MedalRaceScore = 0.0;
for (RaceColumn raceColumn : getLeaderboard().getRaceColumns()) {
int preemptiveColumnResult = 0;
final Double o1Score = totalPointsCache.get(new Pair<Competitor, RaceColumn>(o1, raceColumn));
if (o1Score != null) {
o1Scores.add(o1Score);
o1ScoreSum += o1Score;
}
final Double o2Score = totalPointsCache.get(new Pair<Competitor, RaceColumn>(o2, raceColumn));
if (o2Score != null) {
o2Scores.add(o2Score);
o2ScoreSum += o2Score;
}
if (raceColumn.isMedalRace()) {
o1MedalRaceScore = o1Score;
o2MedalRaceScore = o2Score;
// similar to compareByFleet, however, tracking is not required; having medal race column points
// (tracked or manual) is sufficient
preemptiveColumnResult = compareByMedalRaceParticipation(o1Score, o2Score);
}
if (preemptiveColumnResult == 0) {
preemptiveColumnResult = compareByFleet(raceColumn, o1, o2);
}
if (preemptiveColumnResult != 0) {
return preemptiveColumnResult;
if (getLeaderboard().getScoringScheme().isValidInTotalScore(getLeaderboard(), raceColumn, timePoint)) {
int preemptiveColumnResult = 0;
final Double o1Score = totalPointsCache.get(new Pair<Competitor, RaceColumn>(o1, raceColumn));
if (o1Score != null) {
o1Scores.add(o1Score);
o1ScoreSum += o1Score;
}
final Double o2Score = totalPointsCache.get(new Pair<Competitor, RaceColumn>(o2, raceColumn));
if (o2Score != null) {
o2Scores.add(o2Score);
o2ScoreSum += o2Score;
}
if (raceColumn.isMedalRace()) {
o1MedalRaceScore = o1Score;
o2MedalRaceScore = o2Score;
// similar to compareByFleet, however, tracking is not required; having medal race column points
// (tracked or manual) is sufficient
preemptiveColumnResult = compareByMedalRaceParticipation(o1Score, o2Score);
}
if (preemptiveColumnResult == 0) {
preemptiveColumnResult = compareByFleet(raceColumn, o1, o2);
}
if (preemptiveColumnResult != 0) {
return preemptiveColumnResult;
}
}
}
// now count the races in which they scored; if they scored in a different number of races, prefer the
@@ -10,7 +10,8 @@ import com.sap.sailing.domain.leaderboard.FlexibleRaceColumn;
public class RaceColumnImpl extends AbstractRaceColumn implements FlexibleRaceColumn {
private static final long serialVersionUID = -7801617988982540470L;
private String name;
/**
* All fleets for which this column can contain a race. This is the maximum set of keys possible for
* {@link #trackedRaces} and {@link #raceIdentifiers}.
@@ -19,7 +20,8 @@ public class RaceColumnImpl extends AbstractRaceColumn implements FlexibleRaceCo
private boolean medalRace;
public RaceColumnImpl(String name, boolean medalRace, Iterable<Fleet> fleets) {
super(name);
super();
this.name = name;
this.medalRace = medalRace;
List<Fleet> myFleets = new ArrayList<Fleet>();
for (Fleet fleet : fleets) {
@@ -29,6 +31,16 @@ public class RaceColumnImpl extends AbstractRaceColumn implements FlexibleRaceCo
this.fleets = myFleets;
}
@Override
public void setName(String newName) {
this.name = newName;
}
@Override
public String getName() {
return name;
}
@Override
public boolean isMedalRace() {
return medalRace;
@@ -51,7 +51,7 @@ public class ResultDiscardingRuleImpl implements ThresholdBasedResultDiscardingR
@Override
public Set<RaceColumn> getDiscardedRaceColumns(final Competitor competitor, final Leaderboard leaderboard, final TimePoint timePoint) {
int resultsToDiscard = getNumberOfResultsToDiscard(leaderboard.getRaceColumns(), leaderboard, timePoint);
int resultsToDiscard = getNumberOfResultsToDiscard(competitor, leaderboard.getRaceColumns(), leaderboard, timePoint);
Set<RaceColumn> result;
if (resultsToDiscard > 0) {
result = new HashSet<RaceColumn>();
@@ -94,11 +94,11 @@ public class ResultDiscardingRuleImpl implements ThresholdBasedResultDiscardingR
return result;
}
private int getNumberOfResultsToDiscard(Iterable<RaceColumn> raceColumns, Leaderboard leaderboard, TimePoint timePoint) {
private int getNumberOfResultsToDiscard(Competitor competitor, Iterable<RaceColumn> raceColumns, Leaderboard leaderboard, TimePoint timePoint) {
int numberOfResultsToDiscard;
int numberOfStartedRaces = 0;
for (RaceColumn raceInLeaderboard : raceColumns) {
if (leaderboard.considerForDiscarding(raceInLeaderboard, timePoint)) {
if (leaderboard.countRaceForComparisonWithDiscardingThresholds(competitor, raceInLeaderboard, timePoint)) {
numberOfStartedRaces++;
}
}
@@ -95,10 +95,6 @@ public class MetaLeaderboardColumn extends SimpleAbstractRaceColumn implements R
return false;
}
@Override
public void setName(String newName) {
}
@Override
public void releaseTrackedRace(Fleet fleet) {
}
@@ -118,6 +114,11 @@ public class MetaLeaderboardColumn extends SimpleAbstractRaceColumn implements R
getRaceColumnListeners().notifyListenersAboutIsMedalRaceChanged(raceColumn, newIsMedalRace);
}
@Override
public boolean canAddRaceColumnToContainer(RaceColumn raceColumn) {
return getRaceColumnListeners().canAddRaceColumnToContainer(raceColumn);
}
@Override
public void raceColumnAddedToContainer(RaceColumn raceColumn) {
getRaceColumnListeners().notifyListenersAboutRaceColumnAddedToContainer(raceColumn);
@@ -41,12 +41,14 @@ public class CombinedWindAsNavigableSet extends VirtualWindFixesAsNavigableSet {
/**
* Time point up to and including which the GPS fixes are considered in the race's tracks. Returns the value of
* {@link #to} unless it is <code>null</code>. In this case, the time point of the
* {@link TrackedRace#getEndOfRace() assumed end of race},
* {@link #ceilingToResolution(Wind) ceiled to the resolution of this set} will be returned instead. If no valid
* time of a newest event can be obtained from the race, <code>MillisecondsTimePoint(1)</code> is returned instead.
* {@link TrackedRace#getEndOfRace() assumed end of race}, {@link #ceilingToResolution(Wind) ceiled to the
* resolution of this set} will be considered instead. If no assumed end of race can be determined either, the time
* point of the last event received so far is used. If no valid time of a newest event can be obtained from the
* race, <code>MillisecondsTimePoint(1)</code> is returned instead.
*/
protected TimePoint getTo() {
return getToInternal() == null ? getTrackedRace().getEndOfRace() == null ? new MillisecondsTimePoint(1)
return getToInternal() == null ? getTrackedRace().getEndOfRace() == null ? getTrackedRace().getTimePointOfLastEvent() == null ?
new MillisecondsTimePoint(1) : getTrackedRace().getTimePointOfLastEvent()
: ceilingToResolution(getTrackedRace().getEndOfRace()) : getToInternal();
}
@@ -63,7 +63,6 @@ public class DynamicTrackedRaceImpl extends TrackedRaceImpl implements
windSourcesToExclude.add(windSourceToExclude);
}
windSourcesToExclude.add(new WindSourceImpl(WindSourceType.COURSE_BASED));
windSourcesToExclude.add(new WindSourceImpl(WindSourceType.TRACK_BASED_ESTIMATION));
setWindSourcesToExclude(windSourcesToExclude);
}
@@ -794,6 +794,14 @@ public class GPSFixTrackImpl<ItemType, FixType extends GPSFix> extends TrackImpl
protected void invalidateValidityAndDistanceCaches(FixType gpsFix) {
assertWriteLock();
TimePoint distanceCacheInvalidationStart = gpsFix.getTimePoint();
// see also bug 968: cache entries for intervals ending after the last fix need to be removed because they are
// based on the last fix's position and don't extrapolate; now, with the new gpsFix, the positions between the last
// and the new fix are defined by interpolation and hence differ from the previous assumption the competitor would have
// stopped moving at the last fix.
FixType last = getInternalFixes().lower(gpsFix);
if (last != null) {
distanceCacheInvalidationStart = last.getTimePoint().plus(1); // add one millisecond to invalidate *after* the last fix only
}
gpsFix.invalidateCache();
Iterable<FixType> lowers = getEarlierFixesWhoseValidityMayBeAffected(gpsFix);
for (FixType lower : lowers) {
@@ -1526,7 +1526,8 @@ public abstract class TrackedRaceImpl implements TrackedRace, CourseListener {
try {
for (Competitor competitor : getRace().getCompetitors()) {
TrackedLegOfCompetitor leg = getTrackedLeg(competitor, timePoint);
if (leg != null) {
// if bearings was set to null this indicates there was an exception; no need for further calculations, return null
if (bearings != null && leg != null) {
TrackedLeg trackedLeg = getTrackedLeg(leg.getLeg());
LegType legType;
try {
@@ -1543,8 +1544,7 @@ public abstract class TrackedRaceImpl implements TrackedRace, CourseListener {
if (estimatedSpeedWithConfidence != null
&& estimatedSpeedWithConfidence.getObject() != null &&
// Mark passings may be missing or far off. This can lead to boats apparently
// going
// "backwards" regarding the leg's direction; ignore those
// going "backwards" regarding the leg's direction; ignore those
isNavigatingForward(estimatedSpeedWithConfidence.getObject().getBearing(),
trackedLeg, timePoint)) {
// additionally to generally excluding maneuvers, reduce confidence around mark
@@ -1,7 +1,5 @@
package com.sap.sailing.util.impl;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.TimeUnit;
@@ -16,25 +14,40 @@ public class LockUtil {
private static final int NUMBER_OF_SECONDS_TO_WAIT_FOR_LOCK = 5;
private static final Logger logger = Logger.getLogger(Util.class.getName());
private static final Map<NamedReentrantReadWriteLock, TimePoint> lastTimeWriteLockWasObtained = new WeakHashMap<>();
/**
* Bug <a href="http://bugs.sun.com/view_bug.do?bug_id=6822370">http://bugs.sun.com/view_bug.do?bug_id=6822370</a> seems
* dangerous, particularly if it happens in a <code>LiveLeaderboardUpdater</code> thread. Even though the bug is reported to
* have been fixed in JDK 7(b79) we should be careful. This method tries to acquire a lock, allowing for five seconds to pass.
* After five seconds and not having retrieved the lock, tries again until the lock has been acquired.
* @param lockParent TODO
* @throws InterruptedException
*/
public static void lock(Lock lock, String lockDescriptionForTimeoutLogMessage) {
private static void lock(Lock lock, String lockDescriptionForTimeoutLogMessage, NamedReentrantReadWriteLock lockParent) {
boolean locked = false;
boolean interrupted = false;
while (!locked) {
try {
locked = lock.tryLock(NUMBER_OF_SECONDS_TO_WAIT_FOR_LOCK, TimeUnit.SECONDS);
if (!locked) {
final ByteArrayOutputStream bos = new ByteArrayOutputStream();
new Throwable("This is where the lock couldn't be acquired").printStackTrace(new PrintStream(
bos));
logger.info("Couldn't acquire lock "+lockDescriptionForTimeoutLogMessage+" in "+NUMBER_OF_SECONDS_TO_WAIT_FOR_LOCK+"s at "+
getCurrentStackTrace()+"Trying again...");
StringBuilder message = new StringBuilder();
message.append("Couldn't acquire lock ");
message.append(lockDescriptionForTimeoutLogMessage);
message.append(" in ");
message.append(NUMBER_OF_SECONDS_TO_WAIT_FOR_LOCK);
message.append("s at ");
message.append(getCurrentStackTrace());
Thread writer = lockParent.getWriter();
if (writer != null) {
message.append("\nThe current writer is:\n");
appendThreadData(message, writer);
}
message.append("\nThe current readers are:\n");
for (Thread reader : lockParent.getReaders()) {
appendThreadData(message, reader);
}
message.append("Trying again...");
logger.info(message.toString());
}
}
catch (InterruptedException ex) {
@@ -47,9 +60,16 @@ public class LockUtil {
Thread.currentThread().interrupt();
}
}
private static void appendThreadData(StringBuilder message, Thread writer) {
message.append(writer);
message.append('\n');
message.append(getStackTrace(writer));
message.append('\n');
}
public static void lockForRead(NamedReentrantReadWriteLock lock) {
lock(lock.readLock(), "readLock "+lock.getName());
lock(lock.readLock(), "readLock "+lock.getName(), lock);
}
public static void unlockAfterRead(NamedReentrantReadWriteLock lock) {
@@ -57,7 +77,7 @@ public class LockUtil {
}
public static void lockForWrite(NamedReentrantReadWriteLock lock) {
lock(lock.writeLock(), "writeLock "+lock.getName());
lock(lock.writeLock(), "writeLock "+lock.getName(), lock);
synchronized (lastTimeWriteLockWasObtained) {
lastTimeWriteLockWasObtained.put(lock, MillisecondsTimePoint.now());
}
@@ -71,7 +91,7 @@ public class LockUtil {
}
if (timePointWriteLockWasObtained == null) {
logger.info("Internal error: write lock "+lock.getName()+" to be unlocked but no time recorded for when it was last obtained.\n"+
getCurrentStackTrace());
"This is where the lock interaction happened:\n"+getCurrentStackTrace());
} else {
TimePoint now = MillisecondsTimePoint.now();
if (now.asMillis()-timePointWriteLockWasObtained.asMillis() > 10000l) {
@@ -81,12 +101,17 @@ public class LockUtil {
}
}
}
private static String getStackTrace(Thread thread) {
StringBuilder sb = new StringBuilder();
for (StackTraceElement sf : thread.getStackTrace()) {
sb.append(sf.toString());
sb.append('\n');
}
return sb.toString();
}
private static String getCurrentStackTrace() {
final ByteArrayOutputStream bos = new ByteArrayOutputStream();
new Throwable("This is where the lock interaction happened").printStackTrace(new PrintStream(
bos));
String stackTrace = new String(bos.toByteArray());
return stackTrace;
return getStackTrace(Thread.currentThread());
}
}
@@ -1,5 +1,12 @@
package com.sap.sailing.util.impl;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import com.sap.sailing.domain.common.Named;
@@ -7,10 +14,136 @@ import com.sap.sailing.domain.common.Named;
public class NamedReentrantReadWriteLock extends ReentrantReadWriteLock implements Named {
private static final long serialVersionUID = 2906084982209339774L;
private final String name;
private final WriteLockWrapper writeLockWrapper;
private final ReadLockWrapper readLockWrapper;
private transient List<Thread> readers;
private class WriteLockWrapper extends WriteLock {
private static final long serialVersionUID = -4234819025137348944L;
private final WriteLock writeLock;
protected WriteLockWrapper(WriteLock writeLock) {
super(NamedReentrantReadWriteLock.this);
this.writeLock = writeLock;
}
@Override
public void lock() {
writeLock.lock();
}
@Override
public void lockInterruptibly() throws InterruptedException {
writeLock.lockInterruptibly();
}
@Override
public boolean tryLock() {
return writeLock.tryLock();
}
@Override
public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException {
return writeLock.tryLock(timeout, unit);
}
@Override
public void unlock() {
writeLock.unlock();
}
@Override
public Condition newCondition() {
return writeLock.newCondition();
}
@Override
public String toString() {
return writeLock.toString();
}
@Override
public boolean isHeldByCurrentThread() {
return writeLock.isHeldByCurrentThread();
}
@Override
public int getHoldCount() {
return writeLock.getHoldCount();
}
}
private class ReadLockWrapper extends ReadLock {
private static final long serialVersionUID = -4232071609936663619L;
private final ReadLock readLock;
protected ReadLockWrapper(ReadLock readLock) {
super(NamedReentrantReadWriteLock.this);
this.readLock = readLock;
}
@Override
public void lock() {
readLock.lock();
}
@Override
public void lockInterruptibly() throws InterruptedException {
try {
readLock.lockInterruptibly();
readers.add(Thread.currentThread());
} catch (InterruptedException ie) {
throw ie;
}
}
@Override
public boolean tryLock() {
boolean result = readLock.tryLock();
if (result) {
readers.add(Thread.currentThread());
}
return result;
}
@Override
public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException {
boolean result = readLock.tryLock(timeout, unit);
if (result) {
readers.add(Thread.currentThread());
}
return result;
}
@Override
public void unlock() {
readLock.unlock();
readers.remove(Thread.currentThread());
}
@Override
public Condition newCondition() {
return readLock.newCondition();
}
@Override
public String toString() {
return readLock.toString();
}
}
public NamedReentrantReadWriteLock(String name, boolean fair) {
super(fair);
this.name = name;
this.writeLockWrapper = new WriteLockWrapper(super.writeLock());
this.readLockWrapper = new ReadLockWrapper(super.readLock());
this.readers = Collections.synchronizedList(new ArrayList<Thread>());
}
private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
ois.defaultReadObject();
this.readers = Collections.synchronizedList(new ArrayList<Thread>());
}
@Override
@@ -18,8 +151,26 @@ public class NamedReentrantReadWriteLock extends ReentrantReadWriteLock implemen
return name;
}
@Override
public WriteLock writeLock() {
return writeLockWrapper;
}
@Override
public ReadLock readLock() {
return readLockWrapper;
}
public List<Thread> getReaders() {
return new ArrayList<Thread>(readers);
}
public Thread getWriter() {
return getOwner();
}
@Override
public String toString() {
return "ReentrantReadWriteLock "+getName()+" ("+(isFair()?"fair":"unfair")+")";
return "ReentrantReadWriteLock "+getName()+" ("+(isFair()?"fair":"unfair")+")";
}
}
@@ -92,4 +92,15 @@ public class RaceColumnListeners implements Serializable {
return new HashSet<RaceColumnListener>(raceColumnListeners);
}
}
public boolean canAddRaceColumnToContainer(RaceColumn columnToAdd) {
boolean result = true;
for (RaceColumnListener listener : getRaceColumnListeners()) {
result = listener.canAddRaceColumnToContainer(columnToAdd);
if (!result) {
break;
}
}
return result;
}
}
@@ -119,12 +119,9 @@ public class GwtTestCaseColumnToggling extends GWTTestCase {
}
private void addColumnToLeaderboard() {
leaderboardPanel = new LeaderboardPanelMock(service, LEADERBOARD_NAME,
null, sc);
leaderboardPanel = new LeaderboardPanelMock(service, LEADERBOARD_NAME, null, sc);
service.addColumnToLeaderboard(COLUMN1_NAME, LEADERBOARD_NAME, false,
new AsyncCallback<Void>() {
@Override
public void onFailure(Throwable caught) {
fail("Failed to add column to leaderboard.");
@@ -134,7 +131,7 @@ public class GwtTestCaseColumnToggling extends GWTTestCase {
@Override
public void onSuccess(Void result) {
System.out.println("Added column to leaderboard.");
RaceColumnDTO race = new RaceColumnDTO();
RaceColumnDTO race = new RaceColumnDTO(/* isValidInTotalScore */ null);
race.name = COLUMN1_NAME;
race.setMedalRace(false);
leaderboardPanel.addColumn(leaderboardPanel.createRaceColumn(race));
@@ -21,5 +21,5 @@
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.google.gwt.dev.DevMode"/>
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-war &quot;${project_loc:com.sap.sailing.gwt.ui}&quot; -noserver -remoteUI &quot;${gwt_remote_ui_server_port}:${unique_id}&quot; -logLevel INFO -codeServerPort 9997 -startupUrl /gwt/AdminConsole.html com.sap.sailing.gwt.ui.AdminConsole -startupUrl /gwt/LeaderboardEditing.html com.sap.sailing.gwt.ui.LeaderboardEditing -startupUrl /gwt/UserManagement.html com.sap.sailing.gwt.ui.UserManagement -startupUrl /gwt/Leaderboard.html com.sap.sailing.gwt.ui.Leaderboard -startupUrl /gwt/Spectator.html com.sap.sailing.gwt.ui.Spectator -startupUrl /gwt/RaceBoard.html com.sap.sailing.gwt.ui.RaceBoard"/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="com.sap.sailing.gwt.ui"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xmx1024m"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xmx2048m -XX:MaxPermSize=512m"/>
</launchConfiguration>
@@ -1,108 +1,107 @@
package com.sap.sailing.gwt.ui.adminconsole;
import java.util.ArrayList;
import java.util.List;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.LongBox;
import com.google.gwt.user.client.ui.TextBox;
import com.sap.sailing.domain.common.ScoringSchemeType;
import com.sap.sailing.gwt.ui.client.DataEntryDialog;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.leaderboard.ScoringSchemeTypeFormatter;
public abstract class AbstractLeaderboardDialog extends DataEntryDialog<LeaderboardDescriptor> {
protected final StringMessages stringMessages;
protected TextBox nameTextBox;
protected LeaderboardDescriptor leaderboard;
protected LongBox[] discardThresholdBoxes;
protected static final int MAX_NUMBER_OF_DISCARDED_RESULTS = 4;
public AbstractLeaderboardDialog(String title, LeaderboardDescriptor leaderboardDTO, StringMessages stringConstants,
Validator<LeaderboardDescriptor> validator, AsyncCallback<LeaderboardDescriptor> callback) {
super(title, null, stringConstants.ok(), stringConstants.cancel(), validator, callback);
this.stringMessages = stringConstants;
this.leaderboard = leaderboardDTO;
}
@Override
protected LeaderboardDescriptor getResult() {
int[] discardThresholdsBoxContents = getDiscardThresholds(discardThresholdBoxes);
leaderboard.setName(nameTextBox.getValue());
leaderboard.setDiscardThresholds(discardThresholdsBoxContents);
return leaderboard;
}
protected static int[] getDiscardThresholds(LongBox[] discardThresholdBoxes) {
List<Integer> discardThresholds = new ArrayList<Integer>();
// go backwards; starting from first non-zero element, add them; take over leading zeroes which validator shall discard
for (int i = discardThresholdBoxes.length-1; i>=0; i--) {
if ((discardThresholdBoxes[i].getValue() != null
&& discardThresholdBoxes[i].getValue().toString().length() > 0) || !discardThresholds.isEmpty()) {
if (discardThresholdBoxes[i].getValue() == null) {
discardThresholds.add(0, 0);
} else {
discardThresholds.add(0, discardThresholdBoxes[i].getValue().intValue());
}
}
}
int[] discardThresholdsBoxContents = new int[discardThresholds.size()];
for (int i = 0; i < discardThresholds.size(); i++) {
discardThresholdsBoxContents[i] = discardThresholds.get(i);
}
return discardThresholdsBoxContents;
}
@Override
public void show() {
super.show();
nameTextBox.setFocus(true);
}
protected static LongBox[] initEmptyDiscardThresholdBoxes(DataEntryDialog<?> dialog) {
LongBox[] result = new LongBox[MAX_NUMBER_OF_DISCARDED_RESULTS];
for (int i = 0; i < result.length; i++) {
result[i] = dialog.createLongBoxWithOptionalValue(null, 2);
result[i].setVisibleLength(2);
}
return result;
}
protected static LongBox[] initPrefilledDiscardThresholdBoxes(int[] valuesToShow, DataEntryDialog<?> dialog) {
LongBox[] result = new LongBox[MAX_NUMBER_OF_DISCARDED_RESULTS];
for (int i = 0; i < result.length; i++) {
if (i < valuesToShow.length) {
result[i] = dialog.createLongBox(valuesToShow[i], 2);
} else {
result[i] = dialog.createLongBoxWithOptionalValue(null, 2);
}
result[i].setVisibleLength(2);
}
return result;
}
protected static ListBox createScoringSchemeListBox(DataEntryDialog<?> dialog, StringMessages stringMessages) {
ListBox scoringSchemeListBox2 = dialog.createListBox(false);
for (ScoringSchemeType scoringSchemeType: ScoringSchemeType.values()) {
scoringSchemeListBox2.addItem(ScoringSchemeTypeFormatter.format(scoringSchemeType, stringMessages));
}
return scoringSchemeListBox2;
}
protected static ScoringSchemeType getSelectedScoringSchemeType(ListBox scoringSchemeListBox, StringMessages stringMessages) {
ScoringSchemeType result = null;
int selIndex = scoringSchemeListBox.getSelectedIndex();
if (selIndex >= 0) {
String itemText = scoringSchemeListBox.getItemText(selIndex);
for (ScoringSchemeType scoringSchemeType : ScoringSchemeType.values()) {
if (ScoringSchemeTypeFormatter.format(scoringSchemeType, stringMessages).equals(itemText)) {
result = scoringSchemeType;
break;
}
}
}
return result;
}
}
package com.sap.sailing.gwt.ui.adminconsole;
import java.util.ArrayList;
import java.util.List;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.LongBox;
import com.google.gwt.user.client.ui.TextBox;
import com.sap.sailing.domain.common.ScoringSchemeType;
import com.sap.sailing.gwt.ui.client.DataEntryDialog;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.leaderboard.ScoringSchemeTypeFormatter;
public abstract class AbstractLeaderboardDialog extends DataEntryDialog<LeaderboardDescriptor> {
protected final StringMessages stringMessages;
protected TextBox nameTextBox;
protected LeaderboardDescriptor leaderboard;
protected LongBox[] discardThresholdBoxes;
protected static final int MAX_NUMBER_OF_DISCARDED_RESULTS = 4;
public AbstractLeaderboardDialog(String title, LeaderboardDescriptor leaderboardDTO, StringMessages stringConstants,
Validator<LeaderboardDescriptor> validator, DialogCallback<LeaderboardDescriptor> callback) {
super(title, null, stringConstants.ok(), stringConstants.cancel(), validator, callback);
this.stringMessages = stringConstants;
this.leaderboard = leaderboardDTO;
}
@Override
protected LeaderboardDescriptor getResult() {
int[] discardThresholdsBoxContents = getDiscardThresholds(discardThresholdBoxes);
leaderboard.setName(nameTextBox.getValue());
leaderboard.setDiscardThresholds(discardThresholdsBoxContents);
return leaderboard;
}
protected static int[] getDiscardThresholds(LongBox[] discardThresholdBoxes) {
List<Integer> discardThresholds = new ArrayList<Integer>();
// go backwards; starting from first non-zero element, add them; take over leading zeroes which validator shall discard
for (int i = discardThresholdBoxes.length-1; i>=0; i--) {
if ((discardThresholdBoxes[i].getValue() != null
&& discardThresholdBoxes[i].getValue().toString().length() > 0) || !discardThresholds.isEmpty()) {
if (discardThresholdBoxes[i].getValue() == null) {
discardThresholds.add(0, 0);
} else {
discardThresholds.add(0, discardThresholdBoxes[i].getValue().intValue());
}
}
}
int[] discardThresholdsBoxContents = new int[discardThresholds.size()];
for (int i = 0; i < discardThresholds.size(); i++) {
discardThresholdsBoxContents[i] = discardThresholds.get(i);
}
return discardThresholdsBoxContents;
}
@Override
public void show() {
super.show();
nameTextBox.setFocus(true);
}
protected static LongBox[] initEmptyDiscardThresholdBoxes(DataEntryDialog<?> dialog) {
LongBox[] result = new LongBox[MAX_NUMBER_OF_DISCARDED_RESULTS];
for (int i = 0; i < result.length; i++) {
result[i] = dialog.createLongBoxWithOptionalValue(null, 2);
result[i].setVisibleLength(2);
}
return result;
}
protected static LongBox[] initPrefilledDiscardThresholdBoxes(int[] valuesToShow, DataEntryDialog<?> dialog) {
LongBox[] result = new LongBox[MAX_NUMBER_OF_DISCARDED_RESULTS];
for (int i = 0; i < result.length; i++) {
if (i < valuesToShow.length) {
result[i] = dialog.createLongBox(valuesToShow[i], 2);
} else {
result[i] = dialog.createLongBoxWithOptionalValue(null, 2);
}
result[i].setVisibleLength(2);
}
return result;
}
protected static ListBox createScoringSchemeListBox(DataEntryDialog<?> dialog, StringMessages stringMessages) {
ListBox scoringSchemeListBox2 = dialog.createListBox(false);
for (ScoringSchemeType scoringSchemeType: ScoringSchemeType.values()) {
scoringSchemeListBox2.addItem(ScoringSchemeTypeFormatter.format(scoringSchemeType, stringMessages));
}
return scoringSchemeListBox2;
}
protected static ScoringSchemeType getSelectedScoringSchemeType(ListBox scoringSchemeListBox, StringMessages stringMessages) {
ScoringSchemeType result = null;
int selIndex = scoringSchemeListBox.getSelectedIndex();
if (selIndex >= 0) {
String itemText = scoringSchemeListBox.getItemText(selIndex);
for (ScoringSchemeType scoringSchemeType : ScoringSchemeType.values()) {
if (ScoringSchemeTypeFormatter.format(scoringSchemeType, stringMessages).equals(itemText)) {
result = scoringSchemeType;
break;
}
}
}
return result;
}
}
@@ -1,25 +1,24 @@
package com.sap.sailing.gwt.ui.adminconsole;
import java.util.Collection;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.shared.EventDTO;
public class EventCreateDialog extends EventDialog {
public EventCreateDialog(Collection<EventDTO> existingEvents,
StringMessages stringConstants, AsyncCallback<EventDTO> callback) {
super(new EventParameterValidator(stringConstants, existingEvents),
stringConstants, callback);
nameEntryField = createTextBox(null);
nameEntryField.setWidth("200px");
venueEntryField = createTextBox(null);
venueEntryField.setWidth("200px");
publicationUrlEntryField = createTextBox(null);
publicationUrlEntryField.setWidth("200px");
isPublicCheckBox = createCheckbox("");
isPublicCheckBox.setValue(false);
}
}
package com.sap.sailing.gwt.ui.adminconsole;
import java.util.Collection;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.shared.EventDTO;
public class EventCreateDialog extends EventDialog {
public EventCreateDialog(Collection<EventDTO> existingEvents,
StringMessages stringConstants, DialogCallback<EventDTO> callback) {
super(new EventParameterValidator(stringConstants, existingEvents),
stringConstants, callback);
nameEntryField = createTextBox(null);
nameEntryField.setWidth("200px");
venueEntryField = createTextBox(null);
venueEntryField.setWidth("200px");
publicationUrlEntryField = createTextBox(null);
publicationUrlEntryField.setWidth("200px");
isPublicCheckBox = createCheckbox("");
isPublicCheckBox.setValue(false);
}
}
@@ -1,106 +1,105 @@
package com.sap.sailing.gwt.ui.adminconsole;
import java.util.ArrayList;
import java.util.Collection;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.sap.sailing.gwt.ui.client.DataEntryDialog;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.shared.EventDTO;
import com.sap.sailing.gwt.ui.shared.VenueDTO;
public class EventDialog extends DataEntryDialog<EventDTO> {
protected StringMessages stringConstants;
protected TextBox nameEntryField;
protected TextBox venueEntryField;
protected TextBox publicationUrlEntryField;
protected CheckBox isPublicCheckBox;
protected static class EventParameterValidator implements Validator<EventDTO> {
private StringMessages stringConstants;
private ArrayList<EventDTO> existingEvents;
public EventParameterValidator(StringMessages stringConstants, Collection<EventDTO> existingEvents) {
this.stringConstants = stringConstants;
this.existingEvents = new ArrayList<EventDTO>(existingEvents);
}
@Override
public String getErrorMessage(EventDTO eventToValidate) {
String errorMessage = null;
boolean nameNotEmpty = eventToValidate.name != null && eventToValidate.name.length() > 0;
boolean venueNotEmpty = eventToValidate.venue.name != null && eventToValidate.venue.name.length() > 0;
boolean unique = true;
for (EventDTO event : existingEvents) {
if (event.name.equals(eventToValidate.name)) {
unique = false;
break;
}
}
if (!nameNotEmpty) {
errorMessage = stringConstants.pleaseEnterAName();
} else if (!venueNotEmpty) {
errorMessage = stringConstants.pleaseEnterNonEmptyVenue();
} else if (!unique) {
errorMessage = stringConstants.eventWithThisNameAlreadyExists();
}
return errorMessage;
}
}
public EventDialog(EventParameterValidator validator, StringMessages stringConstants,
AsyncCallback<EventDTO> callback) {
super(stringConstants.event(), null, stringConstants.ok(), stringConstants.cancel(), validator,
callback);
this.stringConstants = stringConstants;
}
@Override
protected EventDTO getResult() {
EventDTO eventDTO = new EventDTO(nameEntryField.getText());
eventDTO.venue = new VenueDTO(venueEntryField.getText());
eventDTO.publicationUrl = publicationUrlEntryField.getText();
eventDTO.isPublic = isPublicCheckBox.getValue();
return eventDTO;
}
@Override
protected Widget getAdditionalWidget() {
VerticalPanel panel = new VerticalPanel();
Widget additionalWidget = super.getAdditionalWidget();
if (additionalWidget != null) {
panel.add(additionalWidget);
}
Grid formGrid = new Grid(4, 2);
panel.add(formGrid);
formGrid.setWidget(0, 0, new Label(stringConstants.name() + ":"));
formGrid.setWidget(0, 1, nameEntryField);
formGrid.setWidget(1, 0, new Label(stringConstants.venue() + ":"));
formGrid.setWidget(1, 1, venueEntryField);
formGrid.setWidget(2, 0, new Label(stringConstants.publicationUrl() + ":"));
formGrid.setWidget(2, 1, publicationUrlEntryField);
formGrid.setWidget(3, 0, new Label(stringConstants.isPublic() + ":"));
formGrid.setWidget(3, 1, isPublicCheckBox);
return panel;
}
@Override
public void show() {
super.show();
nameEntryField.setFocus(true);
}
}
package com.sap.sailing.gwt.ui.adminconsole;
import java.util.ArrayList;
import java.util.Collection;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.sap.sailing.gwt.ui.client.DataEntryDialog;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.shared.EventDTO;
import com.sap.sailing.gwt.ui.shared.VenueDTO;
public class EventDialog extends DataEntryDialog<EventDTO> {
protected StringMessages stringConstants;
protected TextBox nameEntryField;
protected TextBox venueEntryField;
protected TextBox publicationUrlEntryField;
protected CheckBox isPublicCheckBox;
protected static class EventParameterValidator implements Validator<EventDTO> {
private StringMessages stringConstants;
private ArrayList<EventDTO> existingEvents;
public EventParameterValidator(StringMessages stringConstants, Collection<EventDTO> existingEvents) {
this.stringConstants = stringConstants;
this.existingEvents = new ArrayList<EventDTO>(existingEvents);
}
@Override
public String getErrorMessage(EventDTO eventToValidate) {
String errorMessage = null;
boolean nameNotEmpty = eventToValidate.name != null && eventToValidate.name.length() > 0;
boolean venueNotEmpty = eventToValidate.venue.name != null && eventToValidate.venue.name.length() > 0;
boolean unique = true;
for (EventDTO event : existingEvents) {
if (event.name.equals(eventToValidate.name)) {
unique = false;
break;
}
}
if (!nameNotEmpty) {
errorMessage = stringConstants.pleaseEnterAName();
} else if (!venueNotEmpty) {
errorMessage = stringConstants.pleaseEnterNonEmptyVenue();
} else if (!unique) {
errorMessage = stringConstants.eventWithThisNameAlreadyExists();
}
return errorMessage;
}
}
public EventDialog(EventParameterValidator validator, StringMessages stringConstants,
DialogCallback<EventDTO> callback) {
super(stringConstants.event(), null, stringConstants.ok(), stringConstants.cancel(), validator,
callback);
this.stringConstants = stringConstants;
}
@Override
protected EventDTO getResult() {
EventDTO eventDTO = new EventDTO(nameEntryField.getText());
eventDTO.venue = new VenueDTO(venueEntryField.getText());
eventDTO.publicationUrl = publicationUrlEntryField.getText();
eventDTO.isPublic = isPublicCheckBox.getValue();
return eventDTO;
}
@Override
protected Widget getAdditionalWidget() {
VerticalPanel panel = new VerticalPanel();
Widget additionalWidget = super.getAdditionalWidget();
if (additionalWidget != null) {
panel.add(additionalWidget);
}
Grid formGrid = new Grid(4, 2);
panel.add(formGrid);
formGrid.setWidget(0, 0, new Label(stringConstants.name() + ":"));
formGrid.setWidget(0, 1, nameEntryField);
formGrid.setWidget(1, 0, new Label(stringConstants.venue() + ":"));
formGrid.setWidget(1, 1, venueEntryField);
formGrid.setWidget(2, 0, new Label(stringConstants.publicationUrl() + ":"));
formGrid.setWidget(2, 1, publicationUrlEntryField);
formGrid.setWidget(3, 0, new Label(stringConstants.isPublic() + ":"));
formGrid.setWidget(3, 1, isPublicCheckBox);
return panel;
}
@Override
public void show() {
super.show();
nameEntryField.setFocus(true);
}
}
@@ -1,24 +1,23 @@
package com.sap.sailing.gwt.ui.adminconsole;
import java.util.Collection;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.shared.EventDTO;
public class EventEditDialog extends EventDialog {
public EventEditDialog(EventDTO event, Collection<EventDTO> otherExistingEvents,
StringMessages stringConstants, AsyncCallback<EventDTO> callback) {
super(new EventParameterValidator(stringConstants, otherExistingEvents), stringConstants, callback);
nameEntryField = createTextBox(event.name);
nameEntryField.setWidth("200px");
venueEntryField = createTextBox(event.venue.name);
venueEntryField.setWidth("200px");
publicationUrlEntryField = createTextBox(event.publicationUrl);
publicationUrlEntryField.setWidth("200px");
isPublicCheckBox = createCheckbox("");
isPublicCheckBox.setValue(event.isPublic);
}
}
package com.sap.sailing.gwt.ui.adminconsole;
import java.util.Collection;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.shared.EventDTO;
public class EventEditDialog extends EventDialog {
public EventEditDialog(EventDTO event, Collection<EventDTO> otherExistingEvents,
StringMessages stringConstants, DialogCallback<EventDTO> callback) {
super(new EventParameterValidator(stringConstants, otherExistingEvents), stringConstants, callback);
nameEntryField = createTextBox(event.name);
nameEntryField.setWidth("200px");
venueEntryField = createTextBox(event.venue.name);
venueEntryField.setWidth("200px");
publicationUrlEntryField = createTextBox(event.publicationUrl);
publicationUrlEntryField.setWidth("200px");
isPublicCheckBox = createCheckbox("");
isPublicCheckBox.setValue(event.isPublic);
}
}
@@ -1,19 +1,18 @@
package com.sap.sailing.gwt.ui.adminconsole;
import java.util.Collection;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.sap.sailing.gwt.ui.client.ErrorReporter;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.shared.StrippedLeaderboardDTO;
public class FlexibleLeaderboardCreateDialog extends FlexibleLeaderboardDialog {
public FlexibleLeaderboardCreateDialog(Collection<StrippedLeaderboardDTO> existingLeaderboards, StringMessages stringMessages,
ErrorReporter errorReporter, AsyncCallback<LeaderboardDescriptor> callback) {
super(stringMessages.createFlexibleLeaderboard(), new LeaderboardDescriptor(), stringMessages, errorReporter, new FlexibleLeaderboardDialog.LeaderboardParameterValidator(stringMessages, existingLeaderboards), callback);
nameTextBox = createTextBox(null);
scoringSchemeListBox = createScoringSchemeListBox(this, stringMessages);
discardThresholdBoxes = initEmptyDiscardThresholdBoxes(this);
}
}
package com.sap.sailing.gwt.ui.adminconsole;
import java.util.Collection;
import com.sap.sailing.gwt.ui.client.ErrorReporter;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.shared.StrippedLeaderboardDTO;
public class FlexibleLeaderboardCreateDialog extends FlexibleLeaderboardDialog {
public FlexibleLeaderboardCreateDialog(Collection<StrippedLeaderboardDTO> existingLeaderboards, StringMessages stringMessages,
ErrorReporter errorReporter, DialogCallback<LeaderboardDescriptor> callback) {
super(stringMessages.createFlexibleLeaderboard(), new LeaderboardDescriptor(), stringMessages, errorReporter, new FlexibleLeaderboardDialog.LeaderboardParameterValidator(stringMessages, existingLeaderboards), callback);
nameTextBox = createTextBox(null);
scoringSchemeListBox = createScoringSchemeListBox(this, stringMessages);
discardThresholdBoxes = initEmptyDiscardThresholdBoxes(this);
}
}
@@ -1,98 +1,97 @@
package com.sap.sailing.gwt.ui.adminconsole;
import java.util.Collection;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.sap.sailing.gwt.ui.client.ErrorReporter;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.shared.StrippedLeaderboardDTO;
public abstract class FlexibleLeaderboardDialog extends AbstractLeaderboardDialog {
protected ListBox scoringSchemeListBox;
protected static class LeaderboardParameterValidator implements Validator<LeaderboardDescriptor> {
protected final StringMessages stringConstants;
protected final Collection<StrippedLeaderboardDTO> existingLeaderboards;
public LeaderboardParameterValidator(StringMessages stringConstants, Collection<StrippedLeaderboardDTO> existingLeaderboards){
super();
this.stringConstants = stringConstants;
this.existingLeaderboards = existingLeaderboards;
}
@Override
public String getErrorMessage(LeaderboardDescriptor leaderboardToValidate) {
String errorMessage;
boolean nonEmpty = leaderboardToValidate.getName() != null && leaderboardToValidate.getName().length() > 0;
boolean discardThresholdsAscending = true;
for (int i = 1; i < leaderboardToValidate.getDiscardThresholds().length; i++) {
// TODO what are correct values for discarding Thresholds?
if (0 < leaderboardToValidate.getDiscardThresholds().length){
discardThresholdsAscending = discardThresholdsAscending
&& leaderboardToValidate.getDiscardThresholds()[i - 1] < leaderboardToValidate.getDiscardThresholds()[i]
// and if one box is empty, all subsequent boxes need to be empty too
&& (leaderboardToValidate.getDiscardThresholds()[i] == 0 || leaderboardToValidate.getDiscardThresholds()[i-1] > 0);
}
}
boolean unique = true;
for (StrippedLeaderboardDTO dao : existingLeaderboards) {
if(dao.name.equals(leaderboardToValidate.getName())){
unique = false;
}
}
if (!nonEmpty) {
errorMessage = stringConstants.pleaseEnterAName();
} else if(!unique){
errorMessage = stringConstants.leaderboardWithThisNameAlreadyExists();
} else if (!discardThresholdsAscending) {
errorMessage = stringConstants.discardThresholdsMustBeAscending();
} else {
errorMessage = null;
}
return errorMessage;
}
}
public FlexibleLeaderboardDialog(String title, LeaderboardDescriptor leaderboardDTO, StringMessages stringMessages,
ErrorReporter errorReporter, LeaderboardParameterValidator validator, AsyncCallback<LeaderboardDescriptor> callback) {
super(title, leaderboardDTO, stringMessages, validator, callback);
}
@Override
protected LeaderboardDescriptor getResult() {
LeaderboardDescriptor leaderboard = super.getResult();
leaderboard.setRegattaName(null);
leaderboard.setScoringScheme(getSelectedScoringSchemeType(scoringSchemeListBox, stringMessages));
return leaderboard;
}
@Override
protected Widget getAdditionalWidget() {
VerticalPanel mainPanel = new VerticalPanel();
Grid formGrid = new Grid(3,2);
formGrid.setCellSpacing(3);
formGrid.setWidget(0, 0, new Label(stringMessages.name() + ":"));
formGrid.setWidget(0, 1, nameTextBox);
formGrid.setWidget(1, 0, new Label(stringMessages.scoringSystem() + ":"));
formGrid.setWidget(1, 1, scoringSchemeListBox);
mainPanel.add(formGrid);
mainPanel.add(new Label(stringMessages.discardRacesFromHowManyStartedRacesOn()));
HorizontalPanel hp = new HorizontalPanel();
hp.setSpacing(3);
for (int i = 0; i < discardThresholdBoxes.length; i++) {
hp.add(new Label("" + (i + 1) + "."));
hp.add(discardThresholdBoxes[i]);
}
mainPanel.add(hp);
return mainPanel;
}
}
package com.sap.sailing.gwt.ui.adminconsole;
import java.util.Collection;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.sap.sailing.gwt.ui.client.ErrorReporter;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.shared.StrippedLeaderboardDTO;
public abstract class FlexibleLeaderboardDialog extends AbstractLeaderboardDialog {
protected ListBox scoringSchemeListBox;
protected static class LeaderboardParameterValidator implements Validator<LeaderboardDescriptor> {
protected final StringMessages stringConstants;
protected final Collection<StrippedLeaderboardDTO> existingLeaderboards;
public LeaderboardParameterValidator(StringMessages stringConstants, Collection<StrippedLeaderboardDTO> existingLeaderboards){
super();
this.stringConstants = stringConstants;
this.existingLeaderboards = existingLeaderboards;
}
@Override
public String getErrorMessage(LeaderboardDescriptor leaderboardToValidate) {
String errorMessage;
boolean nonEmpty = leaderboardToValidate.getName() != null && leaderboardToValidate.getName().length() > 0;
boolean discardThresholdsAscending = true;
for (int i = 1; i < leaderboardToValidate.getDiscardThresholds().length; i++) {
// TODO what are correct values for discarding Thresholds?
if (0 < leaderboardToValidate.getDiscardThresholds().length){
discardThresholdsAscending = discardThresholdsAscending
&& leaderboardToValidate.getDiscardThresholds()[i - 1] < leaderboardToValidate.getDiscardThresholds()[i]
// and if one box is empty, all subsequent boxes need to be empty too
&& (leaderboardToValidate.getDiscardThresholds()[i] == 0 || leaderboardToValidate.getDiscardThresholds()[i-1] > 0);
}
}
boolean unique = true;
for (StrippedLeaderboardDTO dao : existingLeaderboards) {
if(dao.name.equals(leaderboardToValidate.getName())){
unique = false;
}
}
if (!nonEmpty) {
errorMessage = stringConstants.pleaseEnterAName();
} else if(!unique){
errorMessage = stringConstants.leaderboardWithThisNameAlreadyExists();
} else if (!discardThresholdsAscending) {
errorMessage = stringConstants.discardThresholdsMustBeAscending();
} else {
errorMessage = null;
}
return errorMessage;
}
}
public FlexibleLeaderboardDialog(String title, LeaderboardDescriptor leaderboardDTO, StringMessages stringMessages,
ErrorReporter errorReporter, LeaderboardParameterValidator validator, DialogCallback<LeaderboardDescriptor> callback) {
super(title, leaderboardDTO, stringMessages, validator, callback);
}
@Override
protected LeaderboardDescriptor getResult() {
LeaderboardDescriptor leaderboard = super.getResult();
leaderboard.setRegattaName(null);
leaderboard.setScoringScheme(getSelectedScoringSchemeType(scoringSchemeListBox, stringMessages));
return leaderboard;
}
@Override
protected Widget getAdditionalWidget() {
VerticalPanel mainPanel = new VerticalPanel();
Grid formGrid = new Grid(3,2);
formGrid.setCellSpacing(3);
formGrid.setWidget(0, 0, new Label(stringMessages.name() + ":"));
formGrid.setWidget(0, 1, nameTextBox);
formGrid.setWidget(1, 0, new Label(stringMessages.scoringSystem() + ":"));
formGrid.setWidget(1, 1, scoringSchemeListBox);
mainPanel.add(formGrid);
mainPanel.add(new Label(stringMessages.discardRacesFromHowManyStartedRacesOn()));
HorizontalPanel hp = new HorizontalPanel();
hp.setSpacing(3);
for (int i = 0; i < discardThresholdBoxes.length; i++) {
hp.add(new Label("" + (i + 1) + "."));
hp.add(discardThresholdBoxes[i]);
}
mainPanel.add(hp);
return mainPanel;
}
}
@@ -1,34 +1,33 @@
package com.sap.sailing.gwt.ui.adminconsole;
import java.util.Collection;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.sap.sailing.domain.common.ScoringSchemeType;
import com.sap.sailing.gwt.ui.client.ErrorReporter;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.leaderboard.ScoringSchemeTypeFormatter;
import com.sap.sailing.gwt.ui.shared.StrippedLeaderboardDTO;
public class FlexibleLeaderboardEditDialog extends FlexibleLeaderboardDialog {
public FlexibleLeaderboardEditDialog(Collection<StrippedLeaderboardDTO> otherExistingLeaderboards,
LeaderboardDescriptor leaderboard, StringMessages stringConstants, ErrorReporter errorReporter,
AsyncCallback<LeaderboardDescriptor> callback) {
super(stringConstants.editFlexibleLeaderboard(), leaderboard, stringConstants, errorReporter, new FlexibleLeaderboardDialog.LeaderboardParameterValidator(
stringConstants, otherExistingLeaderboards), callback);
nameTextBox = createTextBox(leaderboard.getName());
scoringSchemeListBox = createListBox(false);
int j = 0;
for (ScoringSchemeType scoringSchemeType: ScoringSchemeType.values()) {
scoringSchemeListBox.addItem(ScoringSchemeTypeFormatter.format(scoringSchemeType, stringConstants));
if(leaderboard.getScoringScheme() == scoringSchemeType) {
scoringSchemeListBox.setSelectedIndex(j);
}
j++;
}
scoringSchemeListBox.setEnabled(false);
discardThresholdBoxes = initPrefilledDiscardThresholdBoxes(leaderboard.getDiscardThresholds(), this);
}
}
package com.sap.sailing.gwt.ui.adminconsole;
import java.util.Collection;
import com.sap.sailing.domain.common.ScoringSchemeType;
import com.sap.sailing.gwt.ui.client.ErrorReporter;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.leaderboard.ScoringSchemeTypeFormatter;
import com.sap.sailing.gwt.ui.shared.StrippedLeaderboardDTO;
public class FlexibleLeaderboardEditDialog extends FlexibleLeaderboardDialog {
public FlexibleLeaderboardEditDialog(Collection<StrippedLeaderboardDTO> otherExistingLeaderboards,
LeaderboardDescriptor leaderboard, StringMessages stringConstants, ErrorReporter errorReporter,
DialogCallback<LeaderboardDescriptor> callback) {
super(stringConstants.editFlexibleLeaderboard(), leaderboard, stringConstants, errorReporter, new FlexibleLeaderboardDialog.LeaderboardParameterValidator(
stringConstants, otherExistingLeaderboards), callback);
nameTextBox = createTextBox(leaderboard.getName());
scoringSchemeListBox = createListBox(false);
int j = 0;
for (ScoringSchemeType scoringSchemeType: ScoringSchemeType.values()) {
scoringSchemeListBox.addItem(ScoringSchemeTypeFormatter.format(scoringSchemeType, stringConstants));
if(leaderboard.getScoringScheme() == scoringSchemeType) {
scoringSchemeListBox.setSelectedIndex(j);
}
j++;
}
scoringSchemeListBox.setEnabled(false);
discardThresholdBoxes = initPrefilledDiscardThresholdBoxes(leaderboard.getDiscardThresholds(), this);
}
}
@@ -14,6 +14,7 @@ import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.sap.sailing.gwt.ui.client.DataEntryDialog;
import com.sap.sailing.gwt.ui.client.DataEntryDialog.DialogCallback;
import com.sap.sailing.gwt.ui.client.ErrorReporter;
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
import com.sap.sailing.gwt.ui.client.StringMessages;
@@ -114,14 +115,14 @@ public class FregResultImportUrlPanel extends FlowPanel {
}
return result;
}
}, new AsyncCallback<String>() {
}, new DialogCallback<String>() {
@Override
public void onFailure(Throwable caught) {
public void cancel() {
// user cancelled; just don't add
}
@Override
public void onSuccess(String result) {
public void ok(String result) {
urlListBox.addItem(result);
sailingService.addFragUrl(result, new AsyncCallback<Void>() {
@Override
@@ -47,6 +47,7 @@ import com.sap.sailing.domain.common.RegattaIdentifier;
import com.sap.sailing.domain.common.RegattaName;
import com.sap.sailing.domain.common.RegattaNameAndRaceName;
import com.sap.sailing.domain.common.impl.Util.Pair;
import com.sap.sailing.gwt.ui.client.DataEntryDialog.DialogCallback;
import com.sap.sailing.gwt.ui.client.ErrorReporter;
import com.sap.sailing.gwt.ui.client.ParallelExecutionCallback;
import com.sap.sailing.gwt.ui.client.ParallelExecutionHolder;
@@ -248,13 +249,13 @@ public class LeaderboardConfigPanel extends FormPanel implements RegattaDisplaye
AbstractLeaderboardDialog dialog = new RegattaLeaderboardEditDialog(Collections
.unmodifiableCollection(otherExistingLeaderboard), Collections.unmodifiableCollection(allRegattas),
descriptor, stringMessages, errorReporter,
new AsyncCallback<LeaderboardDescriptor>() {
new DialogCallback<LeaderboardDescriptor>() {
@Override
public void onFailure(Throwable arg0) {
public void cancel() {
}
@Override
public void onSuccess(LeaderboardDescriptor result) {
public void ok(LeaderboardDescriptor result) {
updateLeaderboard(oldLeaderboardName, result);
}
});
@@ -264,13 +265,13 @@ public class LeaderboardConfigPanel extends FormPanel implements RegattaDisplaye
FlexibleLeaderboardEditDialog dialog = new FlexibleLeaderboardEditDialog(Collections
.unmodifiableCollection(otherExistingLeaderboard),
descriptor, stringMessages, errorReporter,
new AsyncCallback<LeaderboardDescriptor>() {
new DialogCallback<LeaderboardDescriptor>() {
@Override
public void onFailure(Throwable arg0) {
public void cancel() {
}
@Override
public void onSuccess(LeaderboardDescriptor result) {
public void ok(LeaderboardDescriptor result) {
updateLeaderboard(oldLeaderboardName, result);
}
});
@@ -693,13 +694,13 @@ public class LeaderboardConfigPanel extends FormPanel implements RegattaDisplaye
}
existingRacesWithoutThisRace.remove(raceColumnWithFleet.getA());
final RaceColumnInLeaderboardDialog raceDialog = new RaceColumnInLeaderboardDialog(existingRacesWithoutThisRace,
raceColumnWithFleet.getA(), stringMessages, new AsyncCallback<RaceColumnDTO>() {
raceColumnWithFleet.getA(), stringMessages, new DialogCallback<RaceColumnDTO>() {
@Override
public void onFailure(Throwable caught) {
public void cancel() {
}
@Override
public void onSuccess(final RaceColumnDTO result) {
public void ok(final RaceColumnDTO result) {
final ParallelExecutionCallback<Void> renameLeaderboardColumnCallback = new ParallelExecutionCallback<Void>();
final ParallelExecutionCallback<Void> updateIsMedalRaceCallback = new ParallelExecutionCallback<Void>();
new ParallelExecutionHolder(renameLeaderboardColumnCallback, updateIsMedalRaceCallback) {
@@ -748,13 +749,13 @@ public class LeaderboardConfigPanel extends FormPanel implements RegattaDisplaye
}
final RaceColumnsInLeaderboardDialog raceDialog = new RaceColumnsInLeaderboardDialog(existingRaceColumns,
stringMessages, new AsyncCallback<List<RaceColumnDTO>>() {
stringMessages, new DialogCallback<List<RaceColumnDTO>>() {
@Override
public void onFailure(Throwable caught) {
public void cancel() {
}
@Override
public void onSuccess(final List<RaceColumnDTO> result) {
public void ok(final List<RaceColumnDTO> result) {
updateRaceColumnsOfLeaderboard(leaderboardName, existingRaceColumns, result);
}
});
@@ -838,13 +839,13 @@ public class LeaderboardConfigPanel extends FormPanel implements RegattaDisplaye
private void createFlexibleLeaderboard() {
AbstractLeaderboardDialog dialog = new FlexibleLeaderboardCreateDialog(Collections.unmodifiableCollection(availableLeaderboardList),
stringMessages, errorReporter, new AsyncCallback<LeaderboardDescriptor>() {
stringMessages, errorReporter, new DialogCallback<LeaderboardDescriptor>() {
@Override
public void onFailure(Throwable arg0) {
public void cancel() {
}
@Override
public void onSuccess(final LeaderboardDescriptor newLeaderboard) {
public void ok(final LeaderboardDescriptor newLeaderboard) {
sailingService.createFlexibleLeaderboard(newLeaderboard.getName(), newLeaderboard.getDiscardThresholds(),
newLeaderboard.getScoringScheme(),
new AsyncCallback<StrippedLeaderboardDTO>() {
@@ -866,13 +867,13 @@ public class LeaderboardConfigPanel extends FormPanel implements RegattaDisplaye
private void createRegattaLeaderboard() {
RegattaLeaderboardCreateDialog dialog = new RegattaLeaderboardCreateDialog(Collections.unmodifiableCollection(availableLeaderboardList),
Collections.unmodifiableCollection(allRegattas), stringMessages, errorReporter, new AsyncCallback<LeaderboardDescriptor>() {
Collections.unmodifiableCollection(allRegattas), stringMessages, errorReporter, new DialogCallback<LeaderboardDescriptor>() {
@Override
public void onFailure(Throwable arg0) {
public void cancel() {
}
@Override
public void onSuccess(final LeaderboardDescriptor newLeaderboard) {
public void ok(final LeaderboardDescriptor newLeaderboard) {
RegattaIdentifier regattaIdentifier = new RegattaName(newLeaderboard.getRegattaName());
sailingService.createRegattaLeaderboard(regattaIdentifier, newLeaderboard.getDiscardThresholds(),
new AsyncCallback<StrippedLeaderboardDTO>() {
@@ -37,6 +37,7 @@ import com.google.gwt.view.client.SelectionChangeEvent;
import com.google.gwt.view.client.SingleSelectionModel;
import com.sap.sailing.gwt.ui.adminconsole.LeaderboardConfigPanel.AnchorCell;
import com.sap.sailing.gwt.ui.client.AbstractRegattaPanel;
import com.sap.sailing.gwt.ui.client.DataEntryDialog.DialogCallback;
import com.sap.sailing.gwt.ui.client.ErrorReporter;
import com.sap.sailing.gwt.ui.client.RegattaRefresher;
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
@@ -450,11 +451,11 @@ public class LeaderboardGroupConfigPanel extends AbstractRegattaPanel {
final String oldGroupName = group.name;
ArrayList<LeaderboardGroupDTO> otherExistingGroups = new ArrayList<LeaderboardGroupDTO>(availableLeaderboardGroups);
otherExistingGroups.remove(group);
LeaderboardGroupEditDialog dialog = new LeaderboardGroupEditDialog(group, otherExistingGroups, stringMessages, new AsyncCallback<LeaderboardGroupDTO>() {
LeaderboardGroupEditDialog dialog = new LeaderboardGroupEditDialog(group, otherExistingGroups, stringMessages, new DialogCallback<LeaderboardGroupDTO>() {
@Override
public void onFailure(Throwable t) {}
public void cancel() {}
@Override
public void onSuccess(LeaderboardGroupDTO group) {
public void ok(LeaderboardGroupDTO group) {
updateGroup(oldGroupName, group);
}
});
@@ -557,11 +558,11 @@ public class LeaderboardGroupConfigPanel extends AbstractRegattaPanel {
private void addNewGroup() {
LeaderboardGroupCreateDialog dialog = new LeaderboardGroupCreateDialog(
Collections.unmodifiableCollection(availableLeaderboardGroups), stringMessages,
new AsyncCallback<LeaderboardGroupDTO>() {
new DialogCallback<LeaderboardGroupDTO>() {
@Override
public void onFailure(Throwable t) {}
public void cancel() {}
@Override
public void onSuccess(LeaderboardGroupDTO newGroup) {
public void ok(LeaderboardGroupDTO newGroup) {
createNewGroup(newGroup);
}
});
@@ -2,14 +2,13 @@ package com.sap.sailing.gwt.ui.adminconsole;
import java.util.Collection;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.shared.LeaderboardGroupDTO;
public class LeaderboardGroupCreateDialog extends LeaderboardGroupDialog {
public LeaderboardGroupCreateDialog(Collection<LeaderboardGroupDTO> existingGroups,
StringMessages stringMessages, AsyncCallback<LeaderboardGroupDTO> callback) {
StringMessages stringMessages, DialogCallback<LeaderboardGroupDTO> callback) {
super(new LeaderboardGroupDTO(), stringMessages,
callback, existingGroups);
nameEntryField = createTextBox(null);
@@ -8,7 +8,6 @@ import java.util.List;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.HorizontalPanel;
@@ -94,7 +93,7 @@ public class LeaderboardGroupDialog extends DataEntryDialog<LeaderboardGroupDTO>
}
public LeaderboardGroupDialog(LeaderboardGroupDTO group, StringMessages stringMessages,
AsyncCallback<LeaderboardGroupDTO> callback, Collection<LeaderboardGroupDTO> existingLeaderboardGroups) {
DialogCallback<LeaderboardGroupDTO> callback, Collection<LeaderboardGroupDTO> existingLeaderboardGroups) {
super(stringMessages.leaderboardGroup(), null, stringMessages.ok(), stringMessages.cancel(),
new LeaderboardGroupParameterValidator(stringMessages, existingLeaderboardGroups), callback);
this.stringMessages = stringMessages;
@@ -2,14 +2,13 @@ package com.sap.sailing.gwt.ui.adminconsole;
import java.util.Collection;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Widget;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.shared.LeaderboardGroupDTO;
public class LeaderboardGroupEditDialog extends LeaderboardGroupDialog {
public LeaderboardGroupEditDialog(LeaderboardGroupDTO group, Collection<LeaderboardGroupDTO> otherExistingGroups,
StringMessages stringMessages, AsyncCallback<LeaderboardGroupDTO> callback) {
StringMessages stringMessages, DialogCallback<LeaderboardGroupDTO> callback) {
super(group, stringMessages, callback, otherExistingGroups);
// don't allow editing the overall leaderboard's scoring scheme if the group already uses an overall leaderboard
getOverallLeaderboardScoringSchemeListBox().setEnabled(!group.hasOverallLeaderboard());
@@ -1,105 +1,104 @@
package com.sap.sailing.gwt.ui.adminconsole;
import java.util.List;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.HasVerticalAlignment;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.sap.sailing.gwt.ui.client.DataEntryDialog;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.shared.RaceColumnDTO;
/**
* Fills a {@link RaceColumnDTO} object by setting its {@link RaceColumnDTO#setMedalRace(boolean) medal race} property and its name.
* @author Axel Uhl (D043530)
*
*/
public class RaceColumnInLeaderboardDialog extends DataEntryDialog<RaceColumnDTO> {
private final TextBox raceNameBox;
private final CheckBox isMedalRace;
private final StringMessages stringConstants;
private RaceColumnDTO raceColumnToEdit;
private static class RaceDialogValidator implements Validator<RaceColumnDTO> {
private StringMessages stringConstants;
private List<RaceColumnDTO> existingRaces;
public RaceDialogValidator(StringMessages stringConstants, List<RaceColumnDTO> existingRaceColumnsAndFleetNames) {
this.stringConstants = stringConstants;
this.existingRaces = existingRaceColumnsAndFleetNames;
}
@Override
public String getErrorMessage(RaceColumnDTO valueToValidate) {
String errorMessage;
String racename = valueToValidate.getRaceColumnName();
Boolean isMedalRace = valueToValidate.isMedalRace();
boolean isNameNotEmpty = racename != null & racename != "";
boolean medalRaceNotNull = isMedalRace != null;
boolean unique = true;
for (RaceColumnDTO raceColumn : existingRaces) {
if (raceColumn.getRaceColumnName().equals(valueToValidate.getRaceColumnName())) {
unique = false;
}
}
if (!isNameNotEmpty) {
errorMessage = stringConstants.pleaseEnterAName();
} else if (!medalRaceNotNull) {
errorMessage = stringConstants.medalRaceIsNull();
} else if (!unique) {
errorMessage = stringConstants.raceWithThisNameAlreadyExists();
} else {
return errorMessage = null;
}
return errorMessage;
}
}
public RaceColumnInLeaderboardDialog(List<RaceColumnDTO> existingRaces, RaceColumnDTO raceColumnToEdit,
StringMessages stringConstants, AsyncCallback<RaceColumnDTO> callback) {
super(stringConstants.actionRaceEdit(), null, stringConstants.ok(), stringConstants.cancel(),
new RaceDialogValidator(stringConstants, existingRaces), callback);
this.raceColumnToEdit = raceColumnToEdit;
this.stringConstants = stringConstants;
raceNameBox = createTextBox(raceColumnToEdit.getRaceColumnName());
isMedalRace = createCheckbox(stringConstants.medalRace());
isMedalRace.setValue(raceColumnToEdit.isMedalRace());
}
@Override
protected RaceColumnDTO getResult() {
raceColumnToEdit.name = raceNameBox.getValue();
raceColumnToEdit.setMedalRace(isMedalRace.getValue());
return raceColumnToEdit;
}
@Override
protected Widget getAdditionalWidget() {
VerticalPanel mainPanel = new VerticalPanel();
HorizontalPanel raceNamePanel = new HorizontalPanel();
raceNamePanel.setSpacing(3);
mainPanel.add(raceNamePanel);
raceNamePanel.add(new Label(stringConstants.name() + ":"));
raceNamePanel.add(raceNameBox);
alignAllPanelWidgetsVertically(raceNamePanel, HasVerticalAlignment.ALIGN_MIDDLE);
mainPanel.add(isMedalRace);
return mainPanel;
}
@Override
public void show() {
super.show();
raceNameBox.setFocus(true);
}
}
package com.sap.sailing.gwt.ui.adminconsole;
import java.util.List;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.HasVerticalAlignment;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.sap.sailing.gwt.ui.client.DataEntryDialog;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.shared.RaceColumnDTO;
/**
* Fills a {@link RaceColumnDTO} object by setting its {@link RaceColumnDTO#setMedalRace(boolean) medal race} property and its name.
* @author Axel Uhl (D043530)
*
*/
public class RaceColumnInLeaderboardDialog extends DataEntryDialog<RaceColumnDTO> {
private final TextBox raceNameBox;
private final CheckBox isMedalRace;
private final StringMessages stringConstants;
private RaceColumnDTO raceColumnToEdit;
private static class RaceDialogValidator implements Validator<RaceColumnDTO> {
private StringMessages stringConstants;
private List<RaceColumnDTO> existingRaces;
public RaceDialogValidator(StringMessages stringConstants, List<RaceColumnDTO> existingRaceColumnsAndFleetNames) {
this.stringConstants = stringConstants;
this.existingRaces = existingRaceColumnsAndFleetNames;
}
@Override
public String getErrorMessage(RaceColumnDTO valueToValidate) {
String errorMessage;
String racename = valueToValidate.getRaceColumnName();
Boolean isMedalRace = valueToValidate.isMedalRace();
boolean isNameNotEmpty = racename != null & racename != "";
boolean medalRaceNotNull = isMedalRace != null;
boolean unique = true;
for (RaceColumnDTO raceColumn : existingRaces) {
if (raceColumn.getRaceColumnName().equals(valueToValidate.getRaceColumnName())) {
unique = false;
}
}
if (!isNameNotEmpty) {
errorMessage = stringConstants.pleaseEnterAName();
} else if (!medalRaceNotNull) {
errorMessage = stringConstants.medalRaceIsNull();
} else if (!unique) {
errorMessage = stringConstants.raceWithThisNameAlreadyExists();
} else {
return errorMessage = null;
}
return errorMessage;
}
}
public RaceColumnInLeaderboardDialog(List<RaceColumnDTO> existingRaces, RaceColumnDTO raceColumnToEdit,
StringMessages stringConstants, DialogCallback<RaceColumnDTO> callback) {
super(stringConstants.actionRaceEdit(), null, stringConstants.ok(), stringConstants.cancel(),
new RaceDialogValidator(stringConstants, existingRaces), callback);
this.raceColumnToEdit = raceColumnToEdit;
this.stringConstants = stringConstants;
raceNameBox = createTextBox(raceColumnToEdit.getRaceColumnName());
isMedalRace = createCheckbox(stringConstants.medalRace());
isMedalRace.setValue(raceColumnToEdit.isMedalRace());
}
@Override
protected RaceColumnDTO getResult() {
raceColumnToEdit.name = raceNameBox.getValue();
raceColumnToEdit.setMedalRace(isMedalRace.getValue());
return raceColumnToEdit;
}
@Override
protected Widget getAdditionalWidget() {
VerticalPanel mainPanel = new VerticalPanel();
HorizontalPanel raceNamePanel = new HorizontalPanel();
raceNamePanel.setSpacing(3);
mainPanel.add(raceNamePanel);
raceNamePanel.add(new Label(stringConstants.name() + ":"));
raceNamePanel.add(raceNameBox);
alignAllPanelWidgetsVertically(raceNamePanel, HasVerticalAlignment.ALIGN_MIDDLE);
mainPanel.add(isMedalRace);
return mainPanel;
}
@Override
public void show() {
super.show();
raceNameBox.setFocus(true);
}
}
@@ -1,313 +1,331 @@
package com.sap.sailing.gwt.ui.adminconsole;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.sap.sailing.domain.common.impl.Util.Pair;
import com.sap.sailing.gwt.ui.client.DataEntryDialog;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.shared.RaceColumnDTO;
import com.sap.sailing.gwt.ui.shared.RegattaDTO;
import com.sap.sailing.gwt.ui.shared.SeriesDTO;
public class RaceColumnInRegattaSeriesDialog extends DataEntryDialog<Pair<SeriesDTO, List<RaceColumnDTO>>> {
private final RegattaDTO regatta;
private final ListBox seriesListBox;
private final ListBox addRacesListBox;
private Button addRacesBtn;
private final List<TextBox> raceNameEntryFields;
private final List<Button> raceNameDeleteButtons;
private final StringMessages stringMessages;
private final TextBox raceNamePrefixTextBox;
private final boolean hasOneSeries;
private Grid raceColumnsGrid;
private VerticalPanel additionalWidgetPanel;
private static class RaceDialogValidator implements Validator<Pair<SeriesDTO, List<RaceColumnDTO>>> {
private StringMessages stringConstants;
public RaceDialogValidator(RegattaDTO regatta, StringMessages stringConstants) {
this.stringConstants = stringConstants;
}
@Override
public String getErrorMessage(Pair<SeriesDTO, List<RaceColumnDTO>> valueToValidate) {
String errorMessage = null;
SeriesDTO series = valueToValidate.getA();
if(series == null) {
errorMessage = "You must select a series";
}
if(errorMessage == null) {
List<RaceColumnDTO> raceColumnsToValidate = valueToValidate.getB();
int index = 0;
boolean raceColumnNameNotEmpty = true;
for (RaceColumnDTO raceColumn : raceColumnsToValidate) {
raceColumnNameNotEmpty = raceColumn.name != null && raceColumn.name.length() > 0;
if (!raceColumnNameNotEmpty) {
break;
}
index++;
}
int index2 = 0;
boolean raceColumnUnique = true;
HashSet<String> setToFindDuplicates = new HashSet<String>();
for (RaceColumnDTO raceColumn : raceColumnsToValidate) {
if (!setToFindDuplicates.add(raceColumn.name)) {
raceColumnUnique = false;
break;
}
index2++;
}
if (!raceColumnNameNotEmpty) {
errorMessage = stringConstants.race() + " " + (index + 1) + ": "
+ stringConstants.pleaseEnterAName();
} else if (!raceColumnUnique) {
errorMessage = stringConstants.race() + " " + (index2 + 1) + ": "
+ stringConstants.raceWithThisNameAlreadyExists();
}
}
return errorMessage;
}
}
public RaceColumnInRegattaSeriesDialog(RegattaDTO regatta, StringMessages stringConstants,
AsyncCallback<Pair<SeriesDTO, List<RaceColumnDTO>>> callback) {
super(stringConstants.actionEditRaces(), null, stringConstants.ok(), stringConstants.cancel(),
new RaceDialogValidator(regatta, stringConstants), callback);
this.regatta = regatta;
this.stringMessages = stringConstants;
this.hasOneSeries = regatta.series.size() == 1;
seriesListBox = createListBox(false);
addRacesListBox = createListBox(false);
raceNamePrefixTextBox = createTextBox(null);
raceNameEntryFields = new ArrayList<TextBox>();
raceNameDeleteButtons = new ArrayList<Button>();
raceColumnsGrid = new Grid(0, 0);
}
private Widget createRaceNameWidget(String defaultName, boolean enabled) {
TextBox textBox = createTextBox(defaultName);
textBox.setVisibleLength(40);
textBox.setEnabled(enabled);
raceNameEntryFields.add(textBox);
return textBox;
}
private Widget createRaceNameDeleteButtonWidget() {
final Button raceNameDeleteBtn = new Button(stringMessages.delete());
raceNameDeleteBtn.addStyleName("inlineButton");
raceNameDeleteButtons.add(raceNameDeleteBtn);
raceNameDeleteBtn.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
int index = 0;
for(Button btn: raceNameDeleteButtons) {
if(raceNameDeleteBtn == btn) {
break;
}
index++;
}
raceNameEntryFields.remove(index);
raceNameDeleteButtons.remove(index);
updateRaceColumnsGrid(additionalWidgetPanel);
}
});
return raceNameDeleteBtn;
}
@Override
protected Pair<SeriesDTO, List<RaceColumnDTO>> getResult() {
SeriesDTO selectedSeries = getSelectedSeries();
List<RaceColumnDTO> races = new ArrayList<RaceColumnDTO>();
int racesCount = raceNameEntryFields.size();
for(int i = 0; i < racesCount; i++) {
String raceColumnName = raceNameEntryFields.get(i).getValue();
RaceColumnDTO raceColumnDTO = findRaceColumnInSeriesByName(selectedSeries, raceColumnName);
if(raceColumnDTO == null) {
raceColumnDTO = new RaceColumnDTO();
raceColumnDTO.name = raceColumnName;
}
races.add(raceColumnDTO);
}
return new Pair<SeriesDTO, List<RaceColumnDTO>>(selectedSeries, races);
}
private RaceColumnDTO findRaceColumnInSeriesByName(SeriesDTO series, String raceColumnName) {
RaceColumnDTO result = null;
if(series != null) {
for(RaceColumnDTO raceColumn: series.getRaceColumns()) {
if(raceColumn.name.equals(raceColumnName)) {
result = raceColumn;
break;
}
}
}
return result;
}
@Override
protected Widget getAdditionalWidget() {
additionalWidgetPanel = new VerticalPanel();
Widget additionalWidget = super.getAdditionalWidget();
if (additionalWidget != null) {
additionalWidgetPanel.add(additionalWidget);
}
HorizontalPanel seriesPanel = new HorizontalPanel();
seriesPanel.setSpacing(3);
seriesPanel.add(new Label(stringMessages.series() + ":"));
if(hasOneSeries) {
String seriesName = regatta.series.get(0).name;
seriesListBox.addItem(seriesName);
seriesListBox.setSelectedIndex(0);
if("Default".equals(seriesName)) {
raceNamePrefixTextBox.setText("R");
} else {
raceNamePrefixTextBox.setText(seriesName.substring(0, 1).toUpperCase());
}
} else {
seriesListBox.addItem("Please select a series");
for (SeriesDTO series : regatta.series) {
seriesListBox.addItem(series.name);
}
}
seriesListBox.addChangeHandler(new ChangeHandler() {
@Override
public void onChange(ChangeEvent event) {
SeriesDTO selectedSeries = getSelectedSeries();
if(selectedSeries == null) {
raceNamePrefixTextBox.setText("");
} else {
raceNamePrefixTextBox.setText(selectedSeries.name.substring(0, 1).toUpperCase());
}
fillExistingRacesOfSeries();
}
});
seriesPanel.add(seriesListBox);
additionalWidgetPanel.add(seriesPanel);
// add races controls
HorizontalPanel addRacesPanel = new HorizontalPanel();
addRacesPanel.setSpacing(3);
addRacesPanel.add(new Label(stringMessages.add()));
addRacesPanel.add(addRacesListBox);
for(int i = 1; i <= 10; i++) {
addRacesListBox.addItem("" + i);
}
addRacesListBox.setSelectedIndex(0);
raceNamePrefixTextBox.setWidth("20px");
addRacesPanel.add(raceNamePrefixTextBox);
addRacesBtn = new Button(stringMessages.add());
addRacesBtn.addStyleName("inlineButton");
addRacesBtn.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
SeriesDTO selectedSeries = getSelectedSeries();
if(selectedSeries != null) {
String racePrefix = raceNamePrefixTextBox.getText();
int racesCountToCreate = addRacesListBox.getSelectedIndex()+1;
int currentSize = raceNameEntryFields.size();
for(int i = 1; i <= racesCountToCreate; i++) {
String raceName = racePrefix;
if(racesCountToCreate != 1 || selectedSeries.getRaceColumns().size() > 0) {
raceName += (currentSize + i);
}
createRaceNameWidget(raceName, true);
createRaceNameDeleteButtonWidget();
}
updateRaceColumnsGrid(additionalWidgetPanel);
} else {
Window.alert("Please select a series first.");
}
}
});
addRacesPanel.add(addRacesBtn);
additionalWidgetPanel.add(addRacesPanel);
additionalWidgetPanel.add(createHeadlineLabel(stringMessages.races()));
additionalWidgetPanel.add(raceColumnsGrid);
if(hasOneSeries) {
fillExistingRacesOfSeries();
}
return additionalWidgetPanel;
}
private void fillExistingRacesOfSeries() {
SeriesDTO selectedSeries = getSelectedSeries();
raceNameEntryFields.clear();
raceNameDeleteButtons.clear();
if(selectedSeries != null && !selectedSeries.getRaceColumns().isEmpty()) {
for(RaceColumnDTO raceColumn: selectedSeries.getRaceColumns()) {
createRaceNameWidget(raceColumn.name, false);
createRaceNameDeleteButtonWidget();
}
}
updateRaceColumnsGrid(additionalWidgetPanel);
}
private void updateRaceColumnsGrid(VerticalPanel parentPanel) {
int widgetIndex = parentPanel.getWidgetIndex(raceColumnsGrid);
parentPanel.remove(raceColumnsGrid);
int raceNamesCount = raceNameEntryFields.size();
if(raceNamesCount > 0) {
raceColumnsGrid = new Grid(raceNamesCount + 1, 3);
raceColumnsGrid.setCellSpacing(4);
raceColumnsGrid.setHTML(0, 0, stringMessages.name());
for(int i = 0; i < raceNamesCount; i++) {
raceColumnsGrid.setWidget(i+1, 0, raceNameEntryFields.get(i));
raceColumnsGrid.setWidget(i+1, 1, raceNameDeleteButtons.get(i));
}
} else {
raceColumnsGrid = new Grid(0, 0);
}
parentPanel.insert(raceColumnsGrid, widgetIndex);
}
private SeriesDTO getSelectedSeries() {
SeriesDTO result = null;
int selIndex = seriesListBox.getSelectedIndex();
if(selIndex > 0 || hasOneSeries) { // the zero index represents the 'no selection' text
String itemText = seriesListBox.getItemText(selIndex);
for(SeriesDTO seriesDTO: regatta.series) {
if(seriesDTO.name.equals(itemText)) {
result = seriesDTO;
break;
}
}
}
return result;
}
@Override
public void show() {
super.show();
}
}
package com.sap.sailing.gwt.ui.adminconsole;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.sap.sailing.domain.common.impl.Util.Pair;
import com.sap.sailing.gwt.ui.client.DataEntryDialog;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.shared.RaceColumnDTO;
import com.sap.sailing.gwt.ui.shared.RegattaDTO;
import com.sap.sailing.gwt.ui.shared.SeriesDTO;
public class RaceColumnInRegattaSeriesDialog extends DataEntryDialog<Pair<SeriesDTO, List<RaceColumnDTO>>> {
private final RegattaDTO regatta;
private final ListBox seriesListBox;
private final ListBox addRacesListBox;
private Button addRacesBtn;
private final List<TextBox> raceNameEntryFields;
private final List<Button> raceNameDeleteButtons;
private final StringMessages stringMessages;
private final TextBox raceNamePrefixTextBox;
private final boolean hasOneSeries;
private Grid raceColumnsGrid;
private VerticalPanel additionalWidgetPanel;
private static class RaceDialogValidator implements Validator<Pair<SeriesDTO, List<RaceColumnDTO>>> {
private StringMessages stringConstants;
private RegattaDTO regatta;
public RaceDialogValidator(RegattaDTO regatta, StringMessages stringConstants) {
this.stringConstants = stringConstants;
this.regatta = regatta;
}
@Override
public String getErrorMessage(Pair<SeriesDTO, List<RaceColumnDTO>> valueToValidate) {
Set<String> raceColumnNamesOfOtherSeries = new HashSet<String>();
SeriesDTO seriesToValidate = valueToValidate.getA();
List<RaceColumnDTO> raceColumnsToValidate = valueToValidate.getB();
String errorMessage = null;
if(seriesToValidate == null) {
errorMessage = stringConstants.pleaseSelectASeries();
} else {
for (SeriesDTO seriesDTO: regatta.series) {
if(!seriesDTO.name.equals(seriesToValidate.name)) {
for(RaceColumnDTO raceColumn: seriesDTO.getRaceColumns()) {
raceColumnNamesOfOtherSeries.add(raceColumn.name);
}
}
}
boolean raceColumnNameNotEmpty = true;
RaceColumnDTO wrongRaceColumn = null;
for (RaceColumnDTO raceColumn : raceColumnsToValidate) {
raceColumnNameNotEmpty = raceColumn.name != null && raceColumn.name.length() > 0;
if (!raceColumnNameNotEmpty) {
wrongRaceColumn = raceColumn;
break;
}
}
boolean raceColumnUniqueInSeries = true;
boolean raceColumnUniqueInRegatta = true;
HashSet<String> setToFindDuplicates = new HashSet<String>();
for (RaceColumnDTO raceColumn : raceColumnsToValidate) {
if (!setToFindDuplicates.add(raceColumn.name)) {
raceColumnUniqueInSeries = false;
wrongRaceColumn = raceColumn;
break;
} else if(raceColumnNamesOfOtherSeries.contains(raceColumn.name)) {
raceColumnUniqueInRegatta = false;
wrongRaceColumn = raceColumn;
break;
}
}
if (!raceColumnNameNotEmpty) {
errorMessage = stringConstants.race() + " " + wrongRaceColumn.name + ": "
+ stringConstants.pleaseEnterAName();
} else if (!raceColumnUniqueInSeries) {
errorMessage = stringConstants.race() + " " + wrongRaceColumn.name + ": "
+ stringConstants.raceWithThisNameAlreadyExists();
} else if (!raceColumnUniqueInRegatta) {
errorMessage = stringConstants.race() + " " + wrongRaceColumn.name + ": "
+ stringConstants.raceWithThisNameAlreadyExistsInRegatta();
}
}
return errorMessage;
}
}
public RaceColumnInRegattaSeriesDialog(RegattaDTO regatta, StringMessages stringConstants,
DialogCallback<Pair<SeriesDTO, List<RaceColumnDTO>>> callback) {
super(stringConstants.actionEditRaces(), null, stringConstants.ok(), stringConstants.cancel(),
new RaceDialogValidator(regatta, stringConstants), callback);
this.regatta = regatta;
this.stringMessages = stringConstants;
this.hasOneSeries = regatta.series.size() == 1;
seriesListBox = createListBox(false);
addRacesListBox = createListBox(false);
raceNamePrefixTextBox = createTextBox(null);
raceNameEntryFields = new ArrayList<TextBox>();
raceNameDeleteButtons = new ArrayList<Button>();
raceColumnsGrid = new Grid(0, 0);
}
private Widget createRaceNameWidget(String defaultName, boolean enabled) {
TextBox textBox = createTextBox(defaultName);
textBox.setVisibleLength(40);
textBox.setEnabled(enabled);
raceNameEntryFields.add(textBox);
return textBox;
}
private Widget createRaceNameDeleteButtonWidget() {
final Button raceNameDeleteBtn = new Button(stringMessages.delete());
raceNameDeleteBtn.addStyleName("inlineButton");
raceNameDeleteButtons.add(raceNameDeleteBtn);
raceNameDeleteBtn.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
int index = 0;
for(Button btn: raceNameDeleteButtons) {
if(raceNameDeleteBtn == btn) {
break;
}
index++;
}
raceNameEntryFields.remove(index);
raceNameDeleteButtons.remove(index);
updateRaceColumnsGrid(additionalWidgetPanel);
validate();
}
});
return raceNameDeleteBtn;
}
@Override
protected Pair<SeriesDTO, List<RaceColumnDTO>> getResult() {
SeriesDTO selectedSeries = getSelectedSeries();
List<RaceColumnDTO> races = new ArrayList<RaceColumnDTO>();
int racesCount = raceNameEntryFields.size();
for(int i = 0; i < racesCount; i++) {
String raceColumnName = raceNameEntryFields.get(i).getValue();
RaceColumnDTO raceColumnDTO = findRaceColumnInSeriesByName(selectedSeries, raceColumnName);
if(raceColumnDTO == null) {
raceColumnDTO = new RaceColumnDTO(/* isValidInTotalScore not relevant here; not in scope of a leaderboard */ null);
raceColumnDTO.name = raceColumnName;
}
races.add(raceColumnDTO);
}
return new Pair<SeriesDTO, List<RaceColumnDTO>>(selectedSeries, races);
}
private RaceColumnDTO findRaceColumnInSeriesByName(SeriesDTO series, String raceColumnName) {
RaceColumnDTO result = null;
if(series != null) {
for(RaceColumnDTO raceColumn: series.getRaceColumns()) {
if(raceColumn.name.equals(raceColumnName)) {
result = raceColumn;
break;
}
}
}
return result;
}
@Override
protected Widget getAdditionalWidget() {
additionalWidgetPanel = new VerticalPanel();
Widget additionalWidget = super.getAdditionalWidget();
if (additionalWidget != null) {
additionalWidgetPanel.add(additionalWidget);
}
HorizontalPanel seriesPanel = new HorizontalPanel();
seriesPanel.setSpacing(3);
seriesPanel.add(new Label(stringMessages.series() + ":"));
if(hasOneSeries) {
String seriesName = regatta.series.get(0).name;
seriesListBox.addItem(seriesName);
seriesListBox.setSelectedIndex(0);
if("Default".equals(seriesName)) {
raceNamePrefixTextBox.setText("R");
} else {
raceNamePrefixTextBox.setText(seriesName.substring(0, 1).toUpperCase());
}
} else {
seriesListBox.addItem("Please select a series");
for (SeriesDTO series : regatta.series) {
seriesListBox.addItem(series.name);
}
}
seriesListBox.addChangeHandler(new ChangeHandler() {
@Override
public void onChange(ChangeEvent event) {
SeriesDTO selectedSeries = getSelectedSeries();
if(selectedSeries == null) {
raceNamePrefixTextBox.setText("");
} else {
raceNamePrefixTextBox.setText(selectedSeries.name.substring(0, 1).toUpperCase());
}
fillExistingRacesOfSeries();
}
});
seriesPanel.add(seriesListBox);
additionalWidgetPanel.add(seriesPanel);
// add races controls
HorizontalPanel addRacesPanel = new HorizontalPanel();
addRacesPanel.setSpacing(3);
addRacesPanel.add(new Label(stringMessages.add()));
addRacesPanel.add(addRacesListBox);
for(int i = 1; i <= 10; i++) {
addRacesListBox.addItem("" + i);
}
addRacesListBox.setSelectedIndex(0);
raceNamePrefixTextBox.setWidth("20px");
addRacesPanel.add(raceNamePrefixTextBox);
addRacesBtn = new Button(stringMessages.add());
addRacesBtn.addStyleName("inlineButton");
addRacesBtn.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
SeriesDTO selectedSeries = getSelectedSeries();
if(selectedSeries != null) {
String racePrefix = raceNamePrefixTextBox.getText();
int racesCountToCreate = addRacesListBox.getSelectedIndex()+1;
int currentSize = raceNameEntryFields.size();
for(int i = 1; i <= racesCountToCreate; i++) {
String raceName = racePrefix;
if(racesCountToCreate != 1 || selectedSeries.getRaceColumns().size() > 0) {
raceName += (currentSize + i);
}
createRaceNameWidget(raceName, true);
createRaceNameDeleteButtonWidget();
}
updateRaceColumnsGrid(additionalWidgetPanel);
validate();
} else {
Window.alert("Please select a series first.");
}
}
});
addRacesPanel.add(addRacesBtn);
additionalWidgetPanel.add(addRacesPanel);
additionalWidgetPanel.add(createHeadlineLabel(stringMessages.races()));
additionalWidgetPanel.add(raceColumnsGrid);
if(hasOneSeries) {
fillExistingRacesOfSeries();
}
return additionalWidgetPanel;
}
private void fillExistingRacesOfSeries() {
SeriesDTO selectedSeries = getSelectedSeries();
raceNameEntryFields.clear();
raceNameDeleteButtons.clear();
if(selectedSeries != null && !selectedSeries.getRaceColumns().isEmpty()) {
for(RaceColumnDTO raceColumn: selectedSeries.getRaceColumns()) {
createRaceNameWidget(raceColumn.name, false);
createRaceNameDeleteButtonWidget();
}
}
updateRaceColumnsGrid(additionalWidgetPanel);
}
private void updateRaceColumnsGrid(VerticalPanel parentPanel) {
int widgetIndex = parentPanel.getWidgetIndex(raceColumnsGrid);
parentPanel.remove(raceColumnsGrid);
int raceNamesCount = raceNameEntryFields.size();
if(raceNamesCount > 0) {
raceColumnsGrid = new Grid(raceNamesCount + 1, 3);
raceColumnsGrid.setCellSpacing(4);
raceColumnsGrid.setHTML(0, 0, stringMessages.name());
for(int i = 0; i < raceNamesCount; i++) {
raceColumnsGrid.setWidget(i+1, 0, raceNameEntryFields.get(i));
raceColumnsGrid.setWidget(i+1, 1, raceNameDeleteButtons.get(i));
}
} else {
raceColumnsGrid = new Grid(0, 0);
}
parentPanel.insert(raceColumnsGrid, widgetIndex);
}
private SeriesDTO getSelectedSeries() {
SeriesDTO result = null;
int selIndex = seriesListBox.getSelectedIndex();
if(selIndex > 0 || hasOneSeries) { // the zero index represents the 'no selection' text
String itemText = seriesListBox.getItemText(selIndex);
for(SeriesDTO seriesDTO: regatta.series) {
if(seriesDTO.name.equals(itemText)) {
result = seriesDTO;
break;
}
}
}
return result;
}
@Override
public void show() {
super.show();
}
}
@@ -1,207 +1,206 @@
package com.sap.sailing.gwt.ui.adminconsole;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.HasVerticalAlignment;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.sap.sailing.gwt.ui.client.DataEntryDialog;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.shared.RaceColumnDTO;
public class RaceColumnsInLeaderboardDialog extends DataEntryDialog<List<RaceColumnDTO>> {
private final ListBox addRacesListBox;
private Button addRacesBtn;
private final List<TextBox> raceNameEntryFields;
private final List<CheckBox> isMedalRaceCheckboxes;
private final StringMessages stringMessages;
private final TextBox raceNamePrefixTextBox;
private Grid raceColumnsGrid;
private VerticalPanel additionalWidget;
private List<RaceColumnDTO> existingRaces;
private static class RaceDialogValidator implements Validator<List<RaceColumnDTO>> {
private StringMessages stringConstants;
public RaceDialogValidator(StringMessages stringConstants) {
this.stringConstants = stringConstants;
}
@Override
public String getErrorMessage(List<RaceColumnDTO> raceColumnsWithFleetToValidate) {
String errorMessage = null;
List<RaceColumnDTO> raceColumnsToValidate = new ArrayList<RaceColumnDTO>();
for (RaceColumnDTO raceColumnDTO : raceColumnsToValidate) {
raceColumnsToValidate.add(raceColumnDTO);
}
if(errorMessage == null) {
int index = 0;
boolean raceColumnNameNotEmpty = true;
for (RaceColumnDTO raceColumn : raceColumnsToValidate) {
raceColumnNameNotEmpty = raceColumn.name != null && raceColumn.name.length() > 0;
if (!raceColumnNameNotEmpty) {
break;
}
index++;
}
int index2 = 0;
boolean raceColumnUnique = true;
HashSet<String> setToFindDuplicates = new HashSet<String>();
for (RaceColumnDTO raceColumn : raceColumnsToValidate) {
if (!setToFindDuplicates.add(raceColumn.name)) {
raceColumnUnique = false;
break;
}
index2++;
}
if (!raceColumnNameNotEmpty) {
errorMessage = stringConstants.race() + " " + (index + 1) + ": "
+ stringConstants.pleaseEnterAName();
} else if (!raceColumnUnique) {
errorMessage = stringConstants.race() + " " + (index2 + 1) + ": "
+ stringConstants.raceWithThisNameAlreadyExists();
}
}
return errorMessage;
}
}
public RaceColumnsInLeaderboardDialog(List<RaceColumnDTO> existingRaces, StringMessages stringConstants,
AsyncCallback<List<RaceColumnDTO>> callback) {
super(stringConstants.actionAddRaces(), null, stringConstants.ok(), stringConstants.cancel(),
new RaceDialogValidator(stringConstants), callback);
this.existingRaces = existingRaces;
this.stringMessages = stringConstants;
addRacesListBox = createListBox(false);
raceNamePrefixTextBox = createTextBox(null);
raceNameEntryFields = new ArrayList<TextBox>();
isMedalRaceCheckboxes = new ArrayList<CheckBox>();
raceColumnsGrid = new Grid(0, 0);
}
private Widget createRaceNameWidget(String defaultName, boolean enabled) {
TextBox textBox = createTextBox(defaultName);
textBox.setVisibleLength(25);
textBox.setEnabled(enabled);
raceNameEntryFields.add(textBox);
return textBox;
}
private Widget createMedalRaceWidget(boolean isMedalRace, boolean enabled) {
CheckBox checkBox = createCheckbox(stringMessages.medalRace());
checkBox.setEnabled(enabled);
isMedalRaceCheckboxes.add(checkBox);
return checkBox;
}
@Override
protected List<RaceColumnDTO> getResult() {
List<RaceColumnDTO> racesWithFleet = new ArrayList<RaceColumnDTO>();
int racesCount = raceNameEntryFields.size();
for(int i = 0; i < racesCount; i++) {
String raceColumnName = raceNameEntryFields.get(i).getValue();
RaceColumnDTO raceColumnDTO = new RaceColumnDTO();
raceColumnDTO.name = raceColumnName;
raceColumnDTO.setMedalRace(false);
racesWithFleet.add(raceColumnDTO);
}
return racesWithFleet;
}
@Override
protected Widget getAdditionalWidget() {
additionalWidget = new VerticalPanel();
raceNamePrefixTextBox.setText("R");
// add races controls
HorizontalPanel addRacesPanel = new HorizontalPanel();
addRacesPanel.setSpacing(5);
addRacesPanel.add(new Label(stringMessages.add()));
addRacesPanel.add(addRacesListBox);
for(int i = 1; i <= 20; i++) {
addRacesListBox.addItem("" + i);
}
addRacesListBox.setSelectedIndex(0);
addRacesPanel.add(new Label(stringMessages.racesWithNamePrefix()+" "));
raceNamePrefixTextBox.setWidth("20px");
addRacesPanel.add(raceNamePrefixTextBox);
addRacesBtn = new Button(stringMessages.add());
addRacesBtn.addStyleName("inlineButton");
addRacesBtn.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
String racePrefix = raceNamePrefixTextBox.getText();
int racesCountToCreate = addRacesListBox.getSelectedIndex()+1;
int currentSize = raceNameEntryFields.size();
for(int i = 1; i <= racesCountToCreate; i++) {
String raceName = racePrefix;
if(racesCountToCreate != 1 || existingRaces.size() > 0) {
raceName += (currentSize + i);
}
createRaceNameWidget(raceName, true);
createMedalRaceWidget(false, true);
}
updateRaceColumnsGrid(additionalWidget);
}
});
addRacesPanel.add(addRacesBtn);
additionalWidget.add(addRacesPanel);
alignAllPanelWidgetsVertically(addRacesPanel, HasVerticalAlignment.ALIGN_MIDDLE);
additionalWidget.add(createHeadlineLabel(stringMessages.races()));
additionalWidget.add(raceColumnsGrid);
for(RaceColumnDTO raceColumn: existingRaces) {
createRaceNameWidget(raceColumn.getRaceColumnName(), false);
createMedalRaceWidget(raceColumn.isMedalRace(), false);
}
updateRaceColumnsGrid(additionalWidget);
return additionalWidget;
}
private void updateRaceColumnsGrid(VerticalPanel parentPanel) {
int widgetIndex = parentPanel.getWidgetIndex(raceColumnsGrid);
parentPanel.remove(raceColumnsGrid);
int raceNamesCount = raceNameEntryFields.size();
if(raceNamesCount > 0) {
raceColumnsGrid = new Grid(raceNamesCount, 3);
raceColumnsGrid.setCellSpacing(4);
for(int i = 0; i < raceNamesCount; i++) {
raceColumnsGrid.setWidget(i, 0, raceNameEntryFields.get(i));
raceColumnsGrid.setWidget(i, 1, isMedalRaceCheckboxes.get(i));
}
} else {
raceColumnsGrid = new Grid(0, 0);
}
parentPanel.insert(raceColumnsGrid, widgetIndex);
}
@Override
public void show() {
super.show();
}
}
package com.sap.sailing.gwt.ui.adminconsole;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.HasVerticalAlignment;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.sap.sailing.gwt.ui.client.DataEntryDialog;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.shared.RaceColumnDTO;
public class RaceColumnsInLeaderboardDialog extends DataEntryDialog<List<RaceColumnDTO>> {
private final ListBox addRacesListBox;
private Button addRacesBtn;
private final List<TextBox> raceNameEntryFields;
private final List<CheckBox> isMedalRaceCheckboxes;
private final StringMessages stringMessages;
private final TextBox raceNamePrefixTextBox;
private Grid raceColumnsGrid;
private VerticalPanel additionalWidget;
private List<RaceColumnDTO> existingRaces;
private static class RaceDialogValidator implements Validator<List<RaceColumnDTO>> {
private StringMessages stringConstants;
public RaceDialogValidator(StringMessages stringConstants) {
this.stringConstants = stringConstants;
}
@Override
public String getErrorMessage(List<RaceColumnDTO> raceColumnsWithFleetToValidate) {
String errorMessage = null;
List<RaceColumnDTO> raceColumnsToValidate = new ArrayList<RaceColumnDTO>();
for (RaceColumnDTO raceColumnDTO : raceColumnsToValidate) {
raceColumnsToValidate.add(raceColumnDTO);
}
if(errorMessage == null) {
int index = 0;
boolean raceColumnNameNotEmpty = true;
for (RaceColumnDTO raceColumn : raceColumnsToValidate) {
raceColumnNameNotEmpty = raceColumn.name != null && raceColumn.name.length() > 0;
if (!raceColumnNameNotEmpty) {
break;
}
index++;
}
int index2 = 0;
boolean raceColumnUnique = true;
HashSet<String> setToFindDuplicates = new HashSet<String>();
for (RaceColumnDTO raceColumn : raceColumnsToValidate) {
if (!setToFindDuplicates.add(raceColumn.name)) {
raceColumnUnique = false;
break;
}
index2++;
}
if (!raceColumnNameNotEmpty) {
errorMessage = stringConstants.race() + " " + (index + 1) + ": "
+ stringConstants.pleaseEnterAName();
} else if (!raceColumnUnique) {
errorMessage = stringConstants.race() + " " + (index2 + 1) + ": "
+ stringConstants.raceWithThisNameAlreadyExists();
}
}
return errorMessage;
}
}
public RaceColumnsInLeaderboardDialog(List<RaceColumnDTO> existingRaces, StringMessages stringConstants,
DialogCallback<List<RaceColumnDTO>> callback) {
super(stringConstants.actionAddRaces(), null, stringConstants.ok(), stringConstants.cancel(),
new RaceDialogValidator(stringConstants), callback);
this.existingRaces = existingRaces;
this.stringMessages = stringConstants;
addRacesListBox = createListBox(false);
raceNamePrefixTextBox = createTextBox(null);
raceNameEntryFields = new ArrayList<TextBox>();
isMedalRaceCheckboxes = new ArrayList<CheckBox>();
raceColumnsGrid = new Grid(0, 0);
}
private Widget createRaceNameWidget(String defaultName, boolean enabled) {
TextBox textBox = createTextBox(defaultName);
textBox.setVisibleLength(25);
textBox.setEnabled(enabled);
raceNameEntryFields.add(textBox);
return textBox;
}
private Widget createMedalRaceWidget(boolean isMedalRace, boolean enabled) {
CheckBox checkBox = createCheckbox(stringMessages.medalRace());
checkBox.setEnabled(enabled);
isMedalRaceCheckboxes.add(checkBox);
return checkBox;
}
@Override
protected List<RaceColumnDTO> getResult() {
List<RaceColumnDTO> racesWithFleet = new ArrayList<RaceColumnDTO>();
int racesCount = raceNameEntryFields.size();
for(int i = 0; i < racesCount; i++) {
String raceColumnName = raceNameEntryFields.get(i).getValue();
RaceColumnDTO raceColumnDTO = new RaceColumnDTO(/* isValidInTotalScore not relevant here because no scores attached */ null);
raceColumnDTO.name = raceColumnName;
raceColumnDTO.setMedalRace(false);
racesWithFleet.add(raceColumnDTO);
}
return racesWithFleet;
}
@Override
protected Widget getAdditionalWidget() {
additionalWidget = new VerticalPanel();
raceNamePrefixTextBox.setText("R");
// add races controls
HorizontalPanel addRacesPanel = new HorizontalPanel();
addRacesPanel.setSpacing(5);
addRacesPanel.add(new Label(stringMessages.add()));
addRacesPanel.add(addRacesListBox);
for(int i = 1; i <= 20; i++) {
addRacesListBox.addItem("" + i);
}
addRacesListBox.setSelectedIndex(0);
addRacesPanel.add(new Label(stringMessages.racesWithNamePrefix()+" "));
raceNamePrefixTextBox.setWidth("20px");
addRacesPanel.add(raceNamePrefixTextBox);
addRacesBtn = new Button(stringMessages.add());
addRacesBtn.addStyleName("inlineButton");
addRacesBtn.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
String racePrefix = raceNamePrefixTextBox.getText();
int racesCountToCreate = addRacesListBox.getSelectedIndex()+1;
int currentSize = raceNameEntryFields.size();
for(int i = 1; i <= racesCountToCreate; i++) {
String raceName = racePrefix;
if(racesCountToCreate != 1 || existingRaces.size() > 0) {
raceName += (currentSize + i);
}
createRaceNameWidget(raceName, true);
createMedalRaceWidget(false, true);
}
updateRaceColumnsGrid(additionalWidget);
}
});
addRacesPanel.add(addRacesBtn);
additionalWidget.add(addRacesPanel);
alignAllPanelWidgetsVertically(addRacesPanel, HasVerticalAlignment.ALIGN_MIDDLE);
additionalWidget.add(createHeadlineLabel(stringMessages.races()));
additionalWidget.add(raceColumnsGrid);
for(RaceColumnDTO raceColumn: existingRaces) {
createRaceNameWidget(raceColumn.getRaceColumnName(), false);
createMedalRaceWidget(raceColumn.isMedalRace(), false);
}
updateRaceColumnsGrid(additionalWidget);
return additionalWidget;
}
private void updateRaceColumnsGrid(VerticalPanel parentPanel) {
int widgetIndex = parentPanel.getWidgetIndex(raceColumnsGrid);
parentPanel.remove(raceColumnsGrid);
int raceNamesCount = raceNameEntryFields.size();
if(raceNamesCount > 0) {
raceColumnsGrid = new Grid(raceNamesCount, 3);
raceColumnsGrid.setCellSpacing(4);
for(int i = 0; i < raceNamesCount; i++) {
raceColumnsGrid.setWidget(i, 0, raceNameEntryFields.get(i));
raceColumnsGrid.setWidget(i, 1, isMedalRaceCheckboxes.get(i));
}
} else {
raceColumnsGrid = new Grid(0, 0);
}
parentPanel.insert(raceColumnsGrid, widgetIndex);
}
@Override
public void show() {
super.show();
}
}
@@ -1,27 +1,26 @@
package com.sap.sailing.gwt.ui.adminconsole;
import java.util.Collection;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.sap.sailing.gwt.ui.client.ErrorReporter;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.shared.RegattaDTO;
import com.sap.sailing.gwt.ui.shared.StrippedLeaderboardDTO;
public class RegattaLeaderboardCreateDialog extends RegattaLeaderboardDialog {
public RegattaLeaderboardCreateDialog(Collection<StrippedLeaderboardDTO> existingLeaderboards, Collection<RegattaDTO> existingRegattas, StringMessages stringConstants,
ErrorReporter errorReporter, AsyncCallback<LeaderboardDescriptor> callback) {
super(stringConstants.createRegattaLeaderboard(), new LeaderboardDescriptor(), existingRegattas, stringConstants, errorReporter, new RegattaLeaderboardDialog.LeaderboardParameterValidator(stringConstants, existingLeaderboards), callback);
nameTextBox = createTextBox(null);
regattaListBox = createListBox(false);
regattaListBox.addItem(stringConstants.pleaseSelectARegatta());
for (RegattaDTO regatta : existingRegattas) {
regattaListBox.addItem(regatta.name);
}
discardThresholdBoxes = initEmptyDiscardThresholdBoxes(this);
}
}
package com.sap.sailing.gwt.ui.adminconsole;
import java.util.Collection;
import com.sap.sailing.gwt.ui.client.ErrorReporter;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.shared.RegattaDTO;
import com.sap.sailing.gwt.ui.shared.StrippedLeaderboardDTO;
public class RegattaLeaderboardCreateDialog extends RegattaLeaderboardDialog {
public RegattaLeaderboardCreateDialog(Collection<StrippedLeaderboardDTO> existingLeaderboards, Collection<RegattaDTO> existingRegattas, StringMessages stringConstants,
ErrorReporter errorReporter, DialogCallback<LeaderboardDescriptor> callback) {
super(stringConstants.createRegattaLeaderboard(), new LeaderboardDescriptor(), existingRegattas, stringConstants, errorReporter, new RegattaLeaderboardDialog.LeaderboardParameterValidator(stringConstants, existingLeaderboards), callback);
nameTextBox = createTextBox(null);
regattaListBox = createListBox(false);
regattaListBox.addItem(stringConstants.pleaseSelectARegatta());
for (RegattaDTO regatta : existingRegattas) {
regattaListBox.addItem(regatta.name);
}
discardThresholdBoxes = initEmptyDiscardThresholdBoxes(this);
}
}
@@ -1,124 +1,123 @@
package com.sap.sailing.gwt.ui.adminconsole;
import java.util.Collection;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.sap.sailing.gwt.ui.client.ErrorReporter;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.shared.RegattaDTO;
import com.sap.sailing.gwt.ui.shared.StrippedLeaderboardDTO;
public abstract class RegattaLeaderboardDialog extends AbstractLeaderboardDialog {
protected ListBox regattaListBox;
protected Collection<RegattaDTO> existingRegattas;
protected static class LeaderboardParameterValidator implements Validator<LeaderboardDescriptor> {
protected final StringMessages stringConstants;
protected final Collection<StrippedLeaderboardDTO> existingLeaderboards;
public LeaderboardParameterValidator(StringMessages stringConstants, Collection<StrippedLeaderboardDTO> existingLeaderboards){
super();
this.stringConstants = stringConstants;
this.existingLeaderboards = existingLeaderboards;
}
@Override
public String getErrorMessage(LeaderboardDescriptor leaderboardToValidate) {
String errorMessage;
boolean nonEmpty = leaderboardToValidate.getName() != null && leaderboardToValidate.getName().length() > 0;
boolean discardThresholdsAscending = true;
for (int i = 1; i < leaderboardToValidate.getDiscardThresholds().length; i++) {
// TODO what are correct values for discarding Thresholds?
if (0 < leaderboardToValidate.getDiscardThresholds().length){
discardThresholdsAscending = discardThresholdsAscending
&& leaderboardToValidate.getDiscardThresholds()[i - 1] < leaderboardToValidate.getDiscardThresholds()[i]
// and if one box is empty, all subsequent boxes need to be empty too
&& (leaderboardToValidate.getDiscardThresholds()[i] == 0 || leaderboardToValidate.getDiscardThresholds()[i-1] > 0);
}
}
boolean unique = true;
for (StrippedLeaderboardDTO dao : existingLeaderboards) {
if(dao.name.equals(leaderboardToValidate.getName())){
unique = false;
}
}
boolean regattaSelected = leaderboardToValidate.getRegattaName() != null ? true : false;
if (!nonEmpty) {
errorMessage = stringConstants.pleaseEnterAName();
} else if(!regattaSelected){
errorMessage = stringConstants.pleaseSelectARegatta();
} else if(!unique){
errorMessage = stringConstants.leaderboardWithThisNameAlreadyExists();
} else if (!discardThresholdsAscending) {
errorMessage = stringConstants.discardThresholdsMustBeAscending();
} else {
errorMessage = null;
}
return errorMessage;
}
}
public RegattaLeaderboardDialog(String title, LeaderboardDescriptor leaderboardDTO, Collection<RegattaDTO> existingRegattas, StringMessages stringConstants,
ErrorReporter errorReporter, LeaderboardParameterValidator validator, AsyncCallback<LeaderboardDescriptor> callback) {
super(title, leaderboardDTO, stringConstants, validator, callback);
this.existingRegattas = existingRegattas;
}
@Override
protected LeaderboardDescriptor getResult() {
LeaderboardDescriptor leaderboard = super.getResult();
leaderboard.setRegattaName(getSelectedRegatta() != null ? getSelectedRegatta().name : null);
return leaderboard;
}
@Override
protected Widget getAdditionalWidget() {
VerticalPanel mainPanel = new VerticalPanel();
Grid formGrid = new Grid(3,2);
formGrid.setCellSpacing(3);
formGrid.setWidget(0, 0, new Label(stringMessages.name() + ":"));
formGrid.setWidget(0, 1, nameTextBox);
formGrid.setWidget(1, 0, new Label(stringMessages.regatta() + ":"));
formGrid.setWidget(1, 1, regattaListBox);
mainPanel.add(formGrid);
mainPanel.add(new Label(stringMessages.discardRacesFromHowManyStartedRacesOn()));
HorizontalPanel hp = new HorizontalPanel();
hp.setSpacing(3);
for (int i = 0; i < discardThresholdBoxes.length; i++) {
hp.add(new Label("" + (i + 1) + "."));
hp.add(discardThresholdBoxes[i]);
}
mainPanel.add(hp);
return mainPanel;
}
public RegattaDTO getSelectedRegatta() {
RegattaDTO result = null;
int selIndex = regattaListBox.getSelectedIndex();
if(selIndex > 0) { // the zero index represents the 'no selection' text
String itemText = regattaListBox.getItemText(selIndex);
for(RegattaDTO regattaDTO: existingRegattas) {
if(regattaDTO.name.equals(itemText)) {
result = regattaDTO;
break;
}
}
}
return result;
}
}
package com.sap.sailing.gwt.ui.adminconsole;
import java.util.Collection;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.sap.sailing.gwt.ui.client.ErrorReporter;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.shared.RegattaDTO;
import com.sap.sailing.gwt.ui.shared.StrippedLeaderboardDTO;
public abstract class RegattaLeaderboardDialog extends AbstractLeaderboardDialog {
protected ListBox regattaListBox;
protected Collection<RegattaDTO> existingRegattas;
protected static class LeaderboardParameterValidator implements Validator<LeaderboardDescriptor> {
protected final StringMessages stringConstants;
protected final Collection<StrippedLeaderboardDTO> existingLeaderboards;
public LeaderboardParameterValidator(StringMessages stringConstants, Collection<StrippedLeaderboardDTO> existingLeaderboards){
super();
this.stringConstants = stringConstants;
this.existingLeaderboards = existingLeaderboards;
}
@Override
public String getErrorMessage(LeaderboardDescriptor leaderboardToValidate) {
String errorMessage;
boolean nonEmpty = leaderboardToValidate.getName() != null && leaderboardToValidate.getName().length() > 0;
boolean discardThresholdsAscending = true;
for (int i = 1; i < leaderboardToValidate.getDiscardThresholds().length; i++) {
// TODO what are correct values for discarding Thresholds?
if (0 < leaderboardToValidate.getDiscardThresholds().length){
discardThresholdsAscending = discardThresholdsAscending
&& leaderboardToValidate.getDiscardThresholds()[i - 1] < leaderboardToValidate.getDiscardThresholds()[i]
// and if one box is empty, all subsequent boxes need to be empty too
&& (leaderboardToValidate.getDiscardThresholds()[i] == 0 || leaderboardToValidate.getDiscardThresholds()[i-1] > 0);
}
}
boolean unique = true;
for (StrippedLeaderboardDTO dao : existingLeaderboards) {
if(dao.name.equals(leaderboardToValidate.getName())){
unique = false;
}
}
boolean regattaSelected = leaderboardToValidate.getRegattaName() != null ? true : false;
if (!nonEmpty) {
errorMessage = stringConstants.pleaseEnterAName();
} else if(!regattaSelected){
errorMessage = stringConstants.pleaseSelectARegatta();
} else if(!unique){
errorMessage = stringConstants.leaderboardWithThisNameAlreadyExists();
} else if (!discardThresholdsAscending) {
errorMessage = stringConstants.discardThresholdsMustBeAscending();
} else {
errorMessage = null;
}
return errorMessage;
}
}
public RegattaLeaderboardDialog(String title, LeaderboardDescriptor leaderboardDTO, Collection<RegattaDTO> existingRegattas, StringMessages stringConstants,
ErrorReporter errorReporter, LeaderboardParameterValidator validator, DialogCallback<LeaderboardDescriptor> callback) {
super(title, leaderboardDTO, stringConstants, validator, callback);
this.existingRegattas = existingRegattas;
}
@Override
protected LeaderboardDescriptor getResult() {
LeaderboardDescriptor leaderboard = super.getResult();
leaderboard.setRegattaName(getSelectedRegatta() != null ? getSelectedRegatta().name : null);
return leaderboard;
}
@Override
protected Widget getAdditionalWidget() {
VerticalPanel mainPanel = new VerticalPanel();
Grid formGrid = new Grid(3,2);
formGrid.setCellSpacing(3);
formGrid.setWidget(0, 0, new Label(stringMessages.name() + ":"));
formGrid.setWidget(0, 1, nameTextBox);
formGrid.setWidget(1, 0, new Label(stringMessages.regatta() + ":"));
formGrid.setWidget(1, 1, regattaListBox);
mainPanel.add(formGrid);
mainPanel.add(new Label(stringMessages.discardRacesFromHowManyStartedRacesOn()));
HorizontalPanel hp = new HorizontalPanel();
hp.setSpacing(3);
for (int i = 0; i < discardThresholdBoxes.length; i++) {
hp.add(new Label("" + (i + 1) + "."));
hp.add(discardThresholdBoxes[i]);
}
mainPanel.add(hp);
return mainPanel;
}
public RegattaDTO getSelectedRegatta() {
RegattaDTO result = null;
int selIndex = regattaListBox.getSelectedIndex();
if(selIndex > 0) { // the zero index represents the 'no selection' text
String itemText = regattaListBox.getItemText(selIndex);
for(RegattaDTO regattaDTO: existingRegattas) {
if(regattaDTO.name.equals(itemText)) {
result = regattaDTO;
break;
}
}
}
return result;
}
}
@@ -1,34 +1,33 @@
package com.sap.sailing.gwt.ui.adminconsole;
import java.util.Collection;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.sap.sailing.gwt.ui.client.ErrorReporter;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.shared.RegattaDTO;
import com.sap.sailing.gwt.ui.shared.StrippedLeaderboardDTO;
public class RegattaLeaderboardEditDialog extends RegattaLeaderboardDialog {
public RegattaLeaderboardEditDialog(Collection<StrippedLeaderboardDTO> otherExistingLeaderboards, Collection<RegattaDTO> existingRegattas,
LeaderboardDescriptor leaderboard, StringMessages stringConstants, ErrorReporter errorReporter,
AsyncCallback<LeaderboardDescriptor> callback) {
super(stringConstants.editRegattaLeaderboard(), leaderboard, existingRegattas, stringConstants, errorReporter, new RegattaLeaderboardDialog.LeaderboardParameterValidator(
stringConstants, otherExistingLeaderboards), callback);
nameTextBox = createTextBox(leaderboard.getName());
regattaListBox = createListBox(false);
regattaListBox.addItem(stringConstants.pleaseSelectARegatta());
int i=1;
for (RegattaDTO regatta : existingRegattas) {
regattaListBox.addItem(regatta.name);
if (regatta.name.equals(leaderboard.getRegattaName())) {
regattaListBox.setSelectedIndex(i);
}
i++;
}
regattaListBox.setEnabled(false);
discardThresholdBoxes = initPrefilledDiscardThresholdBoxes(leaderboard.getDiscardThresholds(), this);
}
}
package com.sap.sailing.gwt.ui.adminconsole;
import java.util.Collection;
import com.sap.sailing.gwt.ui.client.ErrorReporter;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.shared.RegattaDTO;
import com.sap.sailing.gwt.ui.shared.StrippedLeaderboardDTO;
public class RegattaLeaderboardEditDialog extends RegattaLeaderboardDialog {
public RegattaLeaderboardEditDialog(Collection<StrippedLeaderboardDTO> otherExistingLeaderboards, Collection<RegattaDTO> existingRegattas,
LeaderboardDescriptor leaderboard, StringMessages stringConstants, ErrorReporter errorReporter,
DialogCallback<LeaderboardDescriptor> callback) {
super(stringConstants.editRegattaLeaderboard(), leaderboard, existingRegattas, stringConstants, errorReporter, new RegattaLeaderboardDialog.LeaderboardParameterValidator(
stringConstants, otherExistingLeaderboards), callback);
nameTextBox = createTextBox(leaderboard.getName());
regattaListBox = createListBox(false);
regattaListBox.addItem(stringConstants.pleaseSelectARegatta());
int i=1;
for (RegattaDTO regatta : existingRegattas) {
regattaListBox.addItem(regatta.name);
if (regatta.name.equals(leaderboard.getRegattaName())) {
regattaListBox.setSelectedIndex(i);
}
i++;
}
regattaListBox.setEnabled(false);
discardThresholdBoxes = initPrefilledDiscardThresholdBoxes(leaderboard.getDiscardThresholds(), this);
}
}
@@ -1,233 +1,232 @@
package com.sap.sailing.gwt.ui.adminconsole;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import com.google.gwt.dom.client.Style.FontWeight;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.sap.sailing.domain.common.ScoringSchemeType;
import com.sap.sailing.gwt.ui.client.DataEntryDialog;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.leaderboard.ScoringSchemeTypeFormatter;
import com.sap.sailing.gwt.ui.shared.BoatClassDTO;
import com.sap.sailing.gwt.ui.shared.RegattaDTO;
import com.sap.sailing.gwt.ui.shared.SeriesDTO;
public class RegattaWithSeriesAndFleetsCreateDialog extends DataEntryDialog<RegattaDTO> {
private StringMessages stringConstants;
private RegattaDTO regatta;
private TextBox nameEntryField;
private TextBox boatClassEntryField;
private ListBox scoringSchemeListBox;
private List<SeriesDTO> createdSeries;
private Grid seriesGrid;
protected static class RegattaParameterValidator implements Validator<RegattaDTO> {
private StringMessages stringConstants;
private ArrayList<RegattaDTO> existingRegattas;
public RegattaParameterValidator(StringMessages stringConstants, Collection<RegattaDTO> existingRegattas) {
this.stringConstants = stringConstants;
this.existingRegattas = new ArrayList<RegattaDTO>(existingRegattas);
}
@Override
public String getErrorMessage(RegattaDTO regattaToValidate) {
String errorMessage = null;
boolean nameNotEmpty = regattaToValidate.name != null && regattaToValidate.name.length() > 0;
boolean boatClassNotEmpty = regattaToValidate.boatClass != null
&& regattaToValidate.boatClass.name.length() > 0;
boolean unique = true;
for (RegattaDTO regatta : existingRegattas) {
if (regatta.name.equals(regattaToValidate.name)) {
unique = false;
break;
}
}
if (!nameNotEmpty) {
errorMessage = stringConstants.pleaseEnterAName();
} else if (!boatClassNotEmpty) {
errorMessage = stringConstants.pleaseEnterAName();
} else if (!unique) {
errorMessage = stringConstants.regattaWithThisNameAlreadyExists();
}
if (errorMessage == null) {
List<SeriesDTO> seriesToValidate = regattaToValidate.series;
int index = 0;
boolean seriesNameNotEmpty = true;
for (SeriesDTO series : seriesToValidate) {
seriesNameNotEmpty = series.name != null && series.name.length() > 0;
if (!seriesNameNotEmpty) {
break;
}
index++;
}
int index2 = 0;
boolean seriesUnique = true;
HashSet<String> setToFindDuplicates = new HashSet<String>();
for (SeriesDTO series : seriesToValidate) {
if (!setToFindDuplicates.add(series.name)) {
seriesUnique = false;
break;
}
index2++;
}
if (!seriesNameNotEmpty) {
errorMessage = stringConstants.series() + " " + (index + 1) + ": "
+ stringConstants.pleaseEnterAName();
} else if (!seriesUnique) {
errorMessage = stringConstants.series() + " " + (index2 + 1) + ": "
+ stringConstants.seriesWithThisNameAlreadyExists();
}
}
return errorMessage;
}
}
public RegattaWithSeriesAndFleetsCreateDialog(Collection<RegattaDTO> existingRegattas,
StringMessages stringConstants, AsyncCallback<RegattaDTO> callback) {
super(stringConstants.regatta(), null, stringConstants.ok(), stringConstants.cancel(),
new RegattaParameterValidator(stringConstants, existingRegattas), callback);
this.stringConstants = stringConstants;
this.regatta = new RegattaDTO();
nameEntryField = createTextBox(null);
nameEntryField.setVisibleLength(40);
boatClassEntryField = createTextBox(null);
boatClassEntryField.setVisibleLength(20);
scoringSchemeListBox = createListBox(false);
for (ScoringSchemeType scoringSchemeType: ScoringSchemeType.values()) {
scoringSchemeListBox.addItem(ScoringSchemeTypeFormatter.format(scoringSchemeType, stringConstants));
}
createdSeries = new ArrayList<SeriesDTO>();
seriesGrid = new Grid(0, 0);
}
@Override
protected RegattaDTO getResult() {
regatta.name = nameEntryField.getText();
regatta.boatClass = new BoatClassDTO(boatClassEntryField.getText(), 0.0);
regatta.scoringScheme = getSelectedScoringSchemeType();
regatta.series = new ArrayList<SeriesDTO>();
regatta.series.addAll(createdSeries);
return regatta;
}
public ScoringSchemeType getSelectedScoringSchemeType() {
ScoringSchemeType result = null;
int selIndex = scoringSchemeListBox.getSelectedIndex();
if(selIndex >= 0) {
String itemText = scoringSchemeListBox.getItemText(selIndex);
for(ScoringSchemeType scoringSchemeType: ScoringSchemeType.values()) {
if(ScoringSchemeTypeFormatter.format(scoringSchemeType, stringConstants).equals(itemText)) {
result = scoringSchemeType;
break;
}
}
}
return result;
}
@Override
protected Widget getAdditionalWidget() {
final VerticalPanel panel = new VerticalPanel();
Widget additionalWidget = super.getAdditionalWidget();
if (additionalWidget != null) {
panel.add(additionalWidget);
}
Grid formGrid = new Grid(3, 2);
panel.add(formGrid);
formGrid.setWidget(0, 0, new Label(stringConstants.name() + ":"));
formGrid.setWidget(0, 1, nameEntryField);
formGrid.setWidget(1, 0, new Label(stringConstants.boatClass() + ":"));
formGrid.setWidget(1, 1, boatClassEntryField);
formGrid.setWidget(2, 0, new Label(stringConstants.scoringSystem() + ":"));
formGrid.setWidget(2, 1, scoringSchemeListBox);
panel.add(createHeadlineLabel(stringConstants.series()));
panel.add(seriesGrid);
Button addSeriesButton = new Button("Add series");
addSeriesButton.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
RegattaDTO result = getResult();
SeriesWithFleetsCreateDialog dialog = new SeriesWithFleetsCreateDialog(Collections
.unmodifiableCollection(result.series), stringConstants, new AsyncCallback<SeriesDTO>() {
@Override
public void onFailure(Throwable t) {
}
@Override
public void onSuccess(SeriesDTO newSeries) {
createdSeries.add(newSeries);
updateSeriesGrid(panel);
}
});
dialog.show();
}
});
panel.add(addSeriesButton);
return panel;
}
private void updateSeriesGrid(VerticalPanel parentPanel) {
int widgetIndex = parentPanel.getWidgetIndex(seriesGrid);
parentPanel.remove(seriesGrid);
int seriesCount = createdSeries.size();
seriesGrid = new Grid(seriesCount * 2, 3);
seriesGrid.setCellSpacing(3);
for (int i = 0; i < seriesCount; i++) {
SeriesDTO seriesDTO = createdSeries.get(i);
Label seriesLabel = new Label((i + 1) + ". " + stringConstants.series() + ":");
seriesLabel.setWordWrap(false);
seriesLabel.getElement().getStyle().setFontWeight(FontWeight.BOLD);
seriesGrid.setWidget(i * 2, 0, seriesLabel);
seriesGrid.setHTML(i * 2, 1, seriesDTO.name);
if (seriesDTO.getFleets() != null && seriesDTO.getFleets().size() > 0) {
seriesGrid.setHTML(i * 2 + 1, 1, seriesDTO.getFleets().size() + " fleets: "
+ seriesDTO.getFleets().toString());
} else {
seriesGrid.setHTML(i * 2 + 1, 1, seriesDTO.getFleets().size() + " No fleets defined.");
}
}
parentPanel.insert(seriesGrid, widgetIndex);
}
@Override
public void show() {
super.show();
nameEntryField.setFocus(true);
}
}
package com.sap.sailing.gwt.ui.adminconsole;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import com.google.gwt.dom.client.Style.FontWeight;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.sap.sailing.domain.common.ScoringSchemeType;
import com.sap.sailing.gwt.ui.client.DataEntryDialog;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.leaderboard.ScoringSchemeTypeFormatter;
import com.sap.sailing.gwt.ui.shared.BoatClassDTO;
import com.sap.sailing.gwt.ui.shared.RegattaDTO;
import com.sap.sailing.gwt.ui.shared.SeriesDTO;
public class RegattaWithSeriesAndFleetsCreateDialog extends DataEntryDialog<RegattaDTO> {
private StringMessages stringConstants;
private RegattaDTO regatta;
private TextBox nameEntryField;
private TextBox boatClassEntryField;
private ListBox scoringSchemeListBox;
private List<SeriesDTO> createdSeries;
private Grid seriesGrid;
protected static class RegattaParameterValidator implements Validator<RegattaDTO> {
private StringMessages stringConstants;
private ArrayList<RegattaDTO> existingRegattas;
public RegattaParameterValidator(StringMessages stringConstants, Collection<RegattaDTO> existingRegattas) {
this.stringConstants = stringConstants;
this.existingRegattas = new ArrayList<RegattaDTO>(existingRegattas);
}
@Override
public String getErrorMessage(RegattaDTO regattaToValidate) {
String errorMessage = null;
boolean nameNotEmpty = regattaToValidate.name != null && regattaToValidate.name.length() > 0;
boolean boatClassNotEmpty = regattaToValidate.boatClass != null
&& regattaToValidate.boatClass.name.length() > 0;
boolean unique = true;
for (RegattaDTO regatta : existingRegattas) {
if (regatta.name.equals(regattaToValidate.name)) {
unique = false;
break;
}
}
if (!nameNotEmpty) {
errorMessage = stringConstants.pleaseEnterAName();
} else if (!boatClassNotEmpty) {
errorMessage = stringConstants.pleaseEnterAName();
} else if (!unique) {
errorMessage = stringConstants.regattaWithThisNameAlreadyExists();
}
if (errorMessage == null) {
List<SeriesDTO> seriesToValidate = regattaToValidate.series;
int index = 0;
boolean seriesNameNotEmpty = true;
for (SeriesDTO series : seriesToValidate) {
seriesNameNotEmpty = series.name != null && series.name.length() > 0;
if (!seriesNameNotEmpty) {
break;
}
index++;
}
int index2 = 0;
boolean seriesUnique = true;
HashSet<String> setToFindDuplicates = new HashSet<String>();
for (SeriesDTO series : seriesToValidate) {
if (!setToFindDuplicates.add(series.name)) {
seriesUnique = false;
break;
}
index2++;
}
if (!seriesNameNotEmpty) {
errorMessage = stringConstants.series() + " " + (index + 1) + ": "
+ stringConstants.pleaseEnterAName();
} else if (!seriesUnique) {
errorMessage = stringConstants.series() + " " + (index2 + 1) + ": "
+ stringConstants.seriesWithThisNameAlreadyExists();
}
}
return errorMessage;
}
}
public RegattaWithSeriesAndFleetsCreateDialog(Collection<RegattaDTO> existingRegattas,
StringMessages stringConstants, DialogCallback<RegattaDTO> callback) {
super(stringConstants.regatta(), null, stringConstants.ok(), stringConstants.cancel(),
new RegattaParameterValidator(stringConstants, existingRegattas), callback);
this.stringConstants = stringConstants;
this.regatta = new RegattaDTO();
nameEntryField = createTextBox(null);
nameEntryField.setVisibleLength(40);
boatClassEntryField = createTextBox(null);
boatClassEntryField.setVisibleLength(20);
scoringSchemeListBox = createListBox(false);
for (ScoringSchemeType scoringSchemeType: ScoringSchemeType.values()) {
scoringSchemeListBox.addItem(ScoringSchemeTypeFormatter.format(scoringSchemeType, stringConstants));
}
createdSeries = new ArrayList<SeriesDTO>();
seriesGrid = new Grid(0, 0);
}
@Override
protected RegattaDTO getResult() {
regatta.name = nameEntryField.getText();
regatta.boatClass = new BoatClassDTO(boatClassEntryField.getText(), 0.0);
regatta.scoringScheme = getSelectedScoringSchemeType();
regatta.series = new ArrayList<SeriesDTO>();
regatta.series.addAll(createdSeries);
return regatta;
}
public ScoringSchemeType getSelectedScoringSchemeType() {
ScoringSchemeType result = null;
int selIndex = scoringSchemeListBox.getSelectedIndex();
if(selIndex >= 0) {
String itemText = scoringSchemeListBox.getItemText(selIndex);
for(ScoringSchemeType scoringSchemeType: ScoringSchemeType.values()) {
if(ScoringSchemeTypeFormatter.format(scoringSchemeType, stringConstants).equals(itemText)) {
result = scoringSchemeType;
break;
}
}
}
return result;
}
@Override
protected Widget getAdditionalWidget() {
final VerticalPanel panel = new VerticalPanel();
Widget additionalWidget = super.getAdditionalWidget();
if (additionalWidget != null) {
panel.add(additionalWidget);
}
Grid formGrid = new Grid(3, 2);
panel.add(formGrid);
formGrid.setWidget(0, 0, new Label(stringConstants.name() + ":"));
formGrid.setWidget(0, 1, nameEntryField);
formGrid.setWidget(1, 0, new Label(stringConstants.boatClass() + ":"));
formGrid.setWidget(1, 1, boatClassEntryField);
formGrid.setWidget(2, 0, new Label(stringConstants.scoringSystem() + ":"));
formGrid.setWidget(2, 1, scoringSchemeListBox);
panel.add(createHeadlineLabel(stringConstants.series()));
panel.add(seriesGrid);
Button addSeriesButton = new Button("Add series");
addSeriesButton.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
RegattaDTO result = getResult();
SeriesWithFleetsCreateDialog dialog = new SeriesWithFleetsCreateDialog(Collections
.unmodifiableCollection(result.series), stringConstants, new DialogCallback<SeriesDTO>() {
@Override
public void cancel() {
}
@Override
public void ok(SeriesDTO newSeries) {
createdSeries.add(newSeries);
updateSeriesGrid(panel);
}
});
dialog.show();
}
});
panel.add(addSeriesButton);
return panel;
}
private void updateSeriesGrid(VerticalPanel parentPanel) {
int widgetIndex = parentPanel.getWidgetIndex(seriesGrid);
parentPanel.remove(seriesGrid);
int seriesCount = createdSeries.size();
seriesGrid = new Grid(seriesCount * 2, 3);
seriesGrid.setCellSpacing(3);
for (int i = 0; i < seriesCount; i++) {
SeriesDTO seriesDTO = createdSeries.get(i);
Label seriesLabel = new Label((i + 1) + ". " + stringConstants.series() + ":");
seriesLabel.setWordWrap(false);
seriesLabel.getElement().getStyle().setFontWeight(FontWeight.BOLD);
seriesGrid.setWidget(i * 2, 0, seriesLabel);
seriesGrid.setHTML(i * 2, 1, seriesDTO.name);
if (seriesDTO.getFleets() != null && seriesDTO.getFleets().size() > 0) {
seriesGrid.setHTML(i * 2 + 1, 1, seriesDTO.getFleets().size() + " fleets: "
+ seriesDTO.getFleets().toString());
} else {
seriesGrid.setHTML(i * 2 + 1, 1, seriesDTO.getFleets().size() + " No fleets defined.");
}
}
parentPanel.insert(seriesGrid, widgetIndex);
}
@Override
public void show() {
super.show();
nameEntryField.setFocus(true);
}
}
@@ -15,6 +15,7 @@ import com.google.gwt.user.client.ui.Widget;
import com.sap.sailing.domain.common.impl.Util.Pair;
import com.sap.sailing.domain.common.impl.Util.Triple;
import com.sap.sailing.gwt.ui.client.DataEntryDialog;
import com.sap.sailing.gwt.ui.client.DataEntryDialog.DialogCallback;
import com.sap.sailing.gwt.ui.client.ErrorReporter;
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
import com.sap.sailing.gwt.ui.client.StringMessages;
@@ -62,9 +63,9 @@ public class ReplicationPanel extends FlowPanel {
private void addReplication() {
AddReplicationDialog dialog = new AddReplicationDialog(null,
new AsyncCallback<Triple<Pair<String, String>, Integer, Integer>>() {
new DialogCallback<Triple<Pair<String, String>, Integer, Integer>>() {
@Override
public void onSuccess(final Triple<Pair<String, String>, Integer, Integer> masterNameAndExchangeNameAndMessagingPortNumberAndServletPortNumber) {
public void ok(final Triple<Pair<String, String>, Integer, Integer> masterNameAndExchangeNameAndMessagingPortNumberAndServletPortNumber) {
sailingService.startReplicatingFromMaster(masterNameAndExchangeNameAndMessagingPortNumberAndServletPortNumber.getA().getA(),
masterNameAndExchangeNameAndMessagingPortNumberAndServletPortNumber.getA().getB(),
masterNameAndExchangeNameAndMessagingPortNumberAndServletPortNumber.getC(),
@@ -84,7 +85,7 @@ public class ReplicationPanel extends FlowPanel {
}
@Override
public void onFailure(Throwable arg0) {
public void cancel() {
// simply don't add replication
}
});
@@ -141,7 +142,7 @@ public class ReplicationPanel extends FlowPanel {
private final IntegerBox servletPortField;
public AddReplicationDialog(final Validator<Triple<Pair<String, String>, Integer, Integer>> validator,
final AsyncCallback<Triple<Pair<String, String>, Integer, Integer>> callback) {
final DialogCallback<Triple<Pair<String, String>, Integer, Integer>> callback) {
super(stringMessages.add(), stringMessages.enterMaster(),
stringMessages.ok(), stringMessages.cancel(), validator, callback);
hostnameEntryField = createTextBox("localhost");
@@ -1,242 +1,243 @@
package com.sap.sailing.gwt.ui.adminconsole;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import com.google.gwt.cell.client.FieldUpdater;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.cellview.client.CellTable;
import com.google.gwt.user.cellview.client.TextColumn;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.view.client.ListDataProvider;
import com.google.gwt.view.client.SelectionChangeEvent;
import com.google.gwt.view.client.SingleSelectionModel;
import com.sap.sailing.gwt.ui.client.ErrorReporter;
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.shared.EventDTO;
import com.sap.sailing.gwt.ui.shared.RegattaDTO;
/**
* Allows administrators to manage data of a sailing event. This is a temporary panel because the managed event
* is not connected with any regatta and it used only to manage of basic attributes of the an event.
* @author Frank Mittag (C5163974)
*
*/
public class SailingEventManagementPanel extends SimplePanel {
private final SailingServiceAsync sailingService;
private final ErrorReporter errorReporter;
private final StringMessages stringMessages;
private CellTable<EventDTO> eventTable;
private SingleSelectionModel<EventDTO> eventSelectionModel;
private ListDataProvider<EventDTO> eventProvider;
private final AdminConsoleTableResources tableRes = GWT.create(AdminConsoleTableResources.class);
public SailingEventManagementPanel(final SailingServiceAsync sailingService, final ErrorReporter errorReporter,
final StringMessages stringMessages) {
this.sailingService = sailingService;
this.stringMessages = stringMessages;
this.errorReporter = errorReporter;
VerticalPanel mainPanel = new VerticalPanel();
this.setWidget(mainPanel);
mainPanel.setWidth("100%");
HorizontalPanel eventsPanel = new HorizontalPanel();
eventsPanel.setSpacing(5);
mainPanel.add(eventsPanel);
Button createEventBtn = new Button(stringMessages.actionAddEvent());
createEventBtn.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
openCreateEventDialog();
}
});
eventsPanel.add(createEventBtn);
// sailing events table
TextColumn<EventDTO> eventNameColumn = new TextColumn<EventDTO>() {
@Override
public String getValue(EventDTO event) {
return event.name;
}
};
TextColumn<EventDTO> venueNameColumn = new TextColumn<EventDTO>() {
@Override
public String getValue(EventDTO event) {
return event.venue != null ? event.venue.name : "";
}
};
TextColumn<EventDTO> publicationUrlColumn = new TextColumn<EventDTO>() {
@Override
public String getValue(EventDTO event) {
return event.publicationUrl;
}
};
TextColumn<EventDTO> isPublicColumn = new TextColumn<EventDTO>() {
@Override
public String getValue(EventDTO event) {
return event.isPublic ? stringMessages.yes() : stringMessages.no();
}
};
ImagesBarColumn<EventDTO, RegattaConfigImagesBarCell> eventActionColumn = new ImagesBarColumn<EventDTO, RegattaConfigImagesBarCell>(
new RegattaConfigImagesBarCell(stringMessages));
eventActionColumn.setFieldUpdater(new FieldUpdater<EventDTO, String>() {
@Override
public void update(int index, EventDTO event, String value) {
if (RegattaConfigImagesBarCell.ACTION_REMOVE.equals(value)) {
if (Window.confirm(stringMessages.doYouReallyWantToRemoveRegatta(event.name))) {
removeEvent(event);
}
} else if (RegattaConfigImagesBarCell.ACTION_EDIT.equals(value)) {
openEditEventDialog(event);
}
}
});
eventTable = new CellTable<EventDTO>(10000, tableRes);
eventTable.addColumn(eventNameColumn, stringMessages.event());
eventTable.addColumn(venueNameColumn, stringMessages.venue());
eventTable.addColumn(publicationUrlColumn, stringMessages.publicationUrl());
eventTable.addColumn(isPublicColumn, stringMessages.isPublic());
eventTable.addColumn(eventActionColumn, stringMessages.actions());
eventSelectionModel = new SingleSelectionModel<EventDTO>();
eventSelectionModel.addSelectionChangeHandler(new SelectionChangeEvent.Handler() {
@Override
public void onSelectionChange(SelectionChangeEvent event) {
}
});
eventTable.setSelectionModel(eventSelectionModel);
eventProvider = new ListDataProvider<EventDTO>();
eventProvider.addDataDisplay(eventTable);
mainPanel.add(eventTable);
fillEvents();
}
private void removeEvent(final EventDTO event) {
sailingService.removeEvent(event.name, new AsyncCallback<Void>() {
@Override
public void onFailure(Throwable caught) {
errorReporter.reportError("Error trying to remove event " + event.name + ": " + caught.getMessage());
}
@Override
public void onSuccess(Void result) {
fillEvents();
}
});
}
private void openCreateEventDialog() {
List<EventDTO> existingEvents = new ArrayList<EventDTO>(eventProvider.getList());
EventCreateDialog dialog = new EventCreateDialog(Collections.unmodifiableCollection(existingEvents), stringMessages,
new AsyncCallback<EventDTO>() {
@Override
public void onFailure(Throwable t) {
}
@Override
public void onSuccess(EventDTO newEvent) {
createNewEvent(newEvent);
}
});
dialog.show();
}
private void openEditEventDialog(final EventDTO selectedEvent) {
List<EventDTO> existingEvents = new ArrayList<EventDTO>(eventProvider.getList());
existingEvents.remove(selectedEvent);
EventEditDialog dialog = new EventEditDialog(selectedEvent, Collections.unmodifiableCollection(existingEvents), stringMessages,
new AsyncCallback<EventDTO>() {
@Override
public void onFailure(Throwable t) {
}
@Override
public void onSuccess(EventDTO updatedEvent) {
updateEvent(selectedEvent.name, updatedEvent);
}
});
dialog.show();
}
private void updateEvent(final String oldEventName, final EventDTO updatedEvent) {
List<String> regattaNames = new ArrayList<String>();
for(RegattaDTO regatta: updatedEvent.regattas) {
regattaNames.add(regatta.name);
}
sailingService.updateEvent(oldEventName, updatedEvent.venue, updatedEvent.publicationUrl, updatedEvent.isPublic,
regattaNames, new AsyncCallback<Void>() {
@Override
public void onFailure(Throwable t) {
errorReporter.reportError("Error trying to update sailing event" + oldEventName + ": " + t.getMessage());
}
@Override
public void onSuccess(Void result) {
fillEvents();
if(!oldEventName.equals(updatedEvent.name)) {
sailingService.renameEvent(oldEventName, updatedEvent.name, new AsyncCallback<Void>() {
@Override
public void onSuccess(Void result) {
fillEvents();
}
@Override
public void onFailure(Throwable t) {
errorReporter.reportError("Error trying to rename sailing event" + oldEventName + ": " + t.getMessage());
}
});
}
}
});
}
private void createNewEvent(final EventDTO newEvent) {
sailingService.createEvent(newEvent.name, newEvent.venue.name, newEvent.publicationUrl, newEvent.isPublic, new AsyncCallback<EventDTO>() {
@Override
public void onFailure(Throwable t) {
errorReporter.reportError("Error trying to create new event" + newEvent.name + ": " + t.getMessage());
}
@Override
public void onSuccess(EventDTO newEvent) {
fillEvents();
}
});
}
private void fillEvents() {
sailingService.getEvents(new AsyncCallback<List<EventDTO>>() {
@Override
public void onFailure(Throwable caught) {
errorReporter.reportError("Remote Procedure Call getEvents() - Failure: " + caught.getMessage());
}
@Override
public void onSuccess(List<EventDTO> result) {
eventProvider.getList().clear();
eventProvider.getList().addAll(result);
}
});
}
}
package com.sap.sailing.gwt.ui.adminconsole;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import com.google.gwt.cell.client.FieldUpdater;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.cellview.client.CellTable;
import com.google.gwt.user.cellview.client.TextColumn;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.view.client.ListDataProvider;
import com.google.gwt.view.client.SelectionChangeEvent;
import com.google.gwt.view.client.SingleSelectionModel;
import com.sap.sailing.gwt.ui.client.DataEntryDialog.DialogCallback;
import com.sap.sailing.gwt.ui.client.ErrorReporter;
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.shared.EventDTO;
import com.sap.sailing.gwt.ui.shared.RegattaDTO;
/**
* Allows administrators to manage data of a sailing event. This is a temporary panel because the managed event
* is not connected with any regatta and it used only to manage of basic attributes of the an event.
* @author Frank Mittag (C5163974)
*
*/
public class SailingEventManagementPanel extends SimplePanel {
private final SailingServiceAsync sailingService;
private final ErrorReporter errorReporter;
private final StringMessages stringMessages;
private CellTable<EventDTO> eventTable;
private SingleSelectionModel<EventDTO> eventSelectionModel;
private ListDataProvider<EventDTO> eventProvider;
private final AdminConsoleTableResources tableRes = GWT.create(AdminConsoleTableResources.class);
public SailingEventManagementPanel(final SailingServiceAsync sailingService, final ErrorReporter errorReporter,
final StringMessages stringMessages) {
this.sailingService = sailingService;
this.stringMessages = stringMessages;
this.errorReporter = errorReporter;
VerticalPanel mainPanel = new VerticalPanel();
this.setWidget(mainPanel);
mainPanel.setWidth("100%");
HorizontalPanel eventsPanel = new HorizontalPanel();
eventsPanel.setSpacing(5);
mainPanel.add(eventsPanel);
Button createEventBtn = new Button(stringMessages.actionAddEvent());
createEventBtn.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
openCreateEventDialog();
}
});
eventsPanel.add(createEventBtn);
// sailing events table
TextColumn<EventDTO> eventNameColumn = new TextColumn<EventDTO>() {
@Override
public String getValue(EventDTO event) {
return event.name;
}
};
TextColumn<EventDTO> venueNameColumn = new TextColumn<EventDTO>() {
@Override
public String getValue(EventDTO event) {
return event.venue != null ? event.venue.name : "";
}
};
TextColumn<EventDTO> publicationUrlColumn = new TextColumn<EventDTO>() {
@Override
public String getValue(EventDTO event) {
return event.publicationUrl;
}
};
TextColumn<EventDTO> isPublicColumn = new TextColumn<EventDTO>() {
@Override
public String getValue(EventDTO event) {
return event.isPublic ? stringMessages.yes() : stringMessages.no();
}
};
ImagesBarColumn<EventDTO, RegattaConfigImagesBarCell> eventActionColumn = new ImagesBarColumn<EventDTO, RegattaConfigImagesBarCell>(
new RegattaConfigImagesBarCell(stringMessages));
eventActionColumn.setFieldUpdater(new FieldUpdater<EventDTO, String>() {
@Override
public void update(int index, EventDTO event, String value) {
if (RegattaConfigImagesBarCell.ACTION_REMOVE.equals(value)) {
if (Window.confirm(stringMessages.doYouReallyWantToRemoveRegatta(event.name))) {
removeEvent(event);
}
} else if (RegattaConfigImagesBarCell.ACTION_EDIT.equals(value)) {
openEditEventDialog(event);
}
}
});
eventTable = new CellTable<EventDTO>(10000, tableRes);
eventTable.addColumn(eventNameColumn, stringMessages.event());
eventTable.addColumn(venueNameColumn, stringMessages.venue());
eventTable.addColumn(publicationUrlColumn, stringMessages.publicationUrl());
eventTable.addColumn(isPublicColumn, stringMessages.isPublic());
eventTable.addColumn(eventActionColumn, stringMessages.actions());
eventSelectionModel = new SingleSelectionModel<EventDTO>();
eventSelectionModel.addSelectionChangeHandler(new SelectionChangeEvent.Handler() {
@Override
public void onSelectionChange(SelectionChangeEvent event) {
}
});
eventTable.setSelectionModel(eventSelectionModel);
eventProvider = new ListDataProvider<EventDTO>();
eventProvider.addDataDisplay(eventTable);
mainPanel.add(eventTable);
fillEvents();
}
private void removeEvent(final EventDTO event) {
sailingService.removeEvent(event.name, new AsyncCallback<Void>() {
@Override
public void onFailure(Throwable caught) {
errorReporter.reportError("Error trying to remove event " + event.name + ": " + caught.getMessage());
}
@Override
public void onSuccess(Void result) {
fillEvents();
}
});
}
private void openCreateEventDialog() {
List<EventDTO> existingEvents = new ArrayList<EventDTO>(eventProvider.getList());
EventCreateDialog dialog = new EventCreateDialog(Collections.unmodifiableCollection(existingEvents), stringMessages,
new DialogCallback<EventDTO>() {
@Override
public void cancel() {
}
@Override
public void ok(EventDTO newEvent) {
createNewEvent(newEvent);
}
});
dialog.show();
}
private void openEditEventDialog(final EventDTO selectedEvent) {
List<EventDTO> existingEvents = new ArrayList<EventDTO>(eventProvider.getList());
existingEvents.remove(selectedEvent);
EventEditDialog dialog = new EventEditDialog(selectedEvent, Collections.unmodifiableCollection(existingEvents), stringMessages,
new DialogCallback<EventDTO>() {
@Override
public void cancel() {
}
@Override
public void ok(EventDTO updatedEvent) {
updateEvent(selectedEvent.name, updatedEvent);
}
});
dialog.show();
}
private void updateEvent(final String oldEventName, final EventDTO updatedEvent) {
List<String> regattaNames = new ArrayList<String>();
for(RegattaDTO regatta: updatedEvent.regattas) {
regattaNames.add(regatta.name);
}
sailingService.updateEvent(oldEventName, updatedEvent.venue, updatedEvent.publicationUrl, updatedEvent.isPublic,
regattaNames, new AsyncCallback<Void>() {
@Override
public void onFailure(Throwable t) {
errorReporter.reportError("Error trying to update sailing event" + oldEventName + ": " + t.getMessage());
}
@Override
public void onSuccess(Void result) {
fillEvents();
if(!oldEventName.equals(updatedEvent.name)) {
sailingService.renameEvent(oldEventName, updatedEvent.name, new AsyncCallback<Void>() {
@Override
public void onSuccess(Void result) {
fillEvents();
}
@Override
public void onFailure(Throwable t) {
errorReporter.reportError("Error trying to rename sailing event" + oldEventName + ": " + t.getMessage());
}
});
}
}
});
}
private void createNewEvent(final EventDTO newEvent) {
sailingService.createEvent(newEvent.name, newEvent.venue.name, newEvent.publicationUrl, newEvent.isPublic, new AsyncCallback<EventDTO>() {
@Override
public void onFailure(Throwable t) {
errorReporter.reportError("Error trying to create new event" + newEvent.name + ": " + t.getMessage());
}
@Override
public void onSuccess(EventDTO newEvent) {
fillEvents();
}
});
}
private void fillEvents() {
sailingService.getEvents(new AsyncCallback<List<EventDTO>>() {
@Override
public void onFailure(Throwable caught) {
errorReporter.reportError("Remote Procedure Call getEvents() - Failure: " + caught.getMessage());
}
@Override
public void onSuccess(List<EventDTO> result) {
eventProvider.getList().clear();
eventProvider.getList().addAll(result);
}
});
}
}
@@ -1,257 +1,256 @@
package com.sap.sailing.gwt.ui.adminconsole;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.IntegerBox;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.sap.sailing.domain.common.Color;
import com.sap.sailing.gwt.ui.client.DataEntryDialog;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.shared.FleetDTO;
import com.sap.sailing.gwt.ui.shared.SeriesDTO;
public class SeriesWithFleetsCreateDialog extends DataEntryDialog<SeriesDTO> {
private StringMessages stringConstants;
private SeriesDTO series;
private TextBox nameEntryField;
private CheckBox isMedalSeriesCheckbox;
private List<TextBox> fleetNameEntryFields;
private List<ListBox> fleetColorEntryFields;
private List<IntegerBox> fleetOrderNoEntryFields;
private Grid fleetsGrid;
protected static class SeriesParameterValidator implements Validator<SeriesDTO> {
private StringMessages stringConstants;
private ArrayList<SeriesDTO> existingSeries;
public SeriesParameterValidator(StringMessages stringConstants, Collection<SeriesDTO> existingSeries) {
this.stringConstants = stringConstants;
this.existingSeries = new ArrayList<SeriesDTO>(existingSeries);
}
@Override
public String getErrorMessage(SeriesDTO seriesToValidate) {
String errorMessage = null;
boolean nameNotEmpty = seriesToValidate.name != null && seriesToValidate.name.length() > 0;
boolean unique = true;
for (SeriesDTO series : existingSeries) {
if (series.name.equals(seriesToValidate.name)) {
unique = false;
break;
}
}
if (!nameNotEmpty) {
errorMessage = stringConstants.pleaseEnterAName();
} else if (!unique) {
errorMessage = stringConstants.seriesWithThisNameAlreadyExists();
}
if (errorMessage == null) {
List<FleetDTO> fleetsToValidate = seriesToValidate.getFleets();
int index = 0;
boolean fleetNameNotEmpty = true;
for (FleetDTO fleet : fleetsToValidate) {
fleetNameNotEmpty = fleet.name != null && fleet.name.length() > 0;
if (!fleetNameNotEmpty) {
break;
}
index++;
}
int index2 = 0;
boolean fleetUnique = true;
HashSet<String> setToFindDuplicates = new HashSet<String>();
for (FleetDTO fleet: fleetsToValidate) {
if(!setToFindDuplicates.add(fleet.name)) {
fleetUnique = false;
break;
}
index2++;
}
if (!fleetNameNotEmpty) {
errorMessage = stringConstants.fleet() + " " + (index + 1) + ": " + stringConstants.pleaseEnterAName();
} else if (!fleetUnique) {
errorMessage = stringConstants.fleet() + " " + (index2 + 1) + ": " + stringConstants.fleetWithThisNameAlreadyExists();
}
}
return errorMessage;
}
}
public SeriesWithFleetsCreateDialog(Collection<SeriesDTO> existingSeries, StringMessages stringConstants,
AsyncCallback<SeriesDTO> callback) {
super(stringConstants.series(), null, stringConstants.ok(), stringConstants.cancel(),
new SeriesParameterValidator(stringConstants, existingSeries), callback);
this.stringConstants = stringConstants;
this.series = new SeriesDTO();
nameEntryField = createTextBox(null);
nameEntryField.setVisibleLength(40);
isMedalSeriesCheckbox = createCheckbox(stringConstants.medalSeries());
fleetNameEntryFields = new ArrayList<TextBox>();
fleetColorEntryFields = new ArrayList<ListBox>();
fleetOrderNoEntryFields = new ArrayList<IntegerBox>();
fleetsGrid = new Grid(0, 0);
// create at least one fleet
addFleetWidget("Default", 0, null);
}
private Widget createFleetNameWidget(String defaultName) {
TextBox textBox = createTextBox(defaultName);
textBox.setVisibleLength(40);
textBox.setWidth("175px");
fleetNameEntryFields.add(textBox);
return textBox;
}
private Widget createFleetColorWidget(Color defaultColor) {
final ListBox listBox = createListBox(false);
final int fleetIndex = fleetNameEntryFields.size()-1;
listBox.addChangeHandler(new ChangeHandler() {
@Override
public void onChange(ChangeEvent event) {
// set default order no of the selected color
int selIndex = listBox.getSelectedIndex();
IntegerBox orderNoBox = fleetOrderNoEntryFields.get(fleetIndex);
TextBox nameBox = fleetNameEntryFields.get(fleetIndex);
if (selIndex == 0) {
orderNoBox.setValue(0);
} else {
String value = listBox.getValue(selIndex);
final FleetColors color = FleetColors.valueOf(value);
if (color != null) {
orderNoBox.setValue(color.getDefaultOrderNo());
nameBox.setValue(""+color.name().charAt(0)+color.name().toLowerCase().substring(1));
}
}
validate();
}
});
listBox.addItem(stringConstants.noColor());
for(FleetColors value: FleetColors.values()) {
listBox.addItem(value.name());
}
if(defaultColor == null) {
listBox.setSelectedIndex(0);
}
fleetColorEntryFields.add(listBox);
return listBox;
}
private Widget createFleetOrderNoWidget(int defaultValue) {
IntegerBox intBox = createIntegerBox(defaultValue, 3);
fleetOrderNoEntryFields.add(intBox);
return intBox;
}
@Override
protected SeriesDTO getResult() {
series.name = nameEntryField.getText();
series.setMedal(isMedalSeriesCheckbox.getValue());
List<FleetDTO> fleets = new ArrayList<FleetDTO>();
int fleetsCount = fleetNameEntryFields.size();
for(int i = 0; i < fleetsCount; i++) {
FleetDTO fleetDTO = new FleetDTO();
fleetDTO.name = fleetNameEntryFields.get(i).getValue();
fleetDTO.setColor(getSelectedColor(fleetColorEntryFields.get(i)));
int orderNo = -1;
if(fleetOrderNoEntryFields.get(i).getValue() != null) {
orderNo = fleetOrderNoEntryFields.get(i).getValue();
}
fleetDTO.setOrderNo(orderNo);
fleets.add(fleetDTO);
}
series.setFleets(fleets);
return series;
}
private Color getSelectedColor(ListBox colorListBox) {
Color result = null;
int selIndex = colorListBox.getSelectedIndex();
// the zero index represents the 'no color' option
if(selIndex > 0) {
String value = colorListBox.getValue(selIndex);
for(FleetColors color: FleetColors.values()) {
if(color.name().equals(value)) {
result = color.getColor();
break;
}
}
}
return result;
}
@Override
protected Widget getAdditionalWidget() {
final VerticalPanel panel = new VerticalPanel();
Widget additionalWidget = super.getAdditionalWidget();
if (additionalWidget != null) {
panel.add(additionalWidget);
}
Grid formGrid = new Grid(2, 2);
panel.add(formGrid);
formGrid.setWidget(0, 0, new Label(stringConstants.name() + ":"));
formGrid.setWidget(0, 1, nameEntryField);
formGrid.setWidget(1, 1, isMedalSeriesCheckbox);
panel.add(createHeadlineLabel(stringConstants.fleets()));
panel.add(fleetsGrid);
Button addFleetButton = new Button(stringConstants.addFleet());
addFleetButton.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
addFleetWidget(null, 0, null);
updateFleetsGrid(panel);
}
});
panel.add(addFleetButton);
updateFleetsGrid(panel);
return panel;
}
private void addFleetWidget(String fleetName, int ordering, Color color) {
createFleetNameWidget(fleetName);
createFleetOrderNoWidget(ordering);
createFleetColorWidget(color);
}
private void updateFleetsGrid(VerticalPanel parentPanel) {
int widgetIndex = parentPanel.getWidgetIndex(fleetsGrid);
parentPanel.remove(fleetsGrid);
int fleetCount = fleetNameEntryFields.size();
fleetsGrid = new Grid(fleetCount + 1, 3);
fleetsGrid.setCellSpacing(4);
fleetsGrid.setHTML(0, 0, stringConstants.color());
fleetsGrid.setHTML(0, 1, stringConstants.name());
fleetsGrid.setHTML(0, 2, stringConstants.rank());
for(int i = 0; i < fleetCount; i++) {
fleetsGrid.setWidget(i+1, 0, fleetColorEntryFields.get(i));
fleetsGrid.setWidget(i+1, 1, fleetNameEntryFields.get(i));
fleetsGrid.setWidget(i+1, 2, fleetOrderNoEntryFields.get(i));
}
parentPanel.insert(fleetsGrid, widgetIndex);
}
@Override
public void show() {
super.show();
nameEntryField.setFocus(true);
}
}
package com.sap.sailing.gwt.ui.adminconsole;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.IntegerBox;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.sap.sailing.domain.common.Color;
import com.sap.sailing.gwt.ui.client.DataEntryDialog;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.shared.FleetDTO;
import com.sap.sailing.gwt.ui.shared.SeriesDTO;
public class SeriesWithFleetsCreateDialog extends DataEntryDialog<SeriesDTO> {
private StringMessages stringConstants;
private SeriesDTO series;
private TextBox nameEntryField;
private CheckBox isMedalSeriesCheckbox;
private List<TextBox> fleetNameEntryFields;
private List<ListBox> fleetColorEntryFields;
private List<IntegerBox> fleetOrderNoEntryFields;
private Grid fleetsGrid;
protected static class SeriesParameterValidator implements Validator<SeriesDTO> {
private StringMessages stringConstants;
private ArrayList<SeriesDTO> existingSeries;
public SeriesParameterValidator(StringMessages stringConstants, Collection<SeriesDTO> existingSeries) {
this.stringConstants = stringConstants;
this.existingSeries = new ArrayList<SeriesDTO>(existingSeries);
}
@Override
public String getErrorMessage(SeriesDTO seriesToValidate) {
String errorMessage = null;
boolean nameNotEmpty = seriesToValidate.name != null && seriesToValidate.name.length() > 0;
boolean unique = true;
for (SeriesDTO series : existingSeries) {
if (series.name.equals(seriesToValidate.name)) {
unique = false;
break;
}
}
if (!nameNotEmpty) {
errorMessage = stringConstants.pleaseEnterAName();
} else if (!unique) {
errorMessage = stringConstants.seriesWithThisNameAlreadyExists();
}
if (errorMessage == null) {
List<FleetDTO> fleetsToValidate = seriesToValidate.getFleets();
int index = 0;
boolean fleetNameNotEmpty = true;
for (FleetDTO fleet : fleetsToValidate) {
fleetNameNotEmpty = fleet.name != null && fleet.name.length() > 0;
if (!fleetNameNotEmpty) {
break;
}
index++;
}
int index2 = 0;
boolean fleetUnique = true;
HashSet<String> setToFindDuplicates = new HashSet<String>();
for (FleetDTO fleet: fleetsToValidate) {
if(!setToFindDuplicates.add(fleet.name)) {
fleetUnique = false;
break;
}
index2++;
}
if (!fleetNameNotEmpty) {
errorMessage = stringConstants.fleet() + " " + (index + 1) + ": " + stringConstants.pleaseEnterAName();
} else if (!fleetUnique) {
errorMessage = stringConstants.fleet() + " " + (index2 + 1) + ": " + stringConstants.fleetWithThisNameAlreadyExists();
}
}
return errorMessage;
}
}
public SeriesWithFleetsCreateDialog(Collection<SeriesDTO> existingSeries, StringMessages stringConstants,
DialogCallback<SeriesDTO> callback) {
super(stringConstants.series(), null, stringConstants.ok(), stringConstants.cancel(),
new SeriesParameterValidator(stringConstants, existingSeries), callback);
this.stringConstants = stringConstants;
this.series = new SeriesDTO();
nameEntryField = createTextBox(null);
nameEntryField.setVisibleLength(40);
isMedalSeriesCheckbox = createCheckbox(stringConstants.medalSeries());
fleetNameEntryFields = new ArrayList<TextBox>();
fleetColorEntryFields = new ArrayList<ListBox>();
fleetOrderNoEntryFields = new ArrayList<IntegerBox>();
fleetsGrid = new Grid(0, 0);
// create at least one fleet
addFleetWidget("Default", 0, null);
}
private Widget createFleetNameWidget(String defaultName) {
TextBox textBox = createTextBox(defaultName);
textBox.setVisibleLength(40);
textBox.setWidth("175px");
fleetNameEntryFields.add(textBox);
return textBox;
}
private Widget createFleetColorWidget(Color defaultColor) {
final ListBox listBox = createListBox(false);
final int fleetIndex = fleetNameEntryFields.size()-1;
listBox.addChangeHandler(new ChangeHandler() {
@Override
public void onChange(ChangeEvent event) {
// set default order no of the selected color
int selIndex = listBox.getSelectedIndex();
IntegerBox orderNoBox = fleetOrderNoEntryFields.get(fleetIndex);
TextBox nameBox = fleetNameEntryFields.get(fleetIndex);
if (selIndex == 0) {
orderNoBox.setValue(0);
} else {
String value = listBox.getValue(selIndex);
final FleetColors color = FleetColors.valueOf(value);
if (color != null) {
orderNoBox.setValue(color.getDefaultOrderNo());
nameBox.setValue(""+color.name().charAt(0)+color.name().toLowerCase().substring(1));
}
}
validate();
}
});
listBox.addItem(stringConstants.noColor());
for(FleetColors value: FleetColors.values()) {
listBox.addItem(value.name());
}
if(defaultColor == null) {
listBox.setSelectedIndex(0);
}
fleetColorEntryFields.add(listBox);
return listBox;
}
private Widget createFleetOrderNoWidget(int defaultValue) {
IntegerBox intBox = createIntegerBox(defaultValue, 3);
fleetOrderNoEntryFields.add(intBox);
return intBox;
}
@Override
protected SeriesDTO getResult() {
series.name = nameEntryField.getText();
series.setMedal(isMedalSeriesCheckbox.getValue());
List<FleetDTO> fleets = new ArrayList<FleetDTO>();
int fleetsCount = fleetNameEntryFields.size();
for(int i = 0; i < fleetsCount; i++) {
FleetDTO fleetDTO = new FleetDTO();
fleetDTO.name = fleetNameEntryFields.get(i).getValue();
fleetDTO.setColor(getSelectedColor(fleetColorEntryFields.get(i)));
int orderNo = -1;
if(fleetOrderNoEntryFields.get(i).getValue() != null) {
orderNo = fleetOrderNoEntryFields.get(i).getValue();
}
fleetDTO.setOrderNo(orderNo);
fleets.add(fleetDTO);
}
series.setFleets(fleets);
return series;
}
private Color getSelectedColor(ListBox colorListBox) {
Color result = null;
int selIndex = colorListBox.getSelectedIndex();
// the zero index represents the 'no color' option
if(selIndex > 0) {
String value = colorListBox.getValue(selIndex);
for(FleetColors color: FleetColors.values()) {
if(color.name().equals(value)) {
result = color.getColor();
break;
}
}
}
return result;
}
@Override
protected Widget getAdditionalWidget() {
final VerticalPanel panel = new VerticalPanel();
Widget additionalWidget = super.getAdditionalWidget();
if (additionalWidget != null) {
panel.add(additionalWidget);
}
Grid formGrid = new Grid(2, 2);
panel.add(formGrid);
formGrid.setWidget(0, 0, new Label(stringConstants.name() + ":"));
formGrid.setWidget(0, 1, nameEntryField);
formGrid.setWidget(1, 1, isMedalSeriesCheckbox);
panel.add(createHeadlineLabel(stringConstants.fleets()));
panel.add(fleetsGrid);
Button addFleetButton = new Button(stringConstants.addFleet());
addFleetButton.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
addFleetWidget(null, 0, null);
updateFleetsGrid(panel);
}
});
panel.add(addFleetButton);
updateFleetsGrid(panel);
return panel;
}
private void addFleetWidget(String fleetName, int ordering, Color color) {
createFleetNameWidget(fleetName);
createFleetOrderNoWidget(ordering);
createFleetColorWidget(color);
}
private void updateFleetsGrid(VerticalPanel parentPanel) {
int widgetIndex = parentPanel.getWidgetIndex(fleetsGrid);
parentPanel.remove(fleetsGrid);
int fleetCount = fleetNameEntryFields.size();
fleetsGrid = new Grid(fleetCount + 1, 3);
fleetsGrid.setCellSpacing(4);
fleetsGrid.setHTML(0, 0, stringConstants.color());
fleetsGrid.setHTML(0, 1, stringConstants.name());
fleetsGrid.setHTML(0, 2, stringConstants.rank());
for(int i = 0; i < fleetCount; i++) {
fleetsGrid.setWidget(i+1, 0, fleetColorEntryFields.get(i));
fleetsGrid.setWidget(i+1, 1, fleetNameEntryFields.get(i));
fleetsGrid.setWidget(i+1, 2, fleetOrderNoEntryFields.get(i));
}
parentPanel.insert(fleetsGrid, widgetIndex);
}
@Override
public void show() {
super.show();
nameEntryField.setFocus(true);
}
}
@@ -297,7 +297,7 @@ public class TrackedRacesListComposite extends SimplePanel implements Component<
TextColumn<RaceDTO> hasWindDataColumn = new TextColumn<RaceDTO>() {
@Override
public String getValue(RaceDTO raceDTO) {
if (raceDTO.isTracked && raceDTO.trackedRace.hasWindData == true)
if (raceDTO.isTracked && raceDTO.trackedRace != null && raceDTO.trackedRace.hasWindData == true)
return stringMessages.yes();
else
return stringMessages.no();
@@ -307,7 +307,7 @@ public class TrackedRacesListComposite extends SimplePanel implements Component<
TextColumn<RaceDTO> hasGPSDataColumn = new TextColumn<RaceDTO>() {
@Override
public String getValue(RaceDTO raceDTO) {
if (raceDTO.isTracked && raceDTO.trackedRace.hasGPSData == true)
if (raceDTO.isTracked && raceDTO.trackedRace != null && raceDTO.trackedRace.hasGPSData == true)
return stringMessages.yes();
else
return stringMessages.no();
@@ -32,6 +32,7 @@ import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
import com.sap.sailing.domain.common.WindSource;
import com.sap.sailing.domain.common.WindSourceType;
import com.sap.sailing.gwt.ui.actions.AsyncActionsExecutor;
import com.sap.sailing.gwt.ui.client.DataEntryDialog.DialogCallback;
import com.sap.sailing.gwt.ui.client.ErrorReporter;
import com.sap.sailing.gwt.ui.client.RaceSelectionChangeListener;
import com.sap.sailing.gwt.ui.client.RaceSelectionModel;
@@ -215,13 +216,13 @@ public class WindPanel extends FormPanel implements RegattaDisplayer, WindShower
private void showWindSettingDialog(RaceDTO race, CourseDTO course) {
WindSettingDialog windSettingDialog = new WindSettingDialog(race, course, stringMessages,
new AsyncCallback<WindDTO>() {
new DialogCallback<WindDTO>() {
@Override
public void onFailure(Throwable caught) {
public void cancel() {
}
@Override
public void onSuccess(final WindDTO result) {
public void ok(final WindDTO result) {
addWindFix(result);
}
});
@@ -1,6 +1,5 @@
package com.sap.sailing.gwt.ui.adminconsole;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.DoubleBox;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.Label;
@@ -55,7 +54,7 @@ public class WindSettingDialog extends DataEntryDialog<WindDTO> {
}
}
public WindSettingDialog(RaceDTO race, CourseDTO courseDTO, StringMessages stringMessages, AsyncCallback<WindDTO> callback) {
public WindSettingDialog(RaceDTO race, CourseDTO courseDTO, StringMessages stringMessages, DialogCallback<WindDTO> callback) {
super(stringMessages.actionAddWindData(), null, stringMessages.ok(), stringMessages.cancel(), new WindDataValidator(stringMessages), callback);
this.stringMessages = stringMessages;
speedInKnotsBox = createDoubleBox(5);
@@ -49,6 +49,11 @@ public abstract class DataEntryDialog<T> {
*/
String getErrorMessage(T valueToValidate);
}
public static interface DialogCallback<T> {
void ok(T editedObject);
void cancel();
}
/**
* @param validator
@@ -60,7 +65,7 @@ public abstract class DataEntryDialog<T> {
* {@link AsyncCallback#onSuccess(Object) confirmed}
*/
public DataEntryDialog(String title, String message, String okButtonName, String cancelButtonName,
Validator<T> validator, final AsyncCallback<T> callback) {
Validator<T> validator, final DialogCallback<T> callback) {
this(title, message, okButtonName, cancelButtonName, validator, /* animationEnabled */ true, callback);
}
@@ -74,7 +79,7 @@ public abstract class DataEntryDialog<T> {
* {@link AsyncCallback#onSuccess(Object) confirmed}
*/
public DataEntryDialog(String title, String message, String okButtonName, String cancelButtonName,
Validator<T> validator, boolean animationEnabled, final AsyncCallback<T> callback) {
Validator<T> validator, boolean animationEnabled, final DialogCallback<T> callback) {
dateEntryDialog = new DialogBox();
dateEntryDialog.setText(title);
dateEntryDialog.setAnimationEnabled(animationEnabled);
@@ -103,14 +108,14 @@ public abstract class DataEntryDialog<T> {
@Override
public void onClick(ClickEvent event) {
dateEntryDialog.hide();
callback.onFailure(null);
callback.cancel();
}
});
dateEntryDialog.setWidget(dialogFPanel);
okButton.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
dateEntryDialog.hide();
callback.onSuccess(getResult());
callback.ok(getResult());
}
});
}
@@ -18,8 +18,8 @@ public class RaceTimePanel extends TimePanel<RaceTimePanelSettings> implements R
private boolean autoAdjustPlayMode;
private RaceTimesInfoDTO lastRaceTimesInfo;
public RaceTimePanel(Timer timer, StringMessages stringMessages, RaceTimesInfoProvider raceTimesInfoProvider) {
super(timer, stringMessages);
public RaceTimePanel(Timer timer, TimeRangeProvider timeRangeProvider, StringMessages stringMessages, RaceTimesInfoProvider raceTimesInfoProvider) {
super(timer, timeRangeProvider, stringMessages);
this.raceTimesInfoProvider = raceTimesInfoProvider;
selectedRace = null;
autoAdjustPlayMode = true;
@@ -66,7 +66,7 @@ public class RaceTimePanel extends TimePanel<RaceTimePanelSettings> implements R
timer.setPlayMode(PlayModes.Live);
}
boolean timerAlreadyInitialized = getMin() != null && getMax() != null && timeSlider.getCurrentValue() != null;
boolean timerAlreadyInitialized = getFromTime() != null && getToTime() != null && timeSlider.getCurrentValue() != null;
if (!isTimeZoomed) {
updateMinMax(raceTimesInfo);
if (!timerAlreadyInitialized) {
@@ -83,7 +83,7 @@ public class RaceTimePanel extends TimePanel<RaceTimePanelSettings> implements R
}
@Override
public void onTimeZoom(Date zoomStartTimepoint, Date zoomEndTimepoint) {
public void onTimeZoomChanged(Date zoomStartTimepoint, Date zoomEndTimepoint) {
isTimeZoomed = true;
timeSlider.setZoomed(true);
timer.setAutoAdvance(false);
@@ -151,7 +151,7 @@ public class RaceTimePanel extends TimePanel<RaceTimePanelSettings> implements R
Date max = raceMinMax.getB();
// never reduce max if it was already set
if (min != null && max != null && (getMax() == null || getMax().before(max))) {
if (min != null && max != null && (getToTime() == null || getToTime().before(max))) {
setMinMax(min, max, /* fireEvent */ false); // no event because we guarantee time to be between min and max
}
}
@@ -31,6 +31,7 @@ import com.sap.sailing.gwt.ui.shared.ManeuverDTO;
import com.sap.sailing.gwt.ui.shared.MultiCompetitorRaceDataDTO;
import com.sap.sailing.gwt.ui.shared.QuickRankDTO;
import com.sap.sailing.gwt.ui.shared.RaceBuoysDTO;
import com.sap.sailing.gwt.ui.shared.RaceColumnInSeriesDTO;
import com.sap.sailing.gwt.ui.shared.RaceDTO;
import com.sap.sailing.gwt.ui.shared.RaceMapDataDTO;
import com.sap.sailing.gwt.ui.shared.RaceTimesInfoDTO;
@@ -141,19 +142,17 @@ public interface SailingService extends RemoteService {
void removeRegatta(RegattaIdentifier regattaIdentifier);
void addColumnsToSeries(RegattaIdentifier regattaIdentifier, String seriesName, List<String> columnNames);
List<RaceColumnInSeriesDTO> addRaceColumnsToSeries(RegattaIdentifier regattaIdentifier, String seriesName, List<String> columnNames);
void addColumnToSeries(RegattaIdentifier regattaIdentifier, String seriesName, String columnName);
RaceColumnInSeriesDTO addRaceColumnToSeries(RegattaIdentifier regattaIdentifier, String seriesName, String columnName);
void removeColumnsFromSeries(RegattaIdentifier regattaIdentifier, String seriesName, List<String> columnNames);
void removeRaceColumnsFromSeries(RegattaIdentifier regattaIdentifier, String seriesName, List<String> columnNames);
void removeColumnFromSeries(RegattaIdentifier regattaIdentifier, String seriesName, String columnName);
void removeRaceColumnFromSeries(RegattaIdentifier regattaIdentifier, String seriesName, String columnName);
void renameColumnInSeries(RegattaIdentifier regattaIdentifier, String seriesName, String oldColumnName, String newColumnName);
void moveRaceColumnInSeriesUp(RegattaIdentifier regattaIdentifier, String seriesName, String columnName);
void moveColumnInSeriesUp(RegattaIdentifier regattaIdentifier, String seriesName, String columnName);
void moveColumnInSeriesDown(RegattaIdentifier regattaIdentifier, String seriesName, String columnName);
void moveRaceColumnInSeriesDown(RegattaIdentifier regattaIdentifier, String seriesName, String columnName);
boolean connectTrackedRaceToLeaderboardColumn(String leaderboardName, String raceColumnName,
String fleetName, RegattaAndRaceIdentifier raceIdentifier);
@@ -24,6 +24,7 @@ import com.sap.sailing.gwt.ui.shared.ControlPointDTO;
import com.sap.sailing.gwt.ui.shared.CourseDTO;
import com.sap.sailing.gwt.ui.shared.EventDTO;
import com.sap.sailing.gwt.ui.shared.RaceBuoysDTO;
import com.sap.sailing.gwt.ui.shared.RaceColumnInSeriesDTO;
import com.sap.sailing.gwt.ui.shared.RegattaDTO;
import com.sap.sailing.gwt.ui.shared.GPSFixDTO;
import com.sap.sailing.gwt.ui.shared.LeaderboardDTO;
@@ -370,30 +371,26 @@ public interface SailingServiceAsync {
void removeRegatta(RegattaIdentifier regattaIdentifier, AsyncCallback<Void> callback);
void addColumnToSeries(RegattaIdentifier regattaIdentifier, String seriesName, String columnName, AsyncCallback<Void> callback);
void addRaceColumnToSeries(RegattaIdentifier regattaIdentifier, String seriesName, String columnName,
AsyncCallback<RaceColumnInSeriesDTO> callback);
void removeColumnFromSeries(RegattaIdentifier regattaIdentifier, String seriesName, String columnName,
void removeRaceColumnFromSeries(RegattaIdentifier regattaIdentifier, String seriesName, String columnName,
AsyncCallback<Void> callback);
void renameColumnInSeries(RegattaIdentifier regattaIdentifier, String seriesName, String oldColumnName,
String newColumnName, AsyncCallback<Void> callback);
void moveColumnInSeriesUp(RegattaIdentifier regattaIdentifier, String seriesName, String columnName,
void moveRaceColumnInSeriesUp(RegattaIdentifier regattaIdentifier, String seriesName, String columnName,
AsyncCallback<Void> callback);
void moveColumnInSeriesDown(RegattaIdentifier regattaIdentifier, String seriesName, String columnName,
void moveRaceColumnInSeriesDown(RegattaIdentifier regattaIdentifier, String seriesName, String columnName,
AsyncCallback<Void> callback);
void createRegatta(
String regattaName,
String boatClassName,
void createRegatta(String regattaName, String boatClassName,
LinkedHashMap<String, Pair<List<Triple<String, Integer, Color>>, Boolean>> seriesNamesWithFleetNamesAndFleetOrderingAndMedal,
boolean persistent, ScoringSchemeType scoringSchemeType, AsyncCallback<RegattaDTO> callback);
void addColumnsToSeries(RegattaIdentifier regattaIdentifier, String seriesName, List<String> columnNames,
AsyncCallback<Void> callback);
void addRaceColumnsToSeries(RegattaIdentifier regattaIdentifier, String seriesName, List<String> columnNames,
AsyncCallback<List<RaceColumnInSeriesDTO>> callback);
void removeColumnsFromSeries(RegattaIdentifier regattaIdentifier, String seriesName, List<String> columnNames,
void removeRaceColumnsFromSeries(RegattaIdentifier regattaIdentifier, String seriesName, List<String> columnNames,
AsyncCallback<Void> callback);
void getScoreCorrectionProviderDTOs(AsyncCallback<Iterable<ScoreCorrectionProviderDTO>> callback);
@@ -173,6 +173,7 @@ public interface StringMessages extends Messages {
String delete();
String showCharts();
String raceWithThisNameAlreadyExists();
String raceWithThisNameAlreadyExistsInRegatta();
String headUp();
String bearAway();
String tack();
@@ -382,6 +383,7 @@ public interface StringMessages extends Messages {
String useOverallLeaderboard();
String overallStandings();
String scoringSchemeHighPointEssOverall();
String scoringSchemeHighPointLastBreaksTie();
String type();
String actionAddRaces();
String racesWithNamePrefix();
@@ -402,4 +404,5 @@ public interface StringMessages extends Messages {
String isPublic();
String unsuppress();
String suppressedCompetitors();
String pleaseSelectASeries();
}
@@ -168,7 +168,8 @@ no3LetterCodes=Unable to find the IOC 3-letter-codes.
add=Add
delete=Delete
showCharts=Show charts
raceWithThisNameAlreadyExists=A race with that name already exists
raceWithThisNameAlreadyExists=A race with that name already exists.
raceWithThisNameAlreadyExistsInRegatta=A race with that name already exists in this regatta.
headUp=Head up
bearAway=Bear away
tack=Tack
@@ -375,6 +376,7 @@ insertWaypointAfterSelected=Insert waypoint after selected
scoringSchemeLowPointSystem=Low Point System
scoringSchemeHighPointSystem=High Point System
scoringSchemeHighPointEssOverall=High Point System ESS Overall
scoringSchemeHighPointLastBreaksTie=High Point System, Last Race Breaks Tie
scoringSystem=Scoring system
createFlexibleLeaderboard=Create flexible leaderboard
createRegattaLeaderboard=Create regatta leaderboard
@@ -402,5 +404,6 @@ suppress=Suppress
isPublic=Is public
suppress=Suppress
unsuppress=Unsuppress
suppressedCompetitors=Suppressed Competitors
suppressedCompetitors=Suppressed Competitors
pleaseSelectASeries=Please select a series.
@@ -170,7 +170,8 @@ no3LetterCodes=IOC 3-Buchstaben-Codes können nicht gefunden werden.
add=Hinzufügen
delete=Löschen
showCharts=Grafische Ansicht
raceWithThisNameAlreadyExists=Ein Rennen mit diesem Namen existiert schon
raceWithThisNameAlreadyExists=Ein Rennen mit diesem Namen existiert schon.
raceWithThisNameAlreadyExistsInRegatta=Ein Rennen mit diesem Namen existiert schon in der Regatta.
headUp=anluven
bearAway=abfallen
tack=Wende
@@ -376,6 +377,7 @@ insertWaypointAfterSelected=Wegpunkt vor dem selektierten einfügen
scoringSchemeLowPointSystem=Low Point System
scoringSchemeHighPointSystem=High Point System
scoringSchemeHighPointEssOverall=High Point System ESS Gesamtwertung
scoringSchemeHighPointLastBreaksTie=High Point System, letztes Rennen entscheidet bei Gleichstand
scoringSystem=Bepunktungssystem
createFlexibleLeaderboard=Flexible Rangliste anlegen
createRegattaLeaderboard=Rangliste für Regatta anlegen
@@ -404,4 +406,4 @@ isPublic=Öffentlich zugänglich
suppress=Entfernen
unsuppress=Wieder hinzufügen
suppressedCompetitors=Unterdrückte Wettkampfteilnehmer
pleaseSelectASeries=Bitte wählen Sie eine Serie aus.
@@ -1,6 +1,5 @@
package com.sap.sailing.gwt.ui.client;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.Widget;
@@ -15,7 +14,7 @@ public class TextfieldEntryDialog extends DataEntryDialog<String> {
private final TextBox entryField;
public TextfieldEntryDialog(String title, String message, String okButtonName, String cancelButtonName,
String initialValue, final Validator<String> validator, final AsyncCallback<String> callback) {
String initialValue, final Validator<String> validator, final DialogCallback<String> callback) {
super(title, message, okButtonName, cancelButtonName, validator, callback);
entryField = createTextBox(initialValue);
}
@@ -1,6 +1,5 @@
package com.sap.sailing.gwt.ui.client;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;
@@ -12,7 +11,7 @@ public class TextfieldEntryDialogWithCheckbox extends DataEntryDialog<Pair<Strin
private final TextBox entryField;
public TextfieldEntryDialogWithCheckbox(String title, String message, String okButtonName, String cancelButtonName,
String checkboxLabel, String initialValue, Validator<Pair<String, Boolean>> validator, AsyncCallback<Pair<String, Boolean>> callback) {
String checkboxLabel, String initialValue, Validator<Pair<String, Boolean>> validator, DialogCallback<Pair<String, Boolean>> callback) {
super(title, message, okButtonName, cancelButtonName, validator, callback);
checkbox = createCheckbox(checkboxLabel);
entryField = createTextBox(initialValue);
@@ -31,23 +31,10 @@ import com.sap.sailing.gwt.ui.shared.controls.slider.SliderBar;
import com.sap.sailing.gwt.ui.shared.controls.slider.TimeSlider;
public class TimePanel<T extends TimePanelSettings> extends FormPanel implements Component<T>, TimeListener, TimeZoomChangeListener,
PlayStateListener, RequiresResize {
TimeRangeChangeListener, PlayStateListener, RequiresResize {
protected final Timer timer;
protected boolean isTimeZoomed;
/**
* The start time point of the time interval visualized by this time panel. May be <code>null</code> if not yet initialized.
*
* @see #setMinMax(Date, Date, boolean)
*/
private Date min;
/**
* The end time point of the time interval visualized by this time panel. May be <code>null</code> if not yet initialized.
*
* @see #setMinMax(Date, Date, boolean)
*/
private Date max;
protected final TimeRangeProvider timeRangeProvider;
private final IntegerBox playSpeedBox;
private final Label timeDelayLabel;
@@ -67,7 +54,7 @@ public class TimePanel<T extends TimePanelSettings> extends FormPanel implements
private final Image playModeImage;
protected Date lastReceivedDataTimepoint;
private final Button slowDownButton;
private final Button speedUpButton;
private final Button speedUpButton;
/**
* The live delay may be adjusted automatically if the server decides so. However, if the user explicitly sets a live delay,
@@ -96,12 +83,14 @@ public class TimePanel<T extends TimePanelSettings> extends FormPanel implements
}
}
public TimePanel(Timer timer, StringMessages stringMessages) {
public TimePanel(Timer timer, TimeRangeProvider timeRangeProvider, StringMessages stringMessages) {
this.timer = timer;
this.timeRangeProvider = timeRangeProvider;
this.stringMessages = stringMessages;
isTimeZoomed = false;
timer.addTimeListener(this);
timer.addPlayStateListener(this);
timer.addPlayStateListener(this);
timeRangeProvider.addTimeRangeChangeListener(this);
userExplicitlyChangedLivePlayDelay = false;
FlowPanel timePanelInnerWrapper = new FlowPanel();
timePanelInnerWrapper.setStyleName("timePanelInnerWrapper");
@@ -296,16 +285,21 @@ public class TimePanel<T extends TimePanelSettings> extends FormPanel implements
@Override
public void timeChanged(Date time) {
if (getMin() != null && getMax() != null) {
// Handle also the case where time advances beyond slider's end.
// Handle it equally for replay and live mode for robustness reasons. This at least allows a user
// to watch on even if the time panel was off in its assumptions about race end and end of tracking.
if (time.after(getMax())) {
Date newMaxTime = new Date(time.getTime());
if (newMaxTime.getTime() - getMax().getTime() < MINIMUM_AUTO_ADVANCE_TIME_IN_MS) {
newMaxTime.setTime(getMax().getTime() + MINIMUM_AUTO_ADVANCE_TIME_IN_MS);
if (getFromTime() != null && getToTime() != null) {
// handle the case where time advances beyond slider's end.
if (time.after(getToTime())) {
switch (timer.getPlayMode()) {
case Live:
Date newMaxTime = new Date(time.getTime());
if (newMaxTime.getTime() - getToTime().getTime() < MINIMUM_AUTO_ADVANCE_TIME_IN_MS) {
newMaxTime.setTime(getToTime().getTime() + MINIMUM_AUTO_ADVANCE_TIME_IN_MS);
}
setMinMax(getFromTime(), newMaxTime, /* fireEvent */ false); // no event because we guarantee that time is between min/max
break;
case Replay:
timer.stop();
break;
}
setMinMax(getMin(), newMaxTime, /* fireEvent */ false); // no event because we guarantee that time is between min/max
}
long t = time.getTime();
timeSlider.setCurrentValue(new Double(t), false);
@@ -318,12 +312,12 @@ public class TimePanel<T extends TimePanelSettings> extends FormPanel implements
}
}
protected Date getMin() {
return min;
protected Date getFromTime() {
return timeRangeProvider.getFromTime();
}
protected Date getMax() {
return max;
protected Date getToTime() {
return timeRangeProvider.getToTime();
}
/**
@@ -334,20 +328,20 @@ public class TimePanel<T extends TimePanelSettings> extends FormPanel implements
assert min != null && max != null;
boolean changed = false;
if (!max.equals(this.max)) {
if (!max.equals(timeRangeProvider.getToTime())) {
changed = true;
this.max = max;
timeSlider.setMaxValue(new Double(max.getTime()), fireEvent);
}
if (!min.equals(this.min)) {
if (!min.equals(timeRangeProvider.getFromTime())) {
changed = true;
this.min = min;
timeSlider.setMinValue(new Double(min.getTime()), fireEvent);
if (timeSlider.getCurrentValue() == null) {
timeSlider.setCurrentValue(new Double(min.getTime()), fireEvent);
}
}
if (changed) {
timeRangeProvider.setTimeRange(min, max, this);
int numSteps = timeSlider.getElement().getClientWidth();
if (numSteps > 0) {
timeSlider.setStepSize(numSteps, fireEvent);
@@ -379,6 +373,7 @@ public class TimePanel<T extends TimePanelSettings> extends FormPanel implements
case Paused:
playPauseButton.getElement().removeClassName("playPauseButtonPause");
case Stopped:
playPauseButton.getElement().removeClassName("playPauseButtonPause");
playModeImage.setResource(playModeInactiveImg);
break;
}
@@ -406,13 +401,17 @@ public class TimePanel<T extends TimePanelSettings> extends FormPanel implements
}
@Override
public void onTimeZoom(Date zoomStartTimepoint, Date zoomEndTimepoint) {
public void onTimeZoomChanged(Date zoomStartTimepoint, Date zoomEndTimepoint) {
}
@Override
public void onTimeZoomReset() {
}
@Override
public void onTimeRangeChanged(Date fromTime, Date toTime) {
}
protected boolean isLiveModeToBeMadePossible() {
return false;
}
@@ -0,0 +1,7 @@
package com.sap.sailing.gwt.ui.client;
import java.util.Date;
public interface TimeRangeChangeListener {
void onTimeRangeChanged(Date fromTime, Date toTime);
}
@@ -0,0 +1,60 @@
package com.sap.sailing.gwt.ui.client;
import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import com.sap.sailing.domain.common.impl.Util.Pair;
public class TimeRangeModel implements TimeRangeProvider {
/**
* The 'from' time of the time interval. May be <code>null</code> if not yet initialized.
*/
protected Date fromTime;
/**
* The 'to' time of the time interval. May be <code>null</code> if not yet initialized.
*/
protected Date toTime;
protected final Set<TimeRangeChangeListener> listeners;
public TimeRangeModel() {
listeners = new HashSet<TimeRangeChangeListener>();
}
@Override
public void addTimeRangeChangeListener(TimeRangeChangeListener listener) {
listeners.add(listener);
}
@Override
public void removeTimeRangeChangeListener(TimeRangeChangeListener listener) {
listeners.remove(listener);
}
public void setTimeRange(final Date fromTime, final Date toTime, TimeRangeChangeListener... listenersNotToNotify) {
this.fromTime = fromTime != null ? new Date(fromTime.getTime()) : null;
this.toTime = toTime != null ? new Date(toTime.getTime()) : null;
for (TimeRangeChangeListener listener : listeners) {
if (listenersNotToNotify == null || !Arrays.asList(listenersNotToNotify).contains(listener)) {
listener.onTimeRangeChanged(fromTime, toTime);
}
}
}
public Pair<Date, Date> getTimeRange() {
return new Pair<Date, Date>(fromTime, toTime);
}
public Date getFromTime() {
return fromTime;
}
public Date getToTime() {
return toTime;
}
}
@@ -0,0 +1,20 @@
package com.sap.sailing.gwt.ui.client;
import java.util.Date;
import com.sap.sailing.domain.common.impl.Util.Pair;
public interface TimeRangeProvider {
public void addTimeRangeChangeListener(TimeRangeChangeListener listener);
public void removeTimeRangeChangeListener(TimeRangeChangeListener listener);
public void setTimeRange(Date fromTime, Date toTime, TimeRangeChangeListener... listenersNotToNotify);
public Date getFromTime();
public Date getToTime();
public Pair<Date, Date> getTimeRange();
}
@@ -7,14 +7,14 @@ import java.util.Set;
import com.sap.sailing.domain.common.impl.Util.Pair;
public class TimeZoomModel implements TimeZoomProvider {
public class TimeRangeWithZoomModel extends TimeRangeModel implements TimeRangeWithZoomProvider {
private Date zoomStartTimepoint;
private Date zoomEndTimepoint;
private boolean isTimeZoomed;
private final Set<TimeZoomChangeListener> listeners;
public TimeZoomModel() {
public TimeRangeWithZoomModel() {
isTimeZoomed = false;
listeners = new HashSet<TimeZoomChangeListener>();
}
@@ -35,7 +35,7 @@ public class TimeZoomModel implements TimeZoomProvider {
this.isTimeZoomed = true;
for (TimeZoomChangeListener listener : listeners) {
if (listenersNotToNotify == null || !Arrays.asList(listenersNotToNotify).contains(listener)) {
listener.onTimeZoom(zoomStartTimepoint, zoomEndTimepoint);
listener.onTimeZoomChanged(zoomStartTimepoint, zoomEndTimepoint);
}
}
}
@@ -4,7 +4,7 @@ import java.util.Date;
import com.sap.sailing.domain.common.impl.Util.Pair;
public interface TimeZoomProvider {
public interface TimeRangeWithZoomProvider extends TimeRangeProvider {
public void addTimeZoomChangeListener(TimeZoomChangeListener listener);
@@ -4,7 +4,7 @@ import java.util.Date;
public interface TimeZoomChangeListener {
void onTimeZoom(Date zoomStartTimepoint, Date zoomEndTimepoint);
void onTimeZoomChanged(Date zoomStartTimepoint, Date zoomEndTimepoint);
void onTimeZoomReset();
}
@@ -18,10 +18,9 @@ import com.sap.sailing.gwt.ui.client.CompetitorSelectionProvider;
import com.sap.sailing.gwt.ui.client.ErrorReporter;
import com.sap.sailing.gwt.ui.client.RaceSelectionModel;
import com.sap.sailing.gwt.ui.client.RaceSelectionProvider;
import com.sap.sailing.gwt.ui.client.RaceTimesInfoProvider;
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.client.TimeZoomModel;
import com.sap.sailing.gwt.ui.client.TimeRangeWithZoomModel;
import com.sap.sailing.gwt.ui.client.Timer;
import com.sap.sailing.gwt.ui.shared.charts.MultiChartPanel;
import com.sap.sailing.gwt.ui.shared.charts.MultiChartSettings;
@@ -37,8 +36,6 @@ public class CompareCompetitorsChartDialog extends DialogBoxExt {
private final RaceSelectionProvider raceSelectionProvider;
private final MultiChartPanel multiChartPanel;
private final RaceTimesInfoProvider raceTimesInfoProvider;
final ListBox racesListBox;
@@ -54,12 +51,9 @@ public class CompareCompetitorsChartDialog extends DialogBoxExt {
raceSelectionProvider = new RaceSelectionModel();
raceSelectionProvider.setAllRaces(races);
raceTimesInfoProvider = new RaceTimesInfoProvider(sailingService, errorReporter, races, 1000l);
multiChartPanel = new MultiChartPanel(sailingService, new AsyncActionsExecutor(), competitorSelectionProvider, raceSelectionProvider,
timer, new TimeZoomModel(), stringConstants, errorReporter, false, false);
timer, new TimeRangeWithZoomModel(), stringConstants, errorReporter, false, false);
multiChartPanel.setSize("100%", "100%");
raceTimesInfoProvider.addRaceTimesInfoProviderListener(multiChartPanel);
VerticalPanel contentPanel = new VerticalPanel();
contentPanel.setSize("100%", "100%");
@@ -12,6 +12,8 @@ public class ScoringSchemeTypeFormatter {
return stringConstants.scoringSchemeHighPointSystem();
case HIGH_POINT_ESS_OVERALL:
return stringConstants.scoringSchemeHighPointEssOverall();
case HIGH_POINT_LAST_BREAKS_TIE:
return stringConstants.scoringSchemeHighPointLastBreaksTie();
}
return null;
}
@@ -28,6 +28,7 @@ import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.safehtml.shared.SafeHtmlUtils;
import com.google.gwt.text.shared.SafeHtmlRenderer;
import com.google.gwt.user.cellview.client.CellTable;
import com.google.gwt.user.cellview.client.CellTable.Resources;
import com.google.gwt.user.cellview.client.Column;
import com.google.gwt.user.cellview.client.ColumnSortEvent.ListHandler;
import com.google.gwt.user.cellview.client.Header;
@@ -50,6 +51,7 @@ import com.sap.sailing.domain.common.impl.InvertibleComparatorAdapter;
import com.sap.sailing.domain.common.impl.Util.Pair;
import com.sap.sailing.domain.common.impl.Util.Triple;
import com.sap.sailing.gwt.ui.actions.AsyncActionsExecutor;
import com.sap.sailing.gwt.ui.adminconsole.AdminConsoleTableResources;
import com.sap.sailing.gwt.ui.client.Collator;
import com.sap.sailing.gwt.ui.client.CompetitorSelectionModel;
import com.sap.sailing.gwt.ui.client.ErrorReporter;
@@ -398,7 +400,7 @@ public class EditableLeaderboardPanel extends LeaderboardPanel {
private boolean isNetPointsCorrected() {
LeaderboardEntryDTO leaderboardEntryDTO = getCurrentlyRendering().fieldsByRaceColumnName.get(raceName);
return leaderboardEntryDTO.netPointsCorrected;
return leaderboardEntryDTO != null && leaderboardEntryDTO.netPointsCorrected;
}
@Override
@@ -523,7 +525,8 @@ public class EditableLeaderboardPanel extends LeaderboardPanel {
}
private CellTable<CompetitorDTO> createSuppressedCompetitorsTable() {
CellTable<CompetitorDTO> result = new CellTable<CompetitorDTO>();
final Resources tableResources = GWT.create(AdminConsoleTableResources.class);
CellTable<CompetitorDTO> result = new CellTable<CompetitorDTO>(10000, tableResources);
suppressedCompetitorsShown.addDataDisplay(result);
final SuppressedSailIDColumn suppressedSailIDColumn = new SuppressedSailIDColumn();
suppressedSailIDColumn.setSortable(true);
@@ -378,7 +378,7 @@ public class MatchAndApplyScoreCorrectionsDialog extends DataEntryDialog<BulkSco
}
}
private static class Callback implements AsyncCallback<BulkScoreCorrectionDTO> {
private static class Callback implements DialogCallback<BulkScoreCorrectionDTO> {
private final SailingServiceAsync sailingService;
private final StringMessages stringMessages;
private final ErrorReporter errorReporter;
@@ -393,12 +393,12 @@ public class MatchAndApplyScoreCorrectionsDialog extends DataEntryDialog<BulkSco
}
@Override
public void onFailure(Throwable caught) {
public void cancel() {
// user has canceled the dialog
}
@Override
public void onSuccess(final BulkScoreCorrectionDTO result) {
public void ok(final BulkScoreCorrectionDTO result) {
leaderboardPanel.getBusyIndicator().setBusy(true);
sailingService.updateLeaderboardScoreCorrectionsAndMaxPointsReasons(result, new AsyncCallback<Void>() {
@Override
@@ -43,7 +43,7 @@ public class ResultSelectionAndApplyDialog extends DataEntryDialog<Triple<String
}
}
private static class Callback implements AsyncCallback<Triple<String, String, Pair<String, Date>>> {
private static class Callback implements DialogCallback<Triple<String, String, Pair<String, Date>>> {
private final EditableLeaderboardPanel leaderboardPanel;
private final SailingServiceAsync sailingService;
private final StringMessages stringMessages;
@@ -58,12 +58,12 @@ public class ResultSelectionAndApplyDialog extends DataEntryDialog<Triple<String
}
@Override
public void onFailure(Throwable caught) {
public void cancel() {
// don't do anything; dialog was canceled
}
@Override
public void onSuccess(Triple<String, String, Pair<String, Date>> result) {
public void ok(Triple<String, String, Pair<String, Date>> result) {
final String scoreCorrectionProviderName = result.getA();
final String eventName = result.getB();
final String boatClassName = result.getC().getA();
@@ -33,7 +33,7 @@ import com.sap.sailing.gwt.ui.client.RegattaDisplayer;
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.client.TimeListener;
import com.sap.sailing.gwt.ui.client.TimeZoomModel;
import com.sap.sailing.gwt.ui.client.TimeRangeWithZoomModel;
import com.sap.sailing.gwt.ui.client.Timer;
import com.sap.sailing.gwt.ui.client.UserAgentDetails;
import com.sap.sailing.gwt.ui.leaderboard.LeaderboardPanel;
@@ -84,7 +84,7 @@ public class RaceBoardPanel extends FormPanel implements RegattaDisplayer, RaceS
private final RaceSelectionProvider raceSelectionProvider;
private final UserAgentDetails userAgent;
private final CompetitorSelectionModel competitorSelectionModel;
private final TimeZoomModel timeZoomModel;
private final TimeRangeWithZoomModel timeRangeWithZoomModel;
private final RegattaAndRaceIdentifier selectedRaceIdentifier;
private LeaderboardPanel leaderboardPanel;
@@ -123,7 +123,7 @@ public class RaceBoardPanel extends FormPanel implements RegattaDisplayer, RaceS
setWidget(mainPanel);
this.timer = timer;
timeZoomModel = new TimeZoomModel();
timeRangeWithZoomModel = new TimeRangeWithZoomModel();
componentViewers = new ArrayList<ComponentViewer>();
competitorSelectionModel = new CompetitorSelectionModel(/* hasMultiSelection */ true);
@@ -138,8 +138,8 @@ public class RaceBoardPanel extends FormPanel implements RegattaDisplayer, RaceS
break;
}
timePanel = new RaceTimePanel(timer, stringMessages, raceTimesInfoProvider);
timeZoomModel.addTimeZoomChangeListener(timePanel);
timePanel = new RaceTimePanel(timer, timeRangeWithZoomModel, stringMessages, raceTimesInfoProvider);
timeRangeWithZoomModel.addTimeZoomChangeListener(timePanel);
raceTimesInfoProvider.addRaceTimesInfoProviderListener(timePanel);
raceSelectionProvider.addRaceSelectionChangeListener(timePanel);
timePanel.onRaceSelectionChange(raceSelectionProvider.getSelectedRaces());
@@ -155,16 +155,14 @@ public class RaceBoardPanel extends FormPanel implements RegattaDisplayer, RaceS
List<Component<?>> components = new ArrayList<Component<?>>();
competitorChart = new MultiChartPanel(sailingService, asyncActionsExecutor, competitorSelectionModel, raceSelectionProvider,
timer, timeZoomModel, stringMessages, errorReporter, true, true);
timer, timeRangeWithZoomModel, stringMessages, errorReporter, true, true);
competitorChart.setVisible(false);
raceTimesInfoProvider.addRaceTimesInfoProviderListener(competitorChart);
competitorChart.onRaceSelectionChange(raceSelectionProvider.getSelectedRaces());
components.add(competitorChart);
windChart = new WindChart(sailingService, raceSelectionProvider, timer, timeZoomModel, new WindChartSettings(),
windChart = new WindChart(sailingService, raceSelectionProvider, timer, timeRangeWithZoomModel, new WindChartSettings(),
stringMessages, asyncActionsExecutor, errorReporter, /* compactChart */ true);
windChart.setVisible(false);
raceTimesInfoProvider.addRaceTimesInfoProviderListener(windChart);
windChart.onRaceSelectionChange(raceSelectionProvider.getSelectedRaces());
components.add(windChart);
@@ -264,6 +264,11 @@ public class LeaderboardDTOCache {
removeFromCache(leaderboard);
}
@Override
public boolean canAddRaceColumnToContainer(RaceColumn raceColumn) {
return true;
}
@Override
public void raceColumnAddedToContainer(RaceColumn raceColumn) {
removeFromCache(leaderboard);
@@ -152,6 +152,7 @@ import com.sap.sailing.gwt.ui.shared.PositionDTO;
import com.sap.sailing.gwt.ui.shared.QuickRankDTO;
import com.sap.sailing.gwt.ui.shared.RaceBuoysDTO;
import com.sap.sailing.gwt.ui.shared.RaceColumnDTO;
import com.sap.sailing.gwt.ui.shared.RaceColumnInSeriesDTO;
import com.sap.sailing.gwt.ui.shared.RaceDTO;
import com.sap.sailing.gwt.ui.shared.RaceMapDataDTO;
import com.sap.sailing.gwt.ui.shared.RaceTimesInfoDTO;
@@ -197,7 +198,6 @@ import com.sap.sailing.server.operationaltransformation.RemoveLeaderboard;
import com.sap.sailing.server.operationaltransformation.RemoveLeaderboardColumn;
import com.sap.sailing.server.operationaltransformation.RemoveLeaderboardGroup;
import com.sap.sailing.server.operationaltransformation.RemoveRegatta;
import com.sap.sailing.server.operationaltransformation.RenameColumnInSeries;
import com.sap.sailing.server.operationaltransformation.RenameEvent;
import com.sap.sailing.server.operationaltransformation.RenameLeaderboard;
import com.sap.sailing.server.operationaltransformation.RenameLeaderboardColumn;
@@ -459,7 +459,8 @@ public class SailingServiceImpl extends RemoteServiceServlet implements SailingS
result.setSuppressed(convertToCompetitorDTO(suppressedCompetitor), true);
}
for (RaceColumn raceColumn : leaderboard.getRaceColumns()) {
RaceColumnDTO raceColumnDTO = result.createEmptyRaceColumn(raceColumn.getName(), raceColumn.isMedalRace());
RaceColumnDTO raceColumnDTO = result.createEmptyRaceColumn(raceColumn.getName(), raceColumn.isMedalRace(),
leaderboard.getScoringScheme().isValidInTotalScore(leaderboard, raceColumn, timePoint));
for (Fleet fleet : raceColumn.getFleets()) {
RegattaAndRaceIdentifier raceIdentifier = null;
TrackedRace trackedRace = raceColumn.getTrackedRace(fleet);
@@ -468,6 +469,8 @@ public class SailingServiceImpl extends RemoteServiceServlet implements SailingS
raceIdentifier = new RegattaNameAndRaceName(trackedRace.getTrackedRegatta().getRegatta()
.getName(), trackedRace.getRace().getName());
}
// Note: the RaceColumnDTO won't be created by the following addRace call because it has been created
// above by the result.createEmptyRaceColumn call
result.addRace(raceColumn.getName(), fleetDTO, raceColumn.isMedalRace(),
raceIdentifier, /* StrippedRaceDTO */ null);
}
@@ -876,7 +879,7 @@ public class SailingServiceImpl extends RemoteServiceServlet implements SailingS
}
List<RaceColumnDTO> raceColumns = new ArrayList<RaceColumnDTO>();
for (RaceColumnInSeries raceColumn : series.getRaceColumns()) {
RaceColumnDTO raceColumnDTO = new RaceColumnDTO();
RaceColumnDTO raceColumnDTO = new RaceColumnDTO(/* isValidInTotalScore not relevant here because no scores conveyed */ null);
raceColumnDTO.name = raceColumn.getName();
raceColumnDTO.setMedalRace(raceColumn.isMedalRace());
raceColumns.add(raceColumnDTO);
@@ -904,7 +907,8 @@ public class SailingServiceImpl extends RemoteServiceServlet implements SailingS
trackedRaceDTO.timePointOfNewestEvent = trackedRace.getTimePointOfNewestEvent() == null ? null : trackedRace.getTimePointOfNewestEvent().asDate();
trackedRaceDTO.delayToLiveInMs = trackedRace.getDelayToLiveInMillis();
}
RaceWithCompetitorsDTO raceDTO = new RaceWithCompetitorsDTO(raceIdentifier, convertToCompetitorDTOs(r.getCompetitors()), trackedRaceDTO);
RaceWithCompetitorsDTO raceDTO = new RaceWithCompetitorsDTO(raceIdentifier, convertToCompetitorDTOs(r.getCompetitors()),
trackedRaceDTO, getService().isRaceBeingTracked(r));
if (trackedRace != null) {
raceDTO.startOfRace = trackedRace.getStartOfRace() == null ? null : trackedRace.getStartOfRace().asDate();
raceDTO.endOfRace = trackedRace.getEndOfRace() == null ? null : trackedRace.getEndOfRace().asDate();
@@ -1901,8 +1905,8 @@ public class SailingServiceImpl extends RemoteServiceServlet implements SailingS
leaderboardDTO.hasCarriedPoints = leaderboard.hasCarriedPoints();
leaderboardDTO.discardThresholds = leaderboard.getResultDiscardingRule().getDiscardIndexResultsStartingWithHowManyRaces();
for (RaceColumn raceColumn : leaderboard.getRaceColumns()) {
RaceDTO raceDTO = null;
for (Fleet fleet : raceColumn.getFleets()) {
RaceDTO raceDTO = null;
RegattaAndRaceIdentifier raceIdentifier = null;
TrackedRace trackedRace = raceColumn.getTrackedRace(fleet);
if (trackedRace != null) {
@@ -1917,7 +1921,7 @@ public class SailingServiceImpl extends RemoteServiceServlet implements SailingS
trackedRaceDTO.startOfTracking = trackedRace.getStartOfTracking() == null ? null : trackedRace.getStartOfTracking().asDate();
trackedRaceDTO.hasWindData = trackedRace.hasWindData();
trackedRaceDTO.hasGPSData = trackedRace.hasGPSData();
raceDTO = new RaceDTO(raceIdentifier, trackedRaceDTO);
raceDTO = new RaceDTO(raceIdentifier, trackedRaceDTO, getService().isRaceBeingTracked(trackedRace.getRace()));
raceDTO.places = racePlaces;
raceDTO.startOfRace = trackedRace.getStartOfRace() == null ? null : trackedRace.getStartOfRace().asDate();
raceDTO.endOfRace = trackedRace.getEndOfRace() == null ? null : trackedRace.getEndOfRace().asDate();
@@ -2622,43 +2626,55 @@ public class SailingServiceImpl extends RemoteServiceServlet implements SailingS
getService().apply(new RemoveRegatta(regattaIdentifier));
}
private RaceColumnInSeriesDTO convertToRaceColumnInSeriesDTO(RaceColumnInSeries raceColumnInSeries) {
RaceColumnInSeriesDTO raceColumnInSeriesDTO = new RaceColumnInSeriesDTO(raceColumnInSeries.getSeries().getName(),
raceColumnInSeries.getRegatta().getName());
raceColumnInSeriesDTO.name = raceColumnInSeries.getName();
raceColumnInSeriesDTO.setMedalRace(raceColumnInSeries.isMedalRace());
return raceColumnInSeriesDTO;
}
@Override
public void addColumnsToSeries(RegattaIdentifier regattaIdentifier, String seriesName, List<String> columnNames) {
public List<RaceColumnInSeriesDTO> addRaceColumnsToSeries(RegattaIdentifier regattaIdentifier, String seriesName, List<String> columnNames) {
List<RaceColumnInSeriesDTO> result = new ArrayList<RaceColumnInSeriesDTO>();
for(String columnName: columnNames) {
getService().apply(new AddColumnToSeries(regattaIdentifier, seriesName, columnName));
RaceColumnInSeries raceColumnInSeries = getService().apply(new AddColumnToSeries(regattaIdentifier, seriesName, columnName));
if(raceColumnInSeries != null) {
result.add(convertToRaceColumnInSeriesDTO(raceColumnInSeries));
}
}
return result;
}
@Override
public void addColumnToSeries(RegattaIdentifier regattaIdentifier, String seriesName, String columnName) {
getService().apply(new AddColumnToSeries(regattaIdentifier, seriesName, columnName));
public RaceColumnInSeriesDTO addRaceColumnToSeries(RegattaIdentifier regattaIdentifier, String seriesName, String columnName) {
RaceColumnInSeriesDTO result = null;
RaceColumnInSeries raceColumnInSeries = getService().apply(new AddColumnToSeries(regattaIdentifier, seriesName, columnName));
if(raceColumnInSeries != null) {
result = convertToRaceColumnInSeriesDTO(raceColumnInSeries);
}
return result;
}
@Override
public void removeColumnsFromSeries(RegattaIdentifier regattaIdentifier, String seriesName, List<String> columnNames) {
public void removeRaceColumnsFromSeries(RegattaIdentifier regattaIdentifier, String seriesName, List<String> columnNames) {
for(String columnName: columnNames) {
getService().apply(new RemoveColumnFromSeries(regattaIdentifier, seriesName, columnName));
}
}
@Override
public void removeColumnFromSeries(RegattaIdentifier regattaIdentifier, String seriesName, String columnName) {
public void removeRaceColumnFromSeries(RegattaIdentifier regattaIdentifier, String seriesName, String columnName) {
getService().apply(new RemoveColumnFromSeries(regattaIdentifier, seriesName, columnName));
}
@Override
public void renameColumnInSeries(RegattaIdentifier regattaIdentifier, String seriesName, String oldColumnName,
String newColumnName) {
getService().apply(new RenameColumnInSeries(regattaIdentifier, seriesName, oldColumnName, newColumnName));
}
@Override
public void moveColumnInSeriesUp(RegattaIdentifier regattaIdentifier, String seriesName, String columnName) {
public void moveRaceColumnInSeriesUp(RegattaIdentifier regattaIdentifier, String seriesName, String columnName) {
getService().apply(new MoveColumnInSeriesUp(regattaIdentifier, seriesName, columnName));
}
@Override
public void moveColumnInSeriesDown(RegattaIdentifier regattaIdentifier, String seriesName, String columnName) {
public void moveRaceColumnInSeriesDown(RegattaIdentifier regattaIdentifier, String seriesName, String columnName) {
getService().apply(new MoveColumnInSeriesDown(regattaIdentifier, seriesName, columnName));
}
@@ -79,12 +79,12 @@ public abstract class AbstractLeaderboardDTO implements IsSerializable {
public Double getTotalPoints(LeaderboardRowDTO object) {
Double totalPoints = object.carriedPoints == null ? null : object.carriedPoints;
for (LeaderboardEntryDTO e : object.fieldsByRaceColumnName.values()) {
if(e.totalPoints != null) {
if(totalPoints == null) {
totalPoints = e.totalPoints;
for (Map.Entry<String, LeaderboardEntryDTO> e : object.fieldsByRaceColumnName.entrySet()) {
if (e.getValue().totalPoints != null && getOrCreateRaceColumn(e.getKey()).isValidInTotalScore()) {
if (totalPoints == null) {
totalPoints = e.getValue().totalPoints;
} else {
totalPoints += e.totalPoints;
totalPoints += e.getValue().totalPoints;
}
}
}
@@ -124,7 +124,7 @@ public abstract class AbstractLeaderboardDTO implements IsSerializable {
private RaceColumnDTO getOrCreateRaceColumn(String raceColumnName) {
RaceColumnDTO result = getRaceInLeaderboardByName(raceColumnName);
if (result == null) {
result = new RaceColumnDTO();
result = new RaceColumnDTO(/* isValidInTotalScore */ true);
result.name = raceColumnName;
races.add(result);
}
@@ -133,9 +133,10 @@ public abstract class AbstractLeaderboardDTO implements IsSerializable {
/**
* If the {@link RaceColumnDTO} by the name <code>raceColumnName</code> doesn't exist yet within this leaderboard
* DTO, it is created. This method ensures that a fleet named <code>fleetName</code> is present. If it's not present
* yet, it's added to the race column's fleet name list. The <code>trackedRaceIdentifier</code> and
* <code>race</code> are associated with the column for the fleet identified by <code>fleetName</code>.
* DTO, it is created, setting is {@link RaceColumnDTO#isValidInTotalScore()} to <code>true</code>. This method
* ensures that a fleet named <code>fleetName</code> is present. If it's not present yet, it's added to the race
* column's fleet name list. The <code>trackedRaceIdentifier</code> and <code>race</code> are associated with the
* column for the fleet identified by <code>fleetName</code>.
*
* @param fleetDTO
* must not be null
@@ -160,28 +161,8 @@ public abstract class AbstractLeaderboardDTO implements IsSerializable {
return raceColumnDTO;
}
/**
* A new {@link RaceColumnDTO} by the name <code>raceColumnName</code> is created and added to this leaderboard at
* position <code>index</code>. The single fleet <code>fleet</code> is added to the column. The
* <code>trackedRaceIdentifier</code> and <code>race</code> are associated with the column for the fleet identified
* by <code>fleetName</code>.
*
* @param fleet
* must not be null
*/
public void createRaceColumnAt(String raceColumnName, FleetDTO fleet, boolean medalRace,
RegattaAndRaceIdentifier trackedRaceIdentifier, int index) {
assert fleet != null;
RaceColumnDTO raceInLeaderboardDTO = new RaceColumnDTO();
raceInLeaderboardDTO.name = raceColumnName;
raceInLeaderboardDTO.setMedalRace(medalRace);
raceInLeaderboardDTO.addFleet(fleet);
raceInLeaderboardDTO.setRaceIdentifier(fleet, trackedRaceIdentifier);
races.add(index, raceInLeaderboardDTO);
}
public RaceColumnDTO createEmptyRaceColumn(String raceColumnName, boolean medalRace) {
RaceColumnDTO raceColumn = new RaceColumnDTO();
public RaceColumnDTO createEmptyRaceColumn(String raceColumnName, boolean medalRace, boolean isValidInTotalScore) {
RaceColumnDTO raceColumn = new RaceColumnDTO(isValidInTotalScore);
raceColumn.name = raceColumnName;
raceColumn.setMedalRace(medalRace);
races.add(raceColumn);
@@ -17,13 +17,21 @@ public class RaceColumnDTO extends NamedDTO implements IsSerializable {
private List<FleetDTO> fleets;
private Map<FleetDTO, RegattaAndRaceIdentifier> trackedRaceIdentifiersPerFleet;
private Map<FleetDTO, RaceDTO> racesPerFleet;
private Boolean isValidInTotalScore;
public RaceColumnDTO() {
RaceColumnDTO() {} // for GWT serialization
public RaceColumnDTO(Boolean isValidInTotalScore) {
this.isValidInTotalScore = isValidInTotalScore;
trackedRaceIdentifiersPerFleet = new HashMap<FleetDTO, RegattaAndRaceIdentifier>();
racesPerFleet = new HashMap<FleetDTO, RaceDTO>();
fleets = new ArrayList<FleetDTO>();
}
public boolean isValidInTotalScore() {
return isValidInTotalScore;
}
public String getRaceColumnName() {
return name;
}
@@ -0,0 +1,55 @@
package com.sap.sailing.gwt.ui.shared;
public class RaceColumnInSeriesDTO extends RaceColumnDTO {
private String seriesName;
private String regattaName;
// for GWT serialization
RaceColumnInSeriesDTO() {}
public RaceColumnInSeriesDTO(String seriesName, String regattaName) {
super();
this.seriesName = seriesName;
this.regattaName = regattaName;
}
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + ((regattaName == null) ? 0 : regattaName.hashCode());
result = prime * result + ((seriesName == null) ? 0 : seriesName.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!super.equals(obj))
return false;
if (getClass() != obj.getClass())
return false;
RaceColumnInSeriesDTO other = (RaceColumnInSeriesDTO) obj;
if (regattaName == null) {
if (other.regattaName != null)
return false;
} else if (!regattaName.equals(other.regattaName))
return false;
if (seriesName == null) {
if (other.seriesName != null)
return false;
} else if (!seriesName.equals(other.seriesName))
return false;
return true;
}
public String getSeriesName() {
return seriesName;
}
public String getRegattaName() {
return regattaName;
}
}
@@ -27,14 +27,14 @@ public class RaceDTO extends NamedDTO implements IsSerializable {
public RaceDTO() {}
public RaceDTO(RegattaAndRaceIdentifier raceIdentifier) {
this(raceIdentifier, null);
this(raceIdentifier, null, false);
}
public RaceDTO(RegattaAndRaceIdentifier raceIdentifier, TrackedRaceDTO trackedRace) {
public RaceDTO(RegattaAndRaceIdentifier raceIdentifier, TrackedRaceDTO trackedRace, boolean isCurrentlyTracked) {
super(raceIdentifier.getRaceName());
this.regattaName = raceIdentifier.getRegattaName();
this.trackedRace = trackedRace;
this.isTracked = trackedRace != null;
this.isTracked = isCurrentlyTracked;
}
public RegattaAndRaceIdentifier getRaceIdentifier() {
@@ -10,8 +10,9 @@ public class RaceWithCompetitorsDTO extends RaceDTO {
*/
RaceWithCompetitorsDTO() {}
public RaceWithCompetitorsDTO(RegattaAndRaceIdentifier raceIdentifier, Iterable<CompetitorDTO> competitors, TrackedRaceDTO trackedRace) {
super(raceIdentifier, trackedRace);
public RaceWithCompetitorsDTO(RegattaAndRaceIdentifier raceIdentifier, Iterable<CompetitorDTO> competitors,
TrackedRaceDTO trackedRace, boolean isCurrentlyTracked) {
super(raceIdentifier, trackedRace, isCurrentlyTracked);
this.competitors = competitors;
}
@@ -47,7 +47,7 @@ import com.sap.sailing.gwt.ui.client.ErrorReporter;
import com.sap.sailing.gwt.ui.client.RaceSelectionProvider;
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.client.TimeZoomProvider;
import com.sap.sailing.gwt.ui.client.TimeRangeWithZoomProvider;
import com.sap.sailing.gwt.ui.client.Timer;
import com.sap.sailing.gwt.ui.client.Timer.PlayModes;
import com.sap.sailing.gwt.ui.shared.CompetitorDTO;
@@ -88,9 +88,9 @@ implements CompetitorSelectionChangeListener, RequiresResize {
public AbstractChartPanel(SailingServiceAsync sailingService, AsyncActionsExecutor asyncActionsExecutor,
CompetitorSelectionProvider competitorSelectionProvider, RaceSelectionProvider raceSelectionProvider,
Timer timer, TimeZoomProvider timeZoomProvider, final StringMessages stringMessages, ErrorReporter errorReporter, DetailType dataToShow,
Timer timer, TimeRangeWithZoomProvider timeRangeWithZoomProvider, final StringMessages stringMessages, ErrorReporter errorReporter, DetailType dataToShow,
boolean compactChart, boolean allowTimeAdjust) {
super(sailingService, timer, timeZoomProvider, stringMessages, asyncActionsExecutor, errorReporter);
super(sailingService, timer, timeRangeWithZoomProvider, stringMessages, asyncActionsExecutor, errorReporter);
this.competitorSelectionProvider = competitorSelectionProvider;
this.dataToShow = dataToShow;
this.compactChart = compactChart;
@@ -107,8 +107,6 @@ implements CompetitorSelectionChangeListener, RequiresResize {
competitorSelectionProvider.addCompetitorSelectionChangeListener(this);
raceSelectionProvider.addRaceSelectionChangeListener(this);
timer.addTimeListener(this);
timeZoomProvider.addTimeZoomChangeListener(this);
}
@Override
@@ -243,14 +241,14 @@ implements CompetitorSelectionChangeListener, RequiresResize {
if (!chartData.contains(competitor)) {
dataQuery.add(new Pair<Date, CompetitorDTO>(new Date(0), competitor));
} else if (competitorDateOfNewestData.before(chartDataDateOfNewestData)
|| competitorDateOfNewestData.before(maxTimepoint)) {
|| competitorDateOfNewestData.before(timeRangeWithZoomProvider.getToTime())) {
dataQuery.add(new Pair<Date, CompetitorDTO>(new Date(competitorDateOfNewestData.getTime()
+ getStepSize()), competitor));
}
}
GetCompetitorsRaceDataAction getCompetitorsRaceDataAction = new GetCompetitorsRaceDataAction(
sailingService, selectedRaceIdentifier, dataQuery, maxTimepoint, getStepSize(),
sailingService, selectedRaceIdentifier, dataQuery, timeRangeWithZoomProvider.getToTime(), getStepSize(),
getDataToShow(), new AsyncCallback<MultiCompetitorRaceDataDTO>() {
@Override
@@ -273,8 +271,8 @@ implements CompetitorSelectionChangeListener, RequiresResize {
}
}
}
chart.getXAxis().setMin(minTimepoint.getTime());
chart.getXAxis().setMax(maxTimepoint.getTime());
chart.getXAxis().setMin(timeRangeWithZoomProvider.getFromTime().getTime());
chart.getXAxis().setMax(timeRangeWithZoomProvider.getToTime().getTime());
drawChartData();
chart.redraw();
@@ -7,7 +7,7 @@ import com.sap.sailing.gwt.ui.client.ErrorReporter;
import com.sap.sailing.gwt.ui.client.RaceSelectionProvider;
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.client.TimeZoomProvider;
import com.sap.sailing.gwt.ui.client.TimeRangeWithZoomProvider;
import com.sap.sailing.gwt.ui.client.Timer;
import com.sap.sailing.gwt.ui.shared.components.Component;
import com.sap.sailing.gwt.ui.shared.components.SettingsDialogComponent;
@@ -27,9 +27,9 @@ import com.sap.sailing.gwt.ui.shared.components.SettingsDialogComponent;
public class ChartPanel extends AbstractChartPanel<ChartSettings> implements Component<ChartSettings> {
public ChartPanel(SailingServiceAsync sailingService, AsyncActionsExecutor asyncActionsExecutor, CompetitorSelectionProvider competitorSelectionProvider,
RaceSelectionProvider raceSelectionProvider, Timer timer, TimeZoomProvider timeZoomProvider, DetailType dataToShow, StringMessages stringMessages,
RaceSelectionProvider raceSelectionProvider, Timer timer, TimeRangeWithZoomProvider timeRangeWithZoomProvider, DetailType dataToShow, StringMessages stringMessages,
ErrorReporter errorReporter, boolean compactChart, boolean allowTimeAdjust) {
super(sailingService, asyncActionsExecutor, competitorSelectionProvider, raceSelectionProvider, timer, timeZoomProvider, stringMessages, errorReporter,
super(sailingService, asyncActionsExecutor, competitorSelectionProvider, raceSelectionProvider, timer, timeRangeWithZoomProvider, stringMessages, errorReporter,
dataToShow, compactChart, allowTimeAdjust);
}
@@ -7,7 +7,7 @@ import com.sap.sailing.gwt.ui.client.ErrorReporter;
import com.sap.sailing.gwt.ui.client.RaceSelectionProvider;
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.client.TimeZoomProvider;
import com.sap.sailing.gwt.ui.client.TimeRangeWithZoomProvider;
import com.sap.sailing.gwt.ui.client.Timer;
import com.sap.sailing.gwt.ui.shared.components.Component;
import com.sap.sailing.gwt.ui.shared.components.SettingsDialogComponent;
@@ -27,9 +27,9 @@ import com.sap.sailing.gwt.ui.shared.components.SettingsDialogComponent;
public class MultiChartPanel extends AbstractChartPanel<MultiChartSettings> implements Component<MultiChartSettings> {
public MultiChartPanel(SailingServiceAsync sailingService, AsyncActionsExecutor asyncActionsExecutor,
CompetitorSelectionProvider competitorSelectionProvider, RaceSelectionProvider raceSelectionProvider,
Timer timer, TimeZoomProvider timeZoomProvider, final StringMessages stringMessages, ErrorReporter errorReporter, boolean compactChart,
Timer timer, TimeRangeWithZoomProvider timeRangeWithZoomProvider, final StringMessages stringMessages, ErrorReporter errorReporter, boolean compactChart,
boolean allowTimeAdjust) {
super(sailingService, asyncActionsExecutor, competitorSelectionProvider, raceSelectionProvider, timer, timeZoomProvider, stringMessages, errorReporter,
super(sailingService, asyncActionsExecutor, competitorSelectionProvider, raceSelectionProvider, timer, timeRangeWithZoomProvider, stringMessages, errorReporter,
/*show initially*/ DetailType.WINDWARD_DISTANCE_TO_OVERALL_LEADER, compactChart, allowTimeAdjust);
}
@@ -1,7 +1,6 @@
package com.sap.sailing.gwt.ui.shared.charts;
import java.util.Date;
import java.util.Map;
import org.moxieapps.gwt.highcharts.client.Chart;
import org.moxieapps.gwt.highcharts.client.Legend;
@@ -18,33 +17,26 @@ import org.moxieapps.gwt.highcharts.client.plotOptions.SeriesPlotOptions;
import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.user.client.ui.SimplePanel;
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
import com.sap.sailing.domain.common.impl.Util.Pair;
import com.sap.sailing.gwt.ui.actions.AsyncActionsExecutor;
import com.sap.sailing.gwt.ui.client.ErrorReporter;
import com.sap.sailing.gwt.ui.client.RaceSelectionChangeListener;
import com.sap.sailing.gwt.ui.client.RaceTimesCalculationUtil;
import com.sap.sailing.gwt.ui.client.RaceTimesInfoProviderListener;
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.client.TimeListener;
import com.sap.sailing.gwt.ui.client.TimeRangeChangeListener;
import com.sap.sailing.gwt.ui.client.TimeRangeWithZoomProvider;
import com.sap.sailing.gwt.ui.client.TimeZoomChangeListener;
import com.sap.sailing.gwt.ui.client.TimeZoomProvider;
import com.sap.sailing.gwt.ui.client.Timer;
import com.sap.sailing.gwt.ui.client.Timer.PlayModes;
import com.sap.sailing.gwt.ui.shared.RaceTimesInfoDTO;
public abstract class RaceChart extends SimplePanel implements RaceTimesInfoProviderListener, RaceSelectionChangeListener,
TimeListener, TimeZoomChangeListener {
public abstract class RaceChart extends SimplePanel implements RaceSelectionChangeListener,
TimeListener, TimeZoomChangeListener, TimeRangeChangeListener {
protected Chart chart;
protected final Timer timer;
protected final TimeZoomProvider timeZoomProvider;
protected Date minTimepoint;
protected Date maxTimepoint;
protected final TimeRangeWithZoomProvider timeRangeWithZoomProvider;
protected RegattaAndRaceIdentifier selectedRaceIdentifier;
protected RaceTimesInfoDTO lastRaceTimesInfo;
protected final DateTimeFormat dateFormat = DateTimeFormat.getFormat("HH:mm:ss");
protected final DateTimeFormat dateFormatHoursMinutes = DateTimeFormat.getFormat("HH:mm");
@@ -62,47 +54,17 @@ public abstract class RaceChart extends SimplePanel implements RaceTimesInfoProv
private boolean ignoreNextClickEvent;
public RaceChart(SailingServiceAsync sailingService, Timer timer, TimeZoomProvider timeZoomProvider, final StringMessages stringMessages,
public RaceChart(SailingServiceAsync sailingService, Timer timer, TimeRangeWithZoomProvider timeRangeWithZoomProvider, final StringMessages stringMessages,
AsyncActionsExecutor asyncActionsExecutor, ErrorReporter errorReporter) {
this.sailingService = sailingService;
this.timer = timer;
this.timeZoomProvider = timeZoomProvider;
this.timeRangeWithZoomProvider = timeRangeWithZoomProvider;
this.stringMessages = stringMessages;
this.asyncActionsExecutor = asyncActionsExecutor;
this.errorReporter = errorReporter;
}
@Override
public void raceTimesInfosReceived(Map<RegattaAndRaceIdentifier, RaceTimesInfoDTO> raceTimesInfos) {
this.lastRaceTimesInfo = raceTimesInfos.get(selectedRaceIdentifier);
Pair<Date, Date> raceMinMax = RaceTimesCalculationUtil.caluclateRaceMinMax(timer, this.lastRaceTimesInfo);
boolean updateMinMax = false;
if(chart != null) {
if (minTimepoint == null || maxTimepoint == null) {
minTimepoint = raceMinMax.getA();
maxTimepoint = raceMinMax.getB();
updateMinMax = true;
} else if(minTimepoint.getTime() != raceMinMax.getA().getTime() || maxTimepoint.getTime() != raceMinMax.getB().getTime()) {
minTimepoint = raceMinMax.getA();
maxTimepoint = raceMinMax.getB();
updateMinMax = true;
}
}
if (updateMinMax) {
if (minTimepoint != null) {
chart.getXAxis().setMin(minTimepoint.getTime());
}
if (maxTimepoint != null) {
chart.getXAxis().setMax(maxTimepoint.getTime());
}
if (minTimepoint != null && maxTimepoint != null) {
chart.getXAxis().setExtremes(minTimepoint.getTime(), maxTimepoint.getTime(), false, false);
long tickInterval = (maxTimepoint.getTime() - minTimepoint.getTime()) / TICKCOUNT;
chart.getXAxis().setTickInterval(tickInterval);
}
}
timer.addTimeListener(this);
timeRangeWithZoomProvider.addTimeZoomChangeListener(this);
timeRangeWithZoomProvider.addTimeRangeChangeListener(this);
}
protected void showLoading(String message) {
@@ -126,10 +88,10 @@ public abstract class RaceChart extends SimplePanel implements RaceTimesInfoProv
if (!isZoomed) {
isZoomed = true;
}
timeZoomProvider.setTimeZoom(new Date(xAxisMin), new Date(xAxisMax), this);
timeRangeWithZoomProvider.setTimeZoom(new Date(xAxisMin), new Date(xAxisMax), this);
} catch (Throwable t) {
// in case the user clicks the "reset zoom" button chartSelectionEvent.getXAxisMinAsLong() throws in exception
timeZoomProvider.resetTimeZoom(this);
timeRangeWithZoomProvider.resetTimeZoom(this);
// Trigger the redrawing... otherwise chart wouldn't reset the zoom
chart.redraw();
isZoomed = false;
@@ -151,19 +113,42 @@ public abstract class RaceChart extends SimplePanel implements RaceTimesInfoProv
return true;
}
protected void resetMinMaxInterval(Date minIntervalTimepoint, Date maxIntervalTimepoint) {
XAxis xAxis = chart.getXAxis();
Date minTimepoint = timeRangeWithZoomProvider.getFromTime();
Date maxTimepoint = timeRangeWithZoomProvider.getToTime();
if (minTimepoint != null) {
xAxis.setMin(minTimepoint.getTime());
}
if (maxTimepoint != null) {
xAxis.setMax(maxTimepoint.getTime());
}
if (minTimepoint != null && maxTimepoint != null) {
xAxis.setExtremes(minTimepoint.getTime(), maxTimepoint.getTime(), false, false);
long tickInterval = (maxTimepoint.getTime() - minTimepoint.getTime()) / TICKCOUNT;
xAxis.setTickInterval(tickInterval);
}
chart.redraw();
}
protected void changeMinMaxInterval(Date minIntervalTimepoint, Date maxIntervalTimepoint) {
XAxis xAxis = chart.getXAxis();
xAxis.setExtremes(minIntervalTimepoint.getTime(), maxIntervalTimepoint.getTime(), true, true);
}
public void onTimeZoom(Date zoomStartTimepoint, Date zoomEndTimepoint) {
public void onTimeZoomChanged(Date zoomStartTimepoint, Date zoomEndTimepoint) {
changeMinMaxInterval(zoomStartTimepoint, zoomEndTimepoint);
// Probably there is a function for this in a newer version of highcharts: http://jsfiddle.net/mqz3N/1071/
// chart.showResetZoom();
}
public void onTimeRangeChanged(Date fromTime, Date toTime) {
resetMinMaxInterval(fromTime, toTime);
}
public void onTimeZoomReset() {
changeMinMaxInterval(minTimepoint, maxTimepoint);
changeMinMaxInterval(timeRangeWithZoomProvider.getFromTime(), timeRangeWithZoomProvider.getToTime());
}
/**
@@ -43,7 +43,7 @@ import com.sap.sailing.gwt.ui.client.ErrorReporter;
import com.sap.sailing.gwt.ui.client.RaceSelectionProvider;
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.client.TimeZoomProvider;
import com.sap.sailing.gwt.ui.client.TimeRangeWithZoomProvider;
import com.sap.sailing.gwt.ui.client.Timer;
import com.sap.sailing.gwt.ui.client.Timer.PlayModes;
import com.sap.sailing.gwt.ui.client.WindSourceTypeFormatter;
@@ -75,9 +75,9 @@ public class WindChart extends RaceChart implements Component<WindChartSettings>
* server and displayed in this chart. If no race is selected, the chart is cleared.
*/
public WindChart(SailingServiceAsync sailingService, RaceSelectionProvider raceSelectionProvider, Timer timer,
TimeZoomProvider timeZoomProvider, WindChartSettings settings, final StringMessages stringMessages,
TimeRangeWithZoomProvider timeRangeWithZoomProvider, WindChartSettings settings, final StringMessages stringMessages,
AsyncActionsExecutor asyncActionsExecutor, ErrorReporter errorReporter, boolean compactChart) {
super(sailingService, timer, timeZoomProvider, stringMessages, asyncActionsExecutor, errorReporter);
super(sailingService, timer, timeRangeWithZoomProvider, stringMessages, asyncActionsExecutor, errorReporter);
this.windSourceDirectionSeries = new HashMap<WindSource, Series>();
this.windSourceSpeedSeries = new HashMap<WindSource, Series>();
this.colorMap = new ColorMap<WindSource>();
@@ -170,8 +170,6 @@ public class WindChart extends RaceChart implements Component<WindChartSettings>
setSize("100%", "100%");
raceSelectionProvider.addRaceSelectionChangeListener(this);
timer.addTimeListener(this);
timeZoomProvider.addTimeZoomChangeListener(this);
}
@Override
@@ -424,7 +422,7 @@ public class WindChart extends RaceChart implements Component<WindChartSettings>
timeOfEarliestRequestInMillis = null;
timeOfLatestRequestInMillis = null;
loadData(minTimepoint, maxTimepoint, /* append */false);
loadData(timeRangeWithZoomProvider.getFromTime(), timeRangeWithZoomProvider.getToTime(), /* append */false);
}
/**
@@ -504,9 +502,9 @@ public class WindChart extends RaceChart implements Component<WindChartSettings>
{
// is date before first cache entry or is cache empty?
if (timeOfEarliestRequestInMillis == null || timeOfEarliestRequestInMillis > date.getTime()) {
loadData(minTimepoint, date, /* append */ true);
loadData(timeRangeWithZoomProvider.getFromTime(), date, /* append */ true);
} else if (timeOfLatestRequestInMillis < date.getTime()) {
loadData(new Date(timeOfLatestRequestInMillis), maxTimepoint, /* append */true);
loadData(new Date(timeOfLatestRequestInMillis), timeRangeWithZoomProvider.getToTime(), /* append */true);
}
// otherwise the cache spans across date and so we don't need to load anything
break;
@@ -515,7 +513,7 @@ public class WindChart extends RaceChart implements Component<WindChartSettings>
{
// assuming play mode is replay / non-live
if (timeOfLatestRequestInMillis == null) {
loadData(minTimepoint, maxTimepoint, /* append */false); // replace old series
loadData(timeRangeWithZoomProvider.getFromTime(), timeRangeWithZoomProvider.getToTime(), /* append */false); // replace old series
}
break;
}
@@ -1,6 +1,5 @@
package com.sap.sailing.gwt.ui.shared.components;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.FocusWidget;
import com.google.gwt.user.client.ui.Widget;
import com.sap.sailing.gwt.ui.client.DataEntryDialog;
@@ -12,13 +11,13 @@ public class SettingsDialog<SettingsType> extends DataEntryDialog<SettingsType>
public SettingsDialog(final Component<SettingsType> component, StringMessages stringConstants) {
super(stringConstants.settingsForComponent(component.getLocalizedShortName()), null, stringConstants.ok(),
stringConstants.cancel(), component.getSettingsDialogComponent().getValidator(),
new AsyncCallback<SettingsType>() {
new DialogCallback<SettingsType>() {
@Override
public void onFailure(Throwable t) {
public void cancel() {
}
@Override
public void onSuccess(SettingsType newSettings) {
public void ok(SettingsType newSettings) {
component.updateSettings(newSettings);
}
});

Some files were not shown because too many files have changed in this diff Show More