reduced formating changes

This commit is contained in:
Kai Boernert committed 2016-10-12 14:15:49 +02:00
1 parent cff727ae1e
commit 0f1555ce7a
1 file changed
+96 -107
@@ -77,14 +77,14 @@ import com.sap.sse.gwt.client.shared.components.Component;
* @author Benjamin Ebling (D056866), Axel Uhl (d043530)
*
*/
public abstract class AbstractCompetitorRaceChart<SettingsType extends ChartSettings>
extends AbstractRaceChart<SettingsType> implements CompetitorSelectionChangeListener, RequiresResize {
public abstract class AbstractCompetitorRaceChart<SettingsType extends ChartSettings> extends AbstractRaceChart<SettingsType> implements
CompetitorSelectionChangeListener, RequiresResize {
public static final String LOAD_COMPETITOR_CHART_DATA_CATEGORY = "loadCompetitorChartData";
public static final long DEFAULT_STEPSIZE = 5000;
private static final int LINE_WIDTH = 1;
private final Label noCompetitorsSelectedLabel;
private final Label noDataFoundLabel;
private final CompetitorSelectionProvider competitorSelectionProvider;
@@ -98,7 +98,7 @@ public abstract class AbstractCompetitorRaceChart<SettingsType extends ChartSett
private long stepSizeInMillis = DEFAULT_STEPSIZE;
private final Map<Pair<CompetitorDTO, DetailType>, Series> dataSeriesForDetailTypeAndCompetitor = new HashMap<>();
private final Map<Pair<CompetitorDTO, DetailType>, Series> markPassingSeriesByCompetitor = new HashMap<>();
private final TimingHolder primary = new TimingHolder();
private final TimingHolder secondary = new TimingHolder();
@@ -106,15 +106,15 @@ public abstract class AbstractCompetitorRaceChart<SettingsType extends ChartSett
CompetitorSelectionProvider competitorSelectionProvider, RegattaAndRaceIdentifier selectedRaceIdentifier,
Timer timer, TimeRangeWithZoomProvider timeRangeWithZoomProvider, final StringMessages stringMessages,
ErrorReporter errorReporter, DetailType firstDetailType, DetailType secondDetailType, boolean compactChart,
boolean allowTimeAdjust, String leaderboardGroupName, String leaderboardName) {
super(sailingService, selectedRaceIdentifier, timer, timeRangeWithZoomProvider, stringMessages,
asyncActionsExecutor, errorReporter);
boolean allowTimeAdjust,
String leaderboardGroupName, String leaderboardName) {
super(sailingService, selectedRaceIdentifier, timer, timeRangeWithZoomProvider, stringMessages, asyncActionsExecutor, errorReporter);
this.competitorSelectionProvider = competitorSelectionProvider;
this.compactChart = compactChart;
this.allowTimeAdjust = allowTimeAdjust;
this.leaderboardGroupName = leaderboardGroupName;
this.leaderboardName = leaderboardName;
setSize("100%", "100%");
noCompetitorsSelectedLabel = new Label(stringMessages.selectAtLeastOneCompetitor() + ".");
noCompetitorsSelectedLabel.setStyleName("abstractChartPanel-importantMessageOfChart");
@@ -130,15 +130,24 @@ public abstract class AbstractCompetitorRaceChart<SettingsType extends ChartSett
}
/**
* Creates a new chart. Attention: We can't reuse the old chart when the detail changes because HighChart does not
* support the inverting of the Y-Axis
* Creates a new chart.
* Attention: We can't reuse the old chart when the detail changes because HighChart does not support the inverting of the Y-Axis
*/
private Chart createChart() {
Chart chart = new Chart().setZoomType(BaseChart.ZoomType.X).setPersistent(true).setWidth100()
.setAlignTicks(true).setHeight100().setMarginLeft(65).setMarginRight(65)
.setBorderColor(new Color("#CACACA")).setBackgroundColor(new Color("#FFFFFF"))
.setPlotBackgroundColor("#f8f8f8").setBorderWidth(0).setBorderRadius(0).setPlotBorderWidth(0)
Chart chart = new Chart().setZoomType(BaseChart.ZoomType.X)
.setPersistent(true)
.setWidth100()
.setAlignTicks(true)
.setHeight100()
.setMarginLeft(65)
.setMarginRight(65)
.setBorderColor(new Color("#CACACA"))
.setBackgroundColor(new Color("#FFFFFF"))
.setPlotBackgroundColor("#f8f8f8")
.setBorderWidth(0)
.setBorderRadius(0)
.setPlotBorderWidth(0)
.setCredits(new Credits().setEnabled(false))
.setChartSubtitle(new ChartSubtitle().setText(stringMessages.clickAndDragToZoomIn()));
chart.setStyleName(chartsCss.chartStyle());
@@ -164,11 +173,10 @@ public abstract class AbstractCompetitorRaceChart<SettingsType extends ChartSett
chart.getYAxis(1).setStartOnTick(false).setShowFirstLabel(false).setOpposite(true);
} else {
chart.getYAxis(0).setStartOnTick(false).setShowFirstLabel(false);
chart.setLinePlotOptions(
new LinePlotOptions().setLineWidth(LINE_WIDTH)
.setMarker(new Marker().setEnabled(false)
.setHoverState(new Marker().setEnabled(true).setRadius(4)))
.setShadow(false).setHoverStateLineWidth(LINE_WIDTH));
chart.setLinePlotOptions(new LinePlotOptions()
.setLineWidth(LINE_WIDTH)
.setMarker(new Marker().setEnabled(false).setHoverState(new Marker().setEnabled(true).setRadius(4)))
.setShadow(false).setHoverStateLineWidth(LINE_WIDTH));
}
chart.getXAxis().setType(Axis.Type.DATE_TIME).setMaxZoom(60 * 1000); // 1 minute
chart.getXAxis().setLabels(new XAxisLabels().setFormatter(new AxisLabelsFormatter() {
@@ -185,8 +193,8 @@ public abstract class AbstractCompetitorRaceChart<SettingsType extends ChartSett
protected abstract Component<SettingsType> getComponent();
/**
* Loads the needed data (data which isn't in the {@link #chartData cache}) for the {@link #getSelectedCompetitors()
* visible competitors} via
* Loads the needed data (data which isn't in the {@link #chartData cache}) for the
* {@link #getSelectedCompetitors() visible competitors} via
* {@link SailingServiceAsync#getCompetitorsRaceData(RaceIdentifier, List, long, DetailType, AsyncCallback)}. After
* loading, the method {@link #drawChartData()} is called.
* <p>
@@ -219,23 +227,19 @@ public abstract class AbstractCompetitorRaceChart<SettingsType extends ChartSett
competitorsToLoad.add(competitorDTO);
}
// If the time interval is too long and the step size too small, the number of fixes the query would have to
// produce may exceed any reasonable limit. Therefore, we limit the number of fixes that such a query may
// ask
// produce may exceed any reasonable limit. Therefore, we limit the number of fixes that such a query may ask
// for:
doLoadDataForCompetitorsAndDataType(from, to, append, competitorsToLoad, getSelectedFirstDetailType(),
primary);
doLoadDataForCompetitorsAndDataType(from, to, append, competitorsToLoad, getSelectedFirstDetailType(),primary);
if (getSelectedSecondDetailType() != null) {
doLoadDataForCompetitorsAndDataType(from, to, append, competitorsToLoad, getSelectedSecondDetailType(),
secondary);
doLoadDataForCompetitorsAndDataType(from, to, append, competitorsToLoad, getSelectedSecondDetailType(),secondary);
}
}
}
private void doLoadDataForCompetitorsAndDataType(final Date from, final Date to, final boolean append,
ArrayList<CompetitorDTO> competitorsToLoad, final DetailType selectedDataTypeToRetrieve,
final TimingHolder tholder) {
long stepSize = Math.max(getStepSizeInMillis(), from == null || to == null ? 0
: Math.abs(to.getTime() - from.getTime()) / SailingServiceConstants.MAX_NUMBER_OF_FIXES_TO_QUERY);
ArrayList<CompetitorDTO> competitorsToLoad, final DetailType selectedDataTypeToRetrieve,final TimingHolder tholder) {
long stepSize = Math.max(getStepSizeInMillis(),
from==null||to==null ? 0 : Math.abs(to.getTime()-from.getTime())/SailingServiceConstants.MAX_NUMBER_OF_FIXES_TO_QUERY);
GetCompetitorsRaceDataAction getCompetitorsRaceDataAction = new GetCompetitorsRaceDataAction(sailingService,
selectedRaceIdentifier, competitorsToLoad, from, to, stepSize, selectedDataTypeToRetrieve,
leaderboardGroupName, leaderboardName);
@@ -248,7 +252,7 @@ public abstract class AbstractCompetitorRaceChart<SettingsType extends ChartSett
if (result.isEmpty() && chartContainsNoData()) {
setWidget(noDataFoundLabel);
} else {
updateChartSeries(result, selectedDataTypeToRetrieve, append, tholder);
updateChartSeries(result, selectedDataTypeToRetrieve, append,tholder);
}
} else {
if (!append) {
@@ -265,7 +269,7 @@ public abstract class AbstractCompetitorRaceChart<SettingsType extends ChartSett
}
});
}
private boolean chartContainsNoData() {
for (Series competitorSeries : dataSeriesForDetailTypeAndCompetitor.values()) {
if (competitorSeries.getPoints().length != 0) {
@@ -280,19 +284,15 @@ public abstract class AbstractCompetitorRaceChart<SettingsType extends ChartSett
if (isVisible()) {
ArrayList<CompetitorDTO> competitorsToLoad = new ArrayList<CompetitorDTO>();
competitorsToLoad.add(competitor);
{
Date fromDate = primary.timeOfEarliestRequestInMillis == null ? null
: new Date(primary.timeOfEarliestRequestInMillis);
Date toDate = primary.timeOfLatestRequestInMillis == null ? null
: new Date(primary.timeOfLatestRequestInMillis);
Date fromDate = primary.timeOfEarliestRequestInMillis == null ? null : new Date(primary.timeOfEarliestRequestInMillis);
Date toDate = primary.timeOfLatestRequestInMillis == null ? null : new Date(primary.timeOfLatestRequestInMillis);
loadData(fromDate, toDate, competitorsToLoad, false);
}
{
Date fromDate = secondary.timeOfEarliestRequestInMillis == null ? null
: new Date(secondary.timeOfEarliestRequestInMillis);
Date toDate = secondary.timeOfLatestRequestInMillis == null ? null
: new Date(secondary.timeOfLatestRequestInMillis);
Date fromDate = secondary.timeOfEarliestRequestInMillis == null ? null : new Date(secondary.timeOfEarliestRequestInMillis);
Date toDate = secondary.timeOfLatestRequestInMillis == null ? null : new Date(secondary.timeOfLatestRequestInMillis);
loadData(fromDate, toDate, competitorsToLoad, false);
}
}
@@ -324,23 +324,21 @@ public abstract class AbstractCompetitorRaceChart<SettingsType extends ChartSett
/**
* Creates the series for all selected competitors if these aren't created yet.<br />
* Fills the series for the selected competitors with the data in {@link AbstractCompetitorRaceChart#chartData}.
* <br />
* Fills the series for the selected competitors with the data in {@link AbstractCompetitorRaceChart#chartData}.<br />
*/
private synchronized void updateChartSeries(CompetitorsRaceDataDTO chartData, DetailType retrievedDataType,
boolean append, TimingHolder tholder) {
boolean append,TimingHolder tholder) {
// Make sure the busy indicator is removed at this point, or plotting the data results in an exception
setWidget(chart);
for (CompetitorDTO competitor : chartData.getCompetitors()) {
Series competitorDataSeries = getOrCreateCompetitorDataSeries(retrievedDataType, competitor);
Series markPassingSeries = getOrCreateCompetitorMarkPassingSeries(competitorDataSeries, retrievedDataType,
competitor);
CompetitorRaceDataDTO competitorData = chartData.getCompetitorData(competitor);
if (competitorData != null) {
Date toDate = timer.getLiveTimePointAsDate();
List<com.sap.sse.common.Util.Triple<String, Date, Double>> markPassingsData = competitorData
.getMarkPassingsData();
List<com.sap.sse.common.Util.Triple<String, Date, Double>> markPassingsData = competitorData.getMarkPassingsData();
List<Point> markPassingPoints = new ArrayList<Point>();
for (com.sap.sse.common.Util.Triple<String, Date, Double> markPassingData : markPassingsData) {
if (markPassingData.getB() != null && markPassingData.getC() != null) {
@@ -361,13 +359,10 @@ public abstract class AbstractCompetitorRaceChart<SettingsType extends ChartSett
int currentPointIndex = 0;
for (com.sap.sse.common.Util.Pair<Date, Double> raceDataPoint : raceData) {
Double dataPointValue = raceDataPoint.getB();
if (dataPointValue != null) {
if(dataPointValue != null) {
long dataPointTimeAsMillis = raceDataPoint.getA().getTime();
if (append == false
|| (tholder.timeOfEarliestRequestInMillis == null
|| dataPointTimeAsMillis < tholder.timeOfEarliestRequestInMillis)
|| tholder.timeOfLatestRequestInMillis == null
|| dataPointTimeAsMillis > tholder.timeOfLatestRequestInMillis) {
if(append == false || (tholder.timeOfEarliestRequestInMillis == null || dataPointTimeAsMillis < tholder.timeOfEarliestRequestInMillis) ||
tholder.timeOfLatestRequestInMillis == null || dataPointTimeAsMillis > tholder.timeOfLatestRequestInMillis) {
raceDataPointsToAdd[currentPointIndex++] = new Point(dataPointTimeAsMillis, dataPointValue);
}
}
@@ -377,29 +372,26 @@ public abstract class AbstractCompetitorRaceChart<SettingsType extends ChartSett
if (append) {
newRaceDataPoints = new Point[oldRaceDataPoints.length + currentPointIndex];
System.arraycopy(oldRaceDataPoints, 0, newRaceDataPoints, 0, oldRaceDataPoints.length);
System.arraycopy(raceDataPointsToAdd, 0, newRaceDataPoints, oldRaceDataPoints.length,
currentPointIndex);
System.arraycopy(raceDataPointsToAdd, 0, newRaceDataPoints, oldRaceDataPoints.length, currentPointIndex);
} else {
newRaceDataPoints = new Point[currentPointIndex];
System.arraycopy(raceDataPointsToAdd, 0, newRaceDataPoints, 0, currentPointIndex);
}
setSeriesPoints(competitorDataSeries, newRaceDataPoints);
// Adding the series if chart doesn't contain it
List<Series> chartSeries = Arrays.asList(chart.getSeries());
if (!chartSeries.contains(competitorDataSeries)) {
chart.addSeries(competitorDataSeries);
chart.addSeries(markPassingSeries);
}
}
}
if (tholder.timeOfEarliestRequestInMillis == null
|| tholder.timeOfEarliestRequestInMillis > chartData.getRequestedFromTime().getTime()) {
if (tholder.timeOfEarliestRequestInMillis == null || tholder.timeOfEarliestRequestInMillis > chartData.getRequestedFromTime().getTime()) {
tholder.timeOfEarliestRequestInMillis = chartData.getRequestedFromTime().getTime();
}
if (tholder.timeOfLatestRequestInMillis == null
|| tholder.timeOfLatestRequestInMillis < chartData.getRequestedToTime().getTime()) {
if (tholder.timeOfLatestRequestInMillis == null || tholder.timeOfLatestRequestInMillis < chartData.getRequestedToTime().getTime()) {
tholder.timeOfLatestRequestInMillis = chartData.getRequestedToTime().getTime();
}
chart.redraw();
@@ -431,14 +423,13 @@ public abstract class AbstractCompetitorRaceChart<SettingsType extends ChartSett
} else {
result.setName(competitor.getName());
}
result.setPlotOptions(
new LinePlotOptions().setLineWidth(LINE_WIDTH)
.setDashStyle(yAxisIndex == 0 ? PlotLine.DashStyle.SOLID : PlotLine.DashStyle.LONG_DASH)
.setMarker(new Marker().setEnabled(false)
.setHoverState(new Marker().setEnabled(true).setRadius(4)))
.setShadow(false).setHoverStateLineWidth(LINE_WIDTH).setColor(competitorSelectionProvider
.getColor(competitor, selectedRaceIdentifier).getAsHtml())
.setSelected(true));
result.setPlotOptions(new LinePlotOptions()
.setLineWidth(LINE_WIDTH)
.setDashStyle(yAxisIndex == 0 ? PlotLine.DashStyle.SOLID : PlotLine.DashStyle.LONG_DASH)
.setMarker(new Marker().setEnabled(false).setHoverState(new Marker().setEnabled(true).setRadius(4)))
.setShadow(false).setHoverStateLineWidth(LINE_WIDTH)
.setColor(competitorSelectionProvider.getColor(competitor, selectedRaceIdentifier).getAsHtml())
.setSelected(true));
result.setOption("turboThreshold", MAX_SERIES_POINTS);
dataSeriesForDetailTypeAndCompetitor.put(coDePair, result);
}
@@ -462,7 +453,8 @@ public abstract class AbstractCompetitorRaceChart<SettingsType extends ChartSett
result = chart.createSeries().setType(Series.Type.SCATTER)
.setName(stringMessages.markPassing() + " " + competitor.getName());
result.setPlotOptions(new ScatterPlotOptions().setLinkedTo(linkedCompetitorSeries)
.setColor(competitorSelectionProvider.getColor(competitor, selectedRaceIdentifier).getAsHtml())
.setColor(
competitorSelectionProvider.getColor(competitor, selectedRaceIdentifier).getAsHtml())
.setSelected(true));
markPassingSeriesByCompetitor.put(coDePair, result);
}
@@ -613,13 +605,13 @@ public abstract class AbstractCompetitorRaceChart<SettingsType extends ChartSett
// if (oldReversedY0Axis != isY0AxisReversed() || oldReversedY1Axis != isY1AxisReversed()) {
// WORKAROUND: re-creating chart every time since introduction of dual y-axis, since there is no way to
// reset/ delete axis.
chart = createChart();
if (isZoomed) {
com.sap.sse.common.Util.Pair<Date, Date> zoomRange = timeRangeWithZoomProvider.getTimeZoom();
onTimeZoomChanged(zoomRange.getA(), zoomRange.getB());
} else {
resetMinMaxAndExtremesInterval(/* redraw */ true);
}
chart = createChart();
if (isZoomed) {
com.sap.sse.common.Util.Pair<Date, Date> zoomRange = timeRangeWithZoomProvider.getTimeZoom();
onTimeZoomChanged(zoomRange.getA(), zoomRange.getB());
} else {
resetMinMaxAndExtremesInterval(/* redraw */ true);
}
// }
final String unitY0 = DetailTypeFormatter.getUnit(getSelectedFirstDetailType());
@@ -639,22 +631,25 @@ public abstract class AbstractCompetitorRaceChart<SettingsType extends ChartSett
} else {
chart.getYAxis(0).setAxisTitleText(labelY0);
}
if (hasSecondYAxis()) {
chart.getYAxis(0).setReversed(isY0AxisReversed()).setOpposite(false).setGridLineWidth(1)
chart.getYAxis(0).setReversed(isY0AxisReversed()).setOpposite(false)
.setGridLineWidth(1)
.setMinorGridLineWidth(0).setMinorGridLineColor("transparent");
chart.getYAxis(1).setReversed(isY1AxisReversed()).setOpposite(true).setGridLineWidth(1)
.setGridLineDashStyle(DashStyle.LONG_DASH).setMinorGridLineWidth(0)
.setMinorGridLineColor("transparent").setMinorTickIntervalAuto();
chart.getYAxis(1).setReversed(isY1AxisReversed()).setOpposite(true)
.setGridLineWidth(1)
.setGridLineDashStyle(DashStyle.LONG_DASH)
.setMinorGridLineWidth(0).setMinorGridLineColor("transparent")
.setMinorTickIntervalAuto();
} else {
chart.getYAxis(0).setReversed(isY0AxisReversed());
}
chart.setAlignTicks(hasSecondYAxis());
final NumberFormat numberFormatY0 = DetailTypeFormatter.getNumberFormat(selectedFirstDetailType);
final NumberFormat numberFormatY1 = hasSecondYAxis()
? DetailTypeFormatter.getNumberFormat(selectedSecondDetailType) : null;
final NumberFormat numberFormatY1 = hasSecondYAxis() ? DetailTypeFormatter
.getNumberFormat(selectedSecondDetailType) : null;
chart.setToolTip(new ToolTip().setEnabled(true).setFormatter(new ToolTipFormatter() {
@Override
public String format(ToolTipData toolTipData) {
@@ -684,7 +679,7 @@ public abstract class AbstractCompetitorRaceChart<SettingsType extends ChartSett
}
return hasDetailTypeChanged;
}
private boolean isSecondYAxis(String seriesId) {
Series series = chart.getSeries(seriesId);
for (Entry<Pair<CompetitorDTO, DetailType>, Series> entry : dataSeriesForDetailTypeAndCompetitor.entrySet()) {
@@ -694,7 +689,7 @@ public abstract class AbstractCompetitorRaceChart<SettingsType extends ChartSett
}
return false;
}
private boolean hasSecondYAxis() {
return selectedSecondDetailType != null;
}
@@ -725,8 +720,7 @@ public abstract class AbstractCompetitorRaceChart<SettingsType extends ChartSett
* error happened or false, if the error happens before two passings were in the selection. B can be
* <code>null</code>.
*/
public com.sap.sse.common.Util.Pair<Boolean, Boolean> checkPassingRelationToSelection(
ArrayList<ArrayList<Boolean>> markPassingInRange) {
public com.sap.sse.common.Util.Pair<Boolean, Boolean> checkPassingRelationToSelection(ArrayList<ArrayList<Boolean>> markPassingInRange) {
boolean everyPassingInRange = true;
Boolean twoPassingsInRangeBeforeError = null;
ArrayList<Boolean> competitorPassings = markPassingInRange.get(0);
@@ -763,7 +757,7 @@ public abstract class AbstractCompetitorRaceChart<SettingsType extends ChartSett
if (allowTimeAdjust) {
updateTimePlotLine(newTime);
}
switch (timer.getPlayMode()) {
case Live: {
// is date before first cache entry or is cache empty?
@@ -771,8 +765,7 @@ public abstract class AbstractCompetitorRaceChart<SettingsType extends ChartSett
|| newTime.getTime() < primary.timeOfEarliestRequestInMillis) {
updateChart(timeRangeWithZoomProvider.getFromTime(), newTime, /* append */true);
} else if (newTime.getTime() > primary.timeOfLatestRequestInMillis) {
updateChart(new Date(primary.timeOfLatestRequestInMillis), timeRangeWithZoomProvider.getToTime(),
/* append */true);
updateChart(new Date(primary.timeOfLatestRequestInMillis), timeRangeWithZoomProvider.getToTime(), /* append */true);
}
// otherwise the cache spans across date and so we don't need to load anything
break;
@@ -780,16 +773,13 @@ public abstract class AbstractCompetitorRaceChart<SettingsType extends ChartSett
case Replay: {
if (primary.timeOfLatestRequestInMillis == null) {
// pure replay mode
updateChart(timeRangeWithZoomProvider.getFromTime(), timeRangeWithZoomProvider.getToTime(),
/* append */false);
updateChart(timeRangeWithZoomProvider.getFromTime(), timeRangeWithZoomProvider.getToTime(), /* append */false);
} else {
// replay mode during live play
if (primary.timeOfEarliestRequestInMillis == null
|| newTime.getTime() < primary.timeOfEarliestRequestInMillis) {
if (primary.timeOfEarliestRequestInMillis == null || newTime.getTime() < primary.timeOfEarliestRequestInMillis) {
updateChart(timeRangeWithZoomProvider.getFromTime(), newTime, /* append */true);
} else if (newTime.getTime() > primary.timeOfLatestRequestInMillis) {
updateChart(new Date(primary.timeOfLatestRequestInMillis), timeRangeWithZoomProvider.getToTime(),
/* append */true);
updateChart(new Date(primary.timeOfLatestRequestInMillis), timeRangeWithZoomProvider.getToTime(), /* append */true);
}
}
break;
@@ -810,7 +800,7 @@ public abstract class AbstractCompetitorRaceChart<SettingsType extends ChartSett
public void competitorsListChanged(Iterable<CompetitorDTO> competitors) {
timeChanged(timer.getTime(), null);
}
@Override
public void filteredCompetitorsListChanged(Iterable<CompetitorDTO> filteredCompetitors) {
timeChanged(timer.getTime(), null);
@@ -819,11 +809,10 @@ public abstract class AbstractCompetitorRaceChart<SettingsType extends ChartSett
@Override
public void filterChanged(FilterSet<CompetitorDTO, ? extends Filter<CompetitorDTO>> oldFilterSet,
FilterSet<CompetitorDTO, ? extends Filter<CompetitorDTO>> newFilterSet) {
// nothing to do; if it changes the filtered competitor list, a separate call to filteredCompetitorsListChanged
// will occur
// nothing to do; if it changes the filtered competitor list, a separate call to filteredCompetitorsListChanged will occur
}
static class TimingHolder {
static class TimingHolder{
private Long timeOfEarliestRequestInMillis;
private Long timeOfLatestRequestInMillis;
}