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

This commit is contained in:
Alexander Ries committed 2015-07-31 13:47:57 +02:00
commit ce2e6e7e5b
37 files changed
+814 -179

No files matched your search

@@ -56,4 +56,5 @@ NumberOfPenaltyCircles=Anzahl der Straf-Kreise
RankGainsOrLosses=Positions-Verluste oder -Gewinne
DistanceToStarboardSideAtStart=Distanz zur Steuerbordseite (% der Länge)
NumberOfPodiumFinish=Azahl der Podium-Siege
NumberOfWins=Anzahl der Siege
NumberOfWins=Anzahl der Siege
RankGainsOrLossesBetweenFirstMarkAndFinish=Positions-Verluste oder -Gewinne (1ter Wegpunkt -> Ziel)
@@ -56,4 +56,5 @@ NumberOfPenaltyCircles=Number of Penalty Circles
RankGainsOrLosses=Rank Gains or Losses
DistanceToStarboardSideAtStart=Distance to Starboard Side (% of Length)
NumberOfPodiumFinish=Number of Podium Finishes
NumberOfWins=Number of Wins
NumberOfWins=Number of Wins
RankGainsOrLossesBetweenFirstMarkAndFinish=Rank Gains or Losses (1st Mark -> Finish)
@@ -38,7 +38,7 @@ public interface HasRaceOfCompetitorContext {
@Statistic(messageKey="NumberOfPenaltyCircles", resultDecimals=2, ordinal=7)
public Double getNumberOfPenaltyCircles();
@Statistic(messageKey="RankGainsOrLosses", resultDecimals=2, ordinal=8)
public Double getRankGainsOrLosses();
@Statistic(messageKey="RankGainsOrLossesBetweenFirstMarkAndFinish", resultDecimals=2, ordinal=8)
public Double getRankGainsOrLossesBetweenFirstMarkAndFinish();
}
@@ -88,7 +88,7 @@ public class RaceOfCompetitorWithContext implements HasRaceOfCompetitorContext {
}
@Override
public Double getRankGainsOrLosses() {
public Double getRankGainsOrLossesBetweenFirstMarkAndFinish() {
Double rankAtFirstMark = getRankAtFirstMark();
Double rankAtFinish = getRankAtFinish();
return rankAtFirstMark - rankAtFinish;
@@ -40,7 +40,7 @@ public class RaceLogImpl extends AbstractLogImpl<RaceLogEvent, RaceLogEventVisit
@Override
public int getCurrentPassId() {
//return pass id of last event, as pass is the top-level sorting criterion in RaceLogeventComparator
//return pass id of last event, as pass is the top-level sorting criterion in RaceLogEventComparator
if (! getUnrevokedEvents().isEmpty()) {
return getUnrevokedEvents().last().getPassId();
} else {
@@ -0,0 +1,113 @@
package com.sap.sailing.domain.test;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.isNull;
import static org.mockito.Matchers.same;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.util.Set;
import org.junit.Before;
import org.junit.Test;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import com.sap.sailing.domain.base.RaceDefinition;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.WindSource;
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
import com.sap.sailing.domain.common.impl.DegreePosition;
import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl;
import com.sap.sailing.domain.common.impl.WindImpl;
import com.sap.sailing.domain.tracking.WindWithConfidence;
import com.sap.sailing.domain.tracking.impl.ShortTimeWindCache;
import com.sap.sailing.domain.tracking.impl.TrackedRaceImpl;
import com.sap.sailing.domain.tracking.impl.WindWithConfidenceImpl;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util.Pair;
import com.sap.sse.common.impl.MillisecondsTimePoint;
/**
* See also bug 3195
*
* @author Axel Uhl (d043530)
*
*/
public class ShortTimeWindCacheTest {
private static final long CACHE_ENTRY_LIFETIME_IN_MILLIS = /* preserveHowManyMilliseconds */ 100;
public static class MyShortTimeWindCache extends ShortTimeWindCache {
public MyShortTimeWindCache(TrackedRaceImpl trackedRace, long preserveHowManyMilliseconds) {
super(trackedRace, preserveHowManyMilliseconds);
}
public WindWithConfidence<com.sap.sse.common.Util.Pair<Position, TimePoint>> getWindWithConfidence(Position p,
TimePoint at, Set<WindSource> windSourcesToExclude) {
return super.getWindWithConfidence(p, at, windSourcesToExclude);
}
}
private MyShortTimeWindCache cache;
private TrackedRaceImpl trackedRace;
private TimePoint now;
private WindWithConfidence<Pair<Position, TimePoint>> result;
@SuppressWarnings("unchecked")
@Before
public void setUp() {
now = MillisecondsTimePoint.now();
trackedRace = mock(TrackedRaceImpl.class);
cache = new MyShortTimeWindCache(trackedRace, CACHE_ENTRY_LIFETIME_IN_MILLIS);
result = new WindWithConfidenceImpl<Pair<Position, TimePoint>>(
new WindImpl(new DegreePosition(54, 8), now, new KnotSpeedWithBearingImpl(12,
new DegreeBearingImpl(123))), 0.123, new Pair<>(null, now), /* useSpeed */true);
final RaceDefinition race = mock(RaceDefinition.class);
when(race.getName()).thenReturn("Mocked Test Race");
when(trackedRace.getRace()).thenReturn(race);
when(trackedRace.getWindWithConfidenceUncached(/* position */ any(), same(now), (Iterable<WindSource>) isNull(Iterable.class))).thenAnswer(new Answer<WindWithConfidence<Pair<Position, TimePoint>>>() {
@Override
public WindWithConfidence<Pair<Position, TimePoint>> answer(InvocationOnMock invocation) throws Throwable {
return result;
}
});
}
@Test
public void simpleSetupTest() throws InterruptedException {
final Position p1 = null;
final WindWithConfidence<Pair<Position, TimePoint>> firstResult = cache.getWindWithConfidence(p1, now, /* windSourcesToExclude */ null);
assertSame(result, firstResult);
Thread.sleep(CACHE_ENTRY_LIFETIME_IN_MILLIS/10); // wait a bit to make sure that the next cache entry will certainly not be removed by the timer removing the first entry
final Position p2 = new DegreePosition(54, 8);
final WindWithConfidence<Pair<Position, TimePoint>> secondResult = cache.getWindWithConfidence(p2, now, /* windSourcesToExclude */ null);
assertSame(result, secondResult);
Thread.sleep(CACHE_ENTRY_LIFETIME_IN_MILLIS/10); // wait a bit to make sure that the next cache entry will certainly not be removed by the timer removing the first entry
final Position p3 = new DegreePosition(55, 9);
final WindWithConfidence<Pair<Position, TimePoint>> thirdResult = cache.getWindWithConfidence(p3, now, /* windSourcesToExclude */ null);
assertSame(result, thirdResult);
final WindWithConfidence<Pair<Position, TimePoint>> oldResult = result;
result = null; // this is the new response of trackedRace.getWindWithConfidenceUncached
// assert that the trackedRace now responds with null
assertNull(trackedRace.getWindWithConfidenceUncached(/* position */ null, now, /* windSourcesToExclude */ null));
// assert that the cache still holds the cached values:
final WindWithConfidence<Pair<Position, TimePoint>> firstResult_2 = cache.getWindWithConfidence(p1, now, /* windSourcesToExclude */ null);
assertSame(oldResult, firstResult_2);
final WindWithConfidence<Pair<Position, TimePoint>> secondResult_2 = cache.getWindWithConfidence(p2, now, /* windSourcesToExclude */ null);
assertSame(oldResult, secondResult_2);
final WindWithConfidence<Pair<Position, TimePoint>> thirdResult_2 = cache.getWindWithConfidence(p3, now, /* windSourcesToExclude */ null);
assertSame(oldResult, thirdResult_2);
// now wait for the cache entries to be expired
Thread.sleep(CACHE_ENTRY_LIFETIME_IN_MILLIS+1000l);
final WindWithConfidence<Pair<Position, TimePoint>> firstResult_3 = cache.getWindWithConfidence(p1, now, /* windSourcesToExclude */ null);
assertNull(firstResult_3); // this one even works with bug3195 unfixed because the first element is removed properly from "order" and "cache"
final WindWithConfidence<Pair<Position, TimePoint>> secondResult_3 = cache.getWindWithConfidence(p2, now, /* windSourcesToExclude */ null);
assertNull(secondResult_3); // this one fails with bug3195 unfixed because the entry was removed while "peeking" into "order" using a pollFirst() call
// but the entry remained in "cache"
final WindWithConfidence<Pair<Position, TimePoint>> thirdResult_3 = cache.getWindWithConfidence(p3, now, /* windSourcesToExclude */ null);
assertNull(thirdResult_3);
}
}
@@ -328,7 +328,8 @@ public interface TrackedRace extends Serializable, IsManagedByCache<SharedDomain
/**
* Retrieves all wind sources known to this race, including those {@link #getWindSourcesToExclude() to exclude}.
* Callers can freely iterate because a copied collection is returned.
* Callers can freely iterate because a copied collection is returned. The {@link WindSourceType#COMBINED} wind source
* is never part of the result.
*/
Set<WindSource> getWindSources();
@@ -14,6 +14,7 @@ import com.sap.sailing.domain.tracking.WindWithConfidence;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util.Pair;
import com.sap.sse.common.Util.Triple;
import com.sap.sse.util.impl.ApproximateTime;
/**
* Caches wind information across a short duration of a few seconds, based on position and time point. A separate timer
@@ -49,7 +50,8 @@ public class ShortTimeWindCache {
public void run() {
long oldestToKeep = System.currentTimeMillis() - preserveHowManyMilliseconds;
Pair<Long, Triple<Position, TimePoint, Set<WindSource>>> next;
while ((next = order.pollFirst()) != null && next.getA() < oldestToKeep) {
while ((next = order.peekFirst()) != null && next.getA() < oldestToKeep) {
order.pollFirst();
cache.remove(next.getB());
}
synchronized (order) {
@@ -71,16 +73,19 @@ public class ShortTimeWindCache {
private void add(Triple<Position, TimePoint, Set<WindSource>> key, WindWithConfidence<com.sap.sse.common.Util.Pair<Position, TimePoint>> wind) {
cache.put(key, wind);
boolean orderEmpty = order.isEmpty();
synchronized (order) {
order.add(new Pair<Long, Triple<Position, TimePoint, Set<WindSource>>>(System.currentTimeMillis(), key));
if (orderEmpty) {
ensureTimerIsRunning();
final long timestamp;
if (preserveHowManyMilliseconds > 1000) {
timestamp = ApproximateTime.approximateNow().asMillis();
} else {
timestamp = System.currentTimeMillis();
}
order.add(new Pair<Long, Triple<Position, TimePoint, Set<WindSource>>>(timestamp, key));
ensureTimerIsRunning();
}
}
WindWithConfidence<com.sap.sse.common.Util.Pair<Position, TimePoint>> getWindWithConfidence(Position p,
protected WindWithConfidence<com.sap.sse.common.Util.Pair<Position, TimePoint>> getWindWithConfidence(Position p,
TimePoint at, Set<WindSource> windSourcesToExclude) {
WindWithConfidence<com.sap.sse.common.Util.Pair<Position, TimePoint>> wind;
final Triple<Position, TimePoint, Set<WindSource>> key = new Triple<Position, TimePoint, Set<WindSource>>(p, at, windSourcesToExclude);
+14
View File
@@ -25,6 +25,20 @@
</repositories>
<dependencies>
<!-- The following dependency needs re-declaration with the desired version because
gwt-maps-api declares it with no version specifier which may be resolved to an
incorrect version.
-->
<dependency>
<groupId>com.google.gwt</groupId>
<artifactId>gwt-user</artifactId>
<version>${gwt.version}</version>
</dependency>
<dependency>
<groupId>com.google.gwt</groupId>
<artifactId>gwt-dev</artifactId>
<version>${gwt.version}</version>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
@@ -1361,4 +1361,7 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages, c
String racesOverview();
String empty();
String runAQuery();
String plainText();
String columnChart();
String choosePresentation();
}
@@ -1328,3 +1328,6 @@ noFinishedRaces=There are no finished races yet
racesOverview=Races overview
empty=Empty
runAQuery=Run a Query
plainText=Plain Text
columnChart=Column Chart
choosePresentation=Choose Presentation
@@ -1318,3 +1318,6 @@ noFinishedRaces=Es wurden noch keine Wettfahrten beendet.
racesOverview=Wettfahrtübersicht
empty=Leer
runAQuery=Führe eine Anfrage aus
plainText=Einfacher Text
columnChart=Säulendiagramm
choosePresentation=Wähle Präsentation
@@ -1265,3 +1265,6 @@ noFinishedRaces=There are no finished races yet
racesOverview=Races overview
empty=Empty
runAQuery=Run a Query
plainText=Plain Text
columnChart=Column Chart
choosePresentation=Choose Presentation
@@ -1,8 +0,0 @@
package com.sap.sailing.gwt.ui.client.shared.panels;
import com.google.gwt.user.client.ui.RequiresResize;
import com.google.gwt.user.client.ui.SimplePanel;
public abstract class ResizingSimplePanel extends SimplePanel implements RequiresResize {
}
@@ -7,6 +7,8 @@ public interface ResultsPresenter<ResultType> {
public Widget getWidget();
public QueryResult<ResultType> getCurrentResult();
public void showResult(QueryResult<ResultType> result);
public void showError(String error);
@@ -0,0 +1,5 @@
package com.sap.sailing.gwt.ui.datamining;
public interface ResultsPresenterWithControls<ResultType> extends ResultsPresenter<ResultType>, WithControls {
}
@@ -2,7 +2,7 @@ package com.sap.sailing.gwt.ui.datamining;
import com.google.gwt.user.client.ui.Widget;
public interface DataMiningControls {
public interface WithControls {
public void addControl(Widget controlWidget);
@@ -91,5 +91,11 @@ public class PolarChart implements ResultsPresenter<Number> {
// TODO Auto-generated method stub
}
@Override
public QueryResult<Number> getCurrentResult() {
// TODO Auto-generated method stub
return null;
}
}
@@ -0,0 +1,144 @@
package com.sap.sailing.gwt.ui.datamining.presentation;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.dom.client.Style.Unit;
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.DockLayoutPanel;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.SimpleLayoutPanel;
import com.google.gwt.user.client.ui.Widget;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.datamining.ResultsPresenterWithControls;
import com.sap.sse.datamining.shared.QueryResult;
public abstract class AbstractResultsPresenter<ResultType> implements ResultsPresenterWithControls<ResultType> {
private enum ResultsPresenterState { BUSY, ERROR, RESULT }
private final StringMessages stringMessages;
private ResultsPresenterState state;
private final DockLayoutPanel mainPanel;
private final HorizontalPanel controlsPanel;
private final SimpleLayoutPanel presentationPanel;
private final HTML errorLabel;
private final HTML labeledBusyIndicator;
private QueryResult<ResultType> currentResult;
public AbstractResultsPresenter(StringMessages stringMessages) {
this.stringMessages = stringMessages;
mainPanel = new DockLayoutPanel(Unit.PX);
controlsPanel = new HorizontalPanel();
controlsPanel.setSpacing(5);
mainPanel.addNorth(controlsPanel, 40);
Button exportButton = new Button("Export");
exportButton.setEnabled(false);
exportButton.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
// TODO Do the magic
// Push the current result to the server
// Call a servlet to download the previously pushed result as json file
}
});
addControl(exportButton);
presentationPanel = new SimpleLayoutPanel();
mainPanel.add(presentationPanel);
errorLabel = new HTML();
errorLabel.setStyleName("chart-importantMessage");
labeledBusyIndicator = new HTML(stringMessages.runningQuery());
labeledBusyIndicator.setStyleName("chart-busyMessage");
showError(getStringMessages().runAQuery());
}
@Override
public void addControl(Widget controlWidget) {
controlsPanel.add(controlWidget);
}
@Override
public void showResult(QueryResult<ResultType> result) {
if (result != null && !result.isEmpty()) {
if (state != ResultsPresenterState.RESULT) {
mainPanel.setWidgetHidden(controlsPanel, false);
presentationPanel.setWidget(getPresentationWidget());
state = ResultsPresenterState.RESULT;
}
this.currentResult = result;
internalShowResult();
Scheduler.get().scheduleDeferred(new ScheduledCommand() {
@Override
public void execute() {
presentationPanel.onResize();
}
});
} else {
this.currentResult = null;
showError(getStringMessages().noDataFound() + ".");
}
}
protected abstract Widget getPresentationWidget();
protected abstract void internalShowResult();
@Override
public void showError(String error) {
if (state != ResultsPresenterState.ERROR) {
mainPanel.setWidgetHidden(controlsPanel, true);
errorLabel.setHTML(error);
state = ResultsPresenterState.ERROR;
}
currentResult = null;
presentationPanel.setWidget(errorLabel);
}
@Override
public void showError(String mainError, Iterable<String> detailedErrors) {
StringBuilder errorBuilder = new StringBuilder(mainError + ":<br /><ul>");
for (String detailedError : detailedErrors) {
errorBuilder.append("<li>" + detailedError + "</li>");
}
errorBuilder.append("</ul>");
showError(errorBuilder.toString());
}
@Override
public void showBusyIndicator() {
if (state != ResultsPresenterState.BUSY) {
presentationPanel.setWidget(labeledBusyIndicator);
state = ResultsPresenterState.BUSY;
}
currentResult = null;
}
protected StringMessages getStringMessages() {
return stringMessages;
}
@Override
public QueryResult<ResultType> getCurrentResult() {
return currentResult;
}
@Override
public Widget getWidget() {
return mainPanel;
}
}
@@ -0,0 +1,162 @@
package com.sap.sailing.gwt.ui.datamining.presentation;
import java.util.ArrayList;
import java.util.List;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.user.client.ui.DeckLayoutPanel;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ValueListBox;
import com.google.gwt.user.client.ui.Widget;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.client.shared.controls.AbstractObjectRenderer;
import com.sap.sailing.gwt.ui.datamining.ResultsPresenter;
import com.sap.sailing.gwt.ui.datamining.ResultsPresenterWithControls;
import com.sap.sse.datamining.shared.QueryResult;
public class MultiResultsPresenter implements ResultsPresenter<Number> {
private final StringMessages stringMessages;
private final DeckLayoutPanel presentersPanel;
private final HorizontalPanel controlsPanel;
private final ValueListBox<Descriptor> presentersListBox;
private ResultsPresenter<Number> currentPresenter;
private List<Descriptor> availableDescriptors;
public MultiResultsPresenter(StringMessages stringMessages) {
this.stringMessages = stringMessages;
availableDescriptors = new ArrayList<>();
availableDescriptors.add(new ColumnChartDescriptor());
availableDescriptors.add(new PlainDescriptor());
presentersPanel = new DeckLayoutPanel();
controlsPanel = new HorizontalPanel();
controlsPanel.setSpacing(5);
controlsPanel.add(new Label(stringMessages.choosePresentation() + ":"));
presentersListBox = new ValueListBox<>(new AbstractObjectRenderer<Descriptor>() {
@Override
protected String convertObjectToString(Descriptor nonNullObject) {
return nonNullObject.getName();
}
});
presentersListBox.addValueChangeHandler(new ValueChangeHandler<Descriptor>() {
@Override
public void onValueChange(ValueChangeEvent<Descriptor> event) {
setCurrentPresenter(event.getValue().getPresenter());
}
});
controlsPanel.add(presentersListBox);
presentersListBox.setValue(availableDescriptors.get(0), false);
presentersListBox.setAcceptableValues(availableDescriptors);
setCurrentPresenter(availableDescriptors.get(0).getPresenter());
}
private void setCurrentPresenter(ResultsPresenterWithControls<Number> presenter) {
controlsPanel.removeFromParent();
presenter.addControl(controlsPanel);
currentPresenter = presenter;
presentersPanel.setWidget(currentPresenter.getWidget());
Scheduler.get().scheduleDeferred(new ScheduledCommand() {
@Override
public void execute() {
presentersPanel.onResize();
}
});
}
@Override
public Widget getWidget() {
return presentersPanel;
}
@Override
public QueryResult<Number> getCurrentResult() {
return currentPresenter.getCurrentResult();
}
@Override
public void showResult(QueryResult<Number> result) {
for (Descriptor descriptor : availableDescriptors) {
descriptor.getPresenter().showResult(result);
}
}
@Override
public void showError(String error) {
for (Descriptor descriptor : availableDescriptors) {
descriptor.getPresenter().showError(error);
}
}
@Override
public void showError(String mainError, Iterable<String> detailedErrors) {
for (Descriptor descriptor : availableDescriptors) {
descriptor.getPresenter().showError(mainError, detailedErrors);
}
}
@Override
public void showBusyIndicator() {
for (Descriptor descriptor : availableDescriptors) {
descriptor.getPresenter().showBusyIndicator();
}
}
private interface Descriptor {
public String getName();
public ResultsPresenterWithControls<Number> getPresenter();
}
private class PlainDescriptor implements Descriptor {
private final AbstractResultsPresenter<Number> presenter;
public PlainDescriptor() {
presenter = new PlainResultsPresenter(stringMessages);
}
@Override
public String getName() {
return stringMessages.plainText();
}
@Override
public AbstractResultsPresenter<Number> getPresenter() {
return presenter;
}
}
private class ColumnChartDescriptor implements Descriptor {
private final ResultsChart presenter;
public ColumnChartDescriptor() {
presenter = new ResultsChart(stringMessages);
}
@Override
public String getName() {
return stringMessages.columnChart();
}
@Override
public AbstractResultsPresenter<Number> getPresenter() {
return presenter;
}
}
}
@@ -1,77 +1,57 @@
package com.sap.sailing.gwt.ui.datamining.presentation;
import java.util.Map.Entry;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.ScrollPanel;
import com.google.gwt.user.client.ui.Widget;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.datamining.ResultsPresenter;
import com.sap.sse.datamining.shared.GroupKey;
import com.sap.sse.datamining.shared.QueryResult;
import com.sap.sse.gwt.client.controls.busyindicator.BusyIndicator;
import com.sap.sse.gwt.client.controls.busyindicator.SimpleBusyIndicator;
public class PlainResultsPresenter implements ResultsPresenter<Number> {
public class PlainResultsPresenter extends AbstractResultsPresenter<Number> {
private final StringMessages stringMessages;
private final SimplePanel mainPanel;
private final ScrollPanel scrollPanel;
private final HTML resultsLabel;
private final BusyIndicator busyIndicator;
public PlainResultsPresenter(StringMessages stringMessages) {
this.stringMessages = stringMessages;
super(stringMessages);
mainPanel = new SimplePanel();
resultsLabel = new HTML();
mainPanel.setWidget(resultsLabel);
busyIndicator = new SimpleBusyIndicator(true, 0.7f);
scrollPanel = new ScrollPanel(resultsLabel);
}
@Override
public void showResult(QueryResult<Number> result) {
StringBuilder resultsBuilder = new StringBuilder("<b>" + result.getResultSignifier() + "</b></ br>");
resultsBuilder.append(stringMessages.queryResultsChartSubtitle(result.getRetrievedDataAmount(), result.getCalculationTimeInSeconds()));
protected void internalShowResult() {
QueryResult<Number> result = getCurrentResult();
Map<GroupKey, Number> resultValues = result.getResults();
resultsBuilder.append("<ul>");
for (Entry<GroupKey, Number> resultEntry : result.getResults().entrySet()) {
resultsBuilder.append("<li>");
resultsBuilder.append("<b>" + resultEntry.getKey().toString() + "</b>: ");
resultsBuilder.append(resultEntry.getValue().doubleValue());
resultsBuilder.append("</li>");
StringBuilder resultsBuilder = new StringBuilder("<b>" + result.getResultSignifier() + "</b></ br>");
resultsBuilder.append("<table>");
for (GroupKey key : getSortedKeys(result)) {
resultsBuilder.append("<tr>");
resultsBuilder.append("<td><b>" + key.toString() + "</b>:</td>");
resultsBuilder.append("<td>" + resultValues.get(key).doubleValue() + "</td>");
resultsBuilder.append("</tr>");
}
resultsBuilder.append("</ul>");
resultsBuilder.append("</table>");
resultsLabel.setHTML(resultsBuilder.toString());
mainPanel.setWidget(resultsLabel);
}
@Override
public void showError(String error) {
resultsLabel.setHTML(error);
mainPanel.setWidget(resultsLabel);
}
@Override
public void showError(String mainError, Iterable<String> detailedErrors) {
StringBuilder errorBuilder = new StringBuilder(mainError + ":<br /><ul>");
for (String detailedError : detailedErrors) {
errorBuilder.append("<li>" + detailedError + "</li>");
}
errorBuilder.append("</ul>");
showError(errorBuilder.toString());
}
@Override
public void showBusyIndicator() {
mainPanel.setWidget(busyIndicator);
private Iterable<GroupKey> getSortedKeys(QueryResult<Number> result) {
List<GroupKey> sortedKeys = new ArrayList<>(result.getResults().keySet());
Collections.sort(sortedKeys);
return sortedKeys;
}
@Override
public Widget getWidget() {
return mainPanel;
protected Widget getPresentationWidget() {
return scrollPanel;
}
}
@@ -15,6 +15,7 @@ import org.moxieapps.gwt.highcharts.client.ChartSubtitle;
import org.moxieapps.gwt.highcharts.client.ChartTitle;
import org.moxieapps.gwt.highcharts.client.Color;
import org.moxieapps.gwt.highcharts.client.Credits;
import org.moxieapps.gwt.highcharts.client.Exporting;
import org.moxieapps.gwt.highcharts.client.Point;
import org.moxieapps.gwt.highcharts.client.Series;
import org.moxieapps.gwt.highcharts.client.ToolTip;
@@ -22,24 +23,19 @@ import org.moxieapps.gwt.highcharts.client.labels.AxisLabelsData;
import org.moxieapps.gwt.highcharts.client.labels.AxisLabelsFormatter;
import org.moxieapps.gwt.highcharts.client.labels.YAxisLabels;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.text.shared.AbstractRenderer;
import com.google.gwt.user.client.ui.DockLayoutPanel;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.SimpleLayoutPanel;
import com.google.gwt.user.client.ui.ValueListBox;
import com.google.gwt.user.client.ui.Widget;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.client.shared.panels.ResizingSimplePanel;
import com.sap.sailing.gwt.ui.datamining.ResultsPresenter;
import com.sap.sse.datamining.shared.GroupKey;
import com.sap.sse.datamining.shared.QueryResult;
import com.sap.sse.datamining.shared.impl.GenericGroupKey;
public class ResultsChart implements ResultsPresenter<Number> {
public class ResultsChart extends AbstractResultsPresenter<Number> {
private final Comparator<GroupKey> standardKeyComparator = new Comparator<GroupKey>() {
@Override
@@ -48,20 +44,20 @@ public class ResultsChart implements ResultsPresenter<Number> {
}
@Override
public String toString() {
return stringMessages.group();
return getStringMessages().group();
};
};
private final Comparator<GroupKey> ascendingByValueKeyComparator = new Comparator<GroupKey>() {
@Override
public int compare(GroupKey key1, GroupKey key2) {
Map<GroupKey, ? extends Number> resultData = currentResult.getResults();
Map<GroupKey, ? extends Number> resultData = getCurrentResult().getResults();
double doubleValue1 = resultData.get(key1).doubleValue();
double doubleValue2 = resultData.get(key2).doubleValue();
return Double.compare(doubleValue1, doubleValue2);
}
@Override
public String toString() {
return stringMessages.valueAscending();
return getStringMessages().valueAscending();
};
};
private final Comparator<GroupKey> descendingByValueKeyComparator = new Comparator<GroupKey>() {
@@ -71,31 +67,21 @@ public class ResultsChart implements ResultsPresenter<Number> {
}
@Override
public String toString() {
return stringMessages.valueDescending();
return getStringMessages().valueDescending();
};
};
private final StringMessages stringMessages;
private final DockLayoutPanel mainPanel;
private final HorizontalPanel sortByPanel;
private final ValueListBox<Comparator<GroupKey>> keyComparatorListBox;
private final ResizingSimplePanel presentationPanel;
private final SimpleLayoutPanel chartPanel;
private final Chart chart;
private final HTML errorLabel;
private final HTML labeledBusyIndicator;
private Map<GroupKey, Series> seriesMappedByGroupKey;
private final GroupKey simpleResultSeriesKey;
private QueryResult<? extends Number> currentResult;
private Map<GroupKey, Integer> mainKeyToXValueMap;
public ResultsChart(StringMessages stringMessages) {
super();
this.stringMessages = stringMessages;
mainPanel = new DockLayoutPanel(Unit.PX);
super(stringMessages);
sortByPanel = new HorizontalPanel();
sortByPanel.setSpacing(5);
@@ -114,87 +100,55 @@ public class ResultsChart implements ResultsPresenter<Number> {
}
});
sortByPanel.add(keyComparatorListBox);
mainPanel.addNorth(sortByPanel, 40);
mainPanel.setWidgetHidden(sortByPanel, true);
sortByPanel.setVisible(false);
addControl(sortByPanel);
presentationPanel = new ResizingSimplePanel() {
chartPanel = new SimpleLayoutPanel() {
@Override
public void onResize() {
chart.setSizeToMatchContainer();
chart.redraw();
}
};
mainPanel.add(presentationPanel);
chart = createChart();
chartPanel.setWidget(chart);
seriesMappedByGroupKey = new HashMap<GroupKey, Series>();
simpleResultSeriesKey = new GenericGroupKey<String>(stringMessages.results());
errorLabel = new HTML();
errorLabel.setStyleName("chart-importantMessage");
labeledBusyIndicator = new HTML(stringMessages.runningQuery());
labeledBusyIndicator.setStyleName("chart-busyMessage");
showError(this.stringMessages.invalidSelection());
}
@Override
public void showError(String error) {
currentResult = null;
errorLabel.setHTML(error);
presentationPanel.setWidget(errorLabel);
}
@Override
public void showError(String mainError, Iterable<String> detailedErrors) {
StringBuilder errorBuilder = new StringBuilder(mainError + ":<br /><ul>");
for (String detailedError : detailedErrors) {
errorBuilder.append("<li>" + detailedError + "</li>");
}
errorBuilder.append("</ul>");
showError(errorBuilder.toString());
}
@Override
public void showBusyIndicator() {
currentResult = null;
presentationPanel.setWidget(labeledBusyIndicator);
protected Widget getPresentationWidget() {
return chartPanel;
}
@Override
public void showResult(QueryResult<Number> result) {
if (result != null && !result.isEmpty()) {
currentResult = result;
updateKeyComparatorListBox();
resetChartSeries();
updateYAxisLabels();
updateChartSubtitleAndSetChartAsWidget();
showResultData();
} else {
showError(stringMessages.noDataFound() + ".");
}
protected void internalShowResult() {
updateKeyComparatorListBox();
resetChartSeries();
updateYAxisLabels();
updateChartSubtitleAndSetChartAsWidget();
showResultData();
}
private void updateKeyComparatorListBox() {
boolean hidden = true;
boolean visible = false;
Comparator<GroupKey> valueToBeSelected = standardKeyComparator;
Collection<Comparator<GroupKey>> acceptableValues = new ArrayList<>();
acceptableValues.add(valueToBeSelected);
if (currentResult != null && isCurrentResultSimple()) {
if (getCurrentResult() != null && isCurrentResultSimple()) {
valueToBeSelected = getKeyComparator() != null ? getKeyComparator() : valueToBeSelected;
acceptableValues.add(ascendingByValueKeyComparator);
acceptableValues.add(descendingByValueKeyComparator);
hidden = false;
visible = true;
}
keyComparatorListBox.setValue(valueToBeSelected);
keyComparatorListBox.setAcceptableValues(acceptableValues);
mainPanel.setWidgetHidden(sortByPanel, hidden);
mainPanel.forceLayout();
sortByPanel.setVisible(visible);
}
private boolean isCurrentResultSimple() {
for (GroupKey groupKey : currentResult.getResults().keySet()) {
for (GroupKey groupKey : getCurrentResult().getResults().keySet()) {
if (groupKey.hasSubKey()) {
return false;
}
@@ -208,24 +162,21 @@ public class ResultsChart implements ResultsPresenter<Number> {
}
private void updateYAxisLabels() {
chart.getYAxis().setAxisTitleText(currentResult.getResultSignifier());
chart.setToolTip(new ToolTip().setValueDecimals(currentResult.getValueDecimals()).setValueSuffix(
currentResult.getUnitSignifier()));
chart.getYAxis().setAxisTitleText(getCurrentResult().getResultSignifier());
chart.setToolTip(new ToolTip().setValueDecimals(getCurrentResult().getValueDecimals()).setValueSuffix(
getCurrentResult().getUnitSignifier()));
}
private void updateChartSubtitleAndSetChartAsWidget() {
chart.setChartSubtitle(new ChartSubtitle().setText(stringMessages.queryResultsChartSubtitle(
currentResult.getRetrievedDataAmount(), currentResult.getCalculationTimeInSeconds())));
// This is needed, so that the subtitle is updated. Otherwise the text would stay empty
presentationPanel.setWidget(null);
presentationPanel.setWidget(chart);
chart.setChartSubtitle(new ChartSubtitle().setText(getStringMessages().queryResultsChartSubtitle(
getCurrentResult().getRetrievedDataAmount(), getCurrentResult().getCalculationTimeInSeconds())));
}
private void showResultData() {
buildMainKeyMapAndSetXAxisCategories();
createAndAddSeriesToChart();
for (Entry<GroupKey, ? extends Number> resultEntry : currentResult.getResults().entrySet()) {
for (Entry<GroupKey, ? extends Number> resultEntry : getCurrentResult().getResults().entrySet()) {
GroupKey mainKey = resultEntry.getKey().getMainKey();
Point point = new Point(mainKeyToXValueMap.get(mainKey), resultEntry.getValue());
point.setName(mainKey.asString());
@@ -248,9 +199,9 @@ public class ResultsChart implements ResultsPresenter<Number> {
chart.getXAxis().setCategories(false, categories);
}
public List<GroupKey> getSortedMainKeys() {
private List<GroupKey> getSortedMainKeys() {
Collection<GroupKey> mainKeySet = new HashSet<>();
for (GroupKey groupKey : currentResult.getResults().keySet()) {
for (GroupKey groupKey : getCurrentResult().getResults().keySet()) {
mainKeySet.add(groupKey.getMainKey());
}
List<GroupKey> sortedKeys = new ArrayList<>(mainKeySet);
@@ -263,7 +214,7 @@ public class ResultsChart implements ResultsPresenter<Number> {
}
private void createAndAddSeriesToChart() {
for (GroupKey groupKey : currentResult.getResults().keySet()) {
for (GroupKey groupKey : getCurrentResult().getResults().keySet()) {
GroupKey seriesKey = groupKeyToSeriesKey(groupKey);
if (!seriesMappedByGroupKey.containsKey(seriesKey)) {
seriesMappedByGroupKey.put(seriesKey, chart.createSeries().setName(seriesKey.asString()));
@@ -290,7 +241,9 @@ public class ResultsChart implements ResultsPresenter<Number> {
.setBorderColor(new Color("#F0AB00"))
.setPlotBorderWidth(0)
.setCredits(new Credits().setEnabled(false))
.setChartTitle(new ChartTitle().setText(stringMessages.dataMiningResult()));
.setChartTitle(new ChartTitle().setText(getStringMessages().dataMiningResult()));
chart.setExporting(new Exporting().setEnabled(false));
chart.getXAxis().setAllowDecimals(false);
@@ -307,10 +260,5 @@ public class ResultsChart implements ResultsPresenter<Number> {
return chart;
}
@Override
public Widget getWidget() {
return mainPanel;
}
}
@@ -86,6 +86,11 @@ public class TabbedResultsPresenter implements ResultsPresenter<Number> {
getSelectedPresenter().showBusyIndicator();
}
@Override
public QueryResult<Number> getCurrentResult() {
return getSelectedPresenter().getCurrentResult();
}
private CloseableTabHeader getSelectedHeader() {
return (CloseableTabHeader) tabPanel.getTabWidget(tabPanel.getSelectedIndex());
}
@@ -96,8 +101,7 @@ public class TabbedResultsPresenter implements ResultsPresenter<Number> {
private void addTabAndFocus() {
CloseableTabHeader tabHeader = new CloseableTabHeader();
ResultsChart tabPresenter = new ResultsChart(stringMessages);
tabPresenter.showError(stringMessages.runAQuery());
ResultsPresenter<Number> tabPresenter = new MultiResultsPresenter(stringMessages);
presentersMappedByHeader.put(tabHeader, tabPresenter);
tabPanel.insert(tabPresenter.getWidget(), tabHeader, tabPanel.getWidgetCount() - 1);
@@ -19,7 +19,7 @@ import com.google.gwt.user.client.ui.SplitLayoutPanel;
import com.google.gwt.user.client.ui.Widget;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.datamining.DataMiningComponentProvider;
import com.sap.sailing.gwt.ui.datamining.DataMiningControls;
import com.sap.sailing.gwt.ui.datamining.WithControls;
import com.sap.sailing.gwt.ui.datamining.DataMiningServiceAsync;
import com.sap.sailing.gwt.ui.datamining.DataRetrieverChainDefinitionChangedListener;
import com.sap.sailing.gwt.ui.datamining.DataRetrieverChainDefinitionProvider;
@@ -40,7 +40,7 @@ import com.sap.sse.datamining.shared.impl.dto.ModifiableStatisticQueryDefinition
import com.sap.sse.gwt.client.ErrorReporter;
import com.sap.sse.gwt.client.shared.components.SettingsDialogComponent;
public class BufferingQueryDefinitionProviderWithControls extends AbstractQueryDefinitionProvider implements DataMiningControls {
public class BufferingQueryDefinitionProviderWithControls extends AbstractQueryDefinitionProvider implements WithControls {
/**
* The delay before a changed query definition is submitted to the listeners.
@@ -96,13 +96,6 @@
version="1.6.4.1"
unpack="false"/>
<plugin
id="rome"
download-size="0"
install-size="0"
version="1.0.0"
unpack="false"/>
<plugin
id="routeconverter"
download-size="0"
@@ -226,7 +226,6 @@
<plugin id="org.objectweb.asm"/>
<plugin id="org.objectweb.asm.source"/>
<plugin id="org.objenesis"/>
<plugin id="rome"/>
<plugin id="routeconverter"/>
<plugin id="slf4j.api"/>
<plugin id="slf4j.jdk14"/>
+2 -2
View File
@@ -14,6 +14,6 @@ Export-Package: com.sap.sse.common,
com.sap.sse.common.search,
com.sap.sse.common.settings,
com.sap.sse.common.util
Require-Bundle: com.sap.sse.datamining.shared;bundle-version="1.0.0",
org.json.simple;bundle-version="1.1.0"
Require-Bundle: org.json.simple;bundle-version="1.1.0",
com.sap.sse.datamining.shared;bundle-version="1.0.0"
Bundle-ActivationPolicy: lazy
@@ -10,5 +10,6 @@ Export-Package: com.sap.sse.datamining.shared,
com.sap.sse.datamining.shared.data,
com.sap.sse.datamining.shared.dto,
com.sap.sse.datamining.shared.impl,
com.sap.sse.datamining.shared.impl.dto
com.sap.sse.datamining.shared.impl.dto,
com.sap.sse.datamining.shared.util
Bundle-ActivationPolicy: lazy
@@ -6,4 +6,5 @@
<source path='shared/dto'/>
<source path='shared/impl'/>
<source path='shared/impl/dto'/>
<source path='shared/util'/>
</module>
@@ -1,6 +1,7 @@
package com.sap.sse.datamining.shared.impl;
import com.sap.sse.datamining.shared.GroupKey;
import com.sap.sse.datamining.shared.util.AlphanumericalComparator;
public class GenericGroupKey<T> extends AbstractGroupKey {
@@ -50,6 +51,10 @@ public class GenericGroupKey<T> extends AbstractGroupKey {
if (value instanceof Enum && keyValue instanceof Enum) {
result = ((Enum<?>) value).ordinal() - ((Enum<?>) keyValue).ordinal();
}
if (value instanceof String && keyValue instanceof String) {
AlphanumericalComparator comparator = new AlphanumericalComparator();
result = comparator.compare((String) value, (String) keyValue);
}
}
return result;
}
@@ -0,0 +1,78 @@
package com.sap.sse.datamining.shared.util;
import java.io.Serializable;
import java.util.Comparator;
public class AlphanumericalComparator implements Comparator<String>, Serializable {
private static final long serialVersionUID = 1932325900088375762L;
private final boolean isDigit(char ch) {
return ch >= 48 && ch <= 57;
}
/** Length of string is passed in for improved efficiency (only need to calculate it once) **/
private final String getChunk(String s, int slength, int marker) {
StringBuilder chunk = new StringBuilder();
char c = s.charAt(marker);
chunk.append(c);
marker++;
if (isDigit(c)) {
while (marker < slength) {
c = s.charAt(marker);
if (!isDigit(c))
break;
chunk.append(c);
marker++;
}
} else {
while (marker < slength) {
c = s.charAt(marker);
if (isDigit(c))
break;
chunk.append(c);
marker++;
}
}
return chunk.toString();
}
public int compare(String s1, String s2) {
int thisMarker = 0;
int thatMarker = 0;
int s1Length = s1.length();
int s2Length = s2.length();
while (thisMarker < s1Length && thatMarker < s2Length) {
String thisChunk = getChunk(s1, s1Length, thisMarker);
thisMarker += thisChunk.length();
String thatChunk = getChunk(s2, s2Length, thatMarker);
thatMarker += thatChunk.length();
// If both chunks contain numeric characters, sort them numerically
int result = 0;
if (isDigit(thisChunk.charAt(0)) && isDigit(thatChunk.charAt(0))) {
// Simple chunk comparison by length.
int thisChunkLength = thisChunk.length();
result = thisChunkLength - thatChunk.length();
// If equal, the first different number counts
if (result == 0) {
for (int i = 0; i < thisChunkLength; i++) {
result = thisChunk.charAt(i) - thatChunk.charAt(i);
if (result != 0) {
return result;
}
}
}
} else {
result = thisChunk.compareTo(thatChunk);
}
if (result != 0)
return result;
}
return s1Length - s2Length;
}
}
+3 -2
View File
@@ -147,6 +147,7 @@
<properties>
<tycho-version>0.21.0</tycho-version>
<gwt.version>2.7.0</gwt.version>
<gwt.minVersion>2.7.0</gwt.minVersion>
<gwt.workers>2</gwt.workers>
<p2-target>race-analysis-p2-remote</p2-target><!--can be overidden through p2-target.local profile -->
<!-- QUESTION: Which properties or settings are system dependent and differ across different machines/environments? QUESTION:
@@ -170,6 +171,7 @@
<!-- "Forward declaration" for better readability. The actual value should be defined in the local settings.xml, because
of the differences between the CI-Build and the local setup. -->
<parameters.proxy>
-Dhttp.proxyHost=proxy.wdf.sap.corp -Dhttp.proxyPort=8080 -Dhttps.proxyHost=proxy.wdf.sap.corp -Dhttps.proxyPort=8080
</parameters.proxy>
<reportsdirectory>${project.build.directory}/surefire-reports</reportsdirectory>
<skipTests>false</skipTests>
@@ -327,8 +329,7 @@
<artifactId>tycho-compiler-plugin</artifactId>
<version>${tycho-version}</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<useJDK>BREE</useJDK>
</configuration>
</plugin>