diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/ControlPointTableWrapper.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/ControlPointTableWrapper.java new file mode 100644 index 00000000000..1002b056cbc --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/ControlPointTableWrapper.java @@ -0,0 +1,41 @@ +package com.sap.sailing.gwt.ui.adminconsole; + +import com.google.gwt.user.cellview.client.TextColumn; +import com.google.gwt.view.client.SelectionModel; +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.ControlPointDTO; +import com.sap.sailing.gwt.ui.shared.GateDTO; +import com.sap.sailing.gwt.ui.shared.MarkDTO; + +public class ControlPointTableWrapper> extends TableWrapper { + public ControlPointTableWrapper(S selectionModel, SailingServiceAsync sailingService, final StringMessages stringMessages, + ErrorReporter errorReporter) { + super(sailingService, stringMessages, errorReporter, selectionModel); + + mainPanel.add(table); + + TextColumn nameColumn = new TextColumn() { + @Override + public String getValue(ControlPointDTO d) { + return d.getName(); + } + }; + table.addColumn(nameColumn, stringMessages.name()); + + TextColumn typeColumn = new TextColumn() { + @Override + public String getValue(ControlPointDTO d) { + if (d instanceof GateDTO) { + return "2x " + stringMessages.mark(); + } else if (d instanceof MarkDTO) { + return stringMessages.mark(); + } else { + return stringMessages.unknown(); + } + } + }; + table.addColumn(typeColumn, stringMessages.type()); + } +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CourseManagementWidget.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CourseManagementWidget.java index 0ff11ee3117..d16dc2c0291 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CourseManagementWidget.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CourseManagementWidget.java @@ -1,34 +1,28 @@ package com.sap.sailing.gwt.ui.adminconsole; import java.util.ArrayList; -import java.util.HashSet; -import java.util.Iterator; +import java.util.HashMap; import java.util.List; -import java.util.Set; +import java.util.Map; import com.google.gwt.cell.client.FieldUpdater; -import com.google.gwt.core.client.GWT; -import com.google.gwt.dom.client.Style.FontWeight; +import com.google.gwt.core.shared.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.ui.Button; +import com.google.gwt.user.client.ui.CaptionPanel; 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.IsWidget; -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.google.gwt.view.client.ListDataProvider; import com.google.gwt.view.client.MultiSelectionModel; import com.google.gwt.view.client.SelectionChangeEvent; import com.google.gwt.view.client.SelectionChangeEvent.Handler; +import com.google.gwt.view.client.SetSelectionModel; import com.google.gwt.view.client.SingleSelectionModel; import com.sap.sailing.domain.common.PassingInstruction; -import com.sap.sailing.domain.common.impl.Util.Pair; +import com.sap.sailing.domain.common.impl.Util; import com.sap.sailing.gwt.ui.client.ErrorReporter; import com.sap.sailing.gwt.ui.client.SailingServiceAsync; import com.sap.sailing.gwt.ui.client.StringMessages; @@ -40,180 +34,25 @@ import com.sap.sailing.gwt.ui.shared.WaypointDTO; import com.sap.sse.gwt.client.dialog.DataEntryDialog; public abstract class CourseManagementWidget implements IsWidget { - /** - * Represents one mark assignment for a control point. Gates have multiple such records, one for each of their marks. - * - * @author Axel Uhl (D043530) - */ - public static class ControlPointAndOldAndNewMark { - protected final ControlPointDTO controlPoint; - protected final MarkDTO oldMark; - protected MarkDTO newMark; - protected PassingInstruction passingInstructions; - public ControlPointAndOldAndNewMark(ControlPointDTO controlPoint, PassingInstruction passingInstructions, MarkDTO oldMark) { - super(); - this.controlPoint = controlPoint; - this.passingInstructions = passingInstructions; - this.oldMark = oldMark; - this.newMark = oldMark; - } - public PassingInstruction getPassingInstructions() { - return passingInstructions; - } - public MarkDTO getNewMark() { - return newMark; - } - public void setNewMark(MarkDTO newMark) { - this.newMark = newMark; - } - public ControlPointDTO getControlPoint() { - return controlPoint; - } - public MarkDTO getOldMark() { - return oldMark; - } - } + protected final MarkTableWrapper> marks; + protected final ControlPointTableWrapper> multiMarkControlPoints; + protected final WaypointTableWrapper> waypoints; - private class ControlPointCreationDialog extends DataEntryDialog> { - private final MarkTableWrapper> marksTable; - private final MultiSelectionModel selectionModel; - private final ListBox passingInstructions; - private final StringMessages stringMessages; - - public ControlPointCreationDialog(final StringMessages stringMessages, AdminConsoleTableResources tableRes, - List marks, DialogCallback> callback) { - super(stringMessages.controlPoint(), stringMessages.selectOneMarkOrTwoMarksForGate(), - stringMessages.ok(), stringMessages.cancel(), new DataEntryDialog.Validator>() { - @Override - public String getErrorMessage(Pair valueToValidate) { - if (valueToValidate.getA() == null) { - return stringMessages.selectOneMarkOrTwoMarksForGate(); - } else { - return null; - } - } - - }, /* animationEnabled */ false, callback); - - this.stringMessages = stringMessages; - - selectionModel = new MultiSelectionModel(); - selectionModel.addSelectionChangeHandler(new Handler() { - @Override - public void onSelectionChange(SelectionChangeEvent event) { - updatePassingInstructions(); - validate(); - } - }); - marksTable = new MarkTableWrapper>( - selectionModel, CourseManagementWidget.this.sailingService, stringMessages, errorReporter); - marksTable.getDataProvider().getList().addAll(marks); - - passingInstructions = createListBox(false); - updatePassingInstructions(); - } - - private void updatePassingInstructions() { - passingInstructions.clear(); - - int numSelectedMarks = selectionModel.getSelectedSet().size(); - int i = 0; - passingInstructions.insertItem(PassingInstruction.None.name(), i++); - for (PassingInstruction pi : PassingInstruction.relevantValues()) { - for (int numApplicableMarks : pi.applicability) { - if (numApplicableMarks == numSelectedMarks) { - passingInstructions.insertItem(pi.name(), i++); - } - } - } - } - - @Override - protected Pair getResult() { - ControlPointDTO controlPoint = null; - Set selection = selectionModel.getSelectedSet(); - if (selection.size() == 1) { - controlPoint = selectionModel.getSelectedSet().iterator().next(); - } else if (selection.size() == 2) { - Iterator i = selectionModel.getSelectedSet().iterator(); - MarkDTO first = i.next(); - MarkDTO second = i.next(); - MarkDTO left; - MarkDTO right; - String gateName; - if (first.getName().matches("^.*"+REGEX_FOR_LEFT+".*$")) { - left = first; - right = second; - } else { - left = second; - right = first; - } - gateName = left.getName().replaceFirst(REGEX_FOR_LEFT, ""); - controlPoint = new GateDTO(/* generate UUID on the server */ null, gateName, left, right); - } - - PassingInstruction passingInstruction = - passingInstructions.getSelectedIndex() == -1 ? PassingInstruction.None : - PassingInstruction.valueOf(passingInstructions.getValue(passingInstructions.getSelectedIndex())); - - return new Pair(controlPoint, passingInstruction); - } - - @Override - protected Widget getAdditionalWidget() { - Grid grid = new Grid(2,1); - grid.setWidget(0, 0, marksTable); - - HorizontalPanel passingInstructionsRow = new HorizontalPanel(); - grid.setWidget(1, 0, passingInstructionsRow); - passingInstructionsRow.add(new Label(stringMessages.passingInstructions() + ":")); - passingInstructionsRow.add(passingInstructions); - - return grid; - } - } + protected final Grid mainPanel; - protected static final String REGEX_FOR_LEFT = "( \\()?(([lL][eE][fF][tT])|(1))\\)?"; - - /** - * A table that lists the marks for which events have been received for the race selected. Note that this list may - * be longer than the list of marks actually used by the control points backing the course's waypoints because of - * the possibility of spare marks. - */ - protected final SingleSelectionModel markSelectionModel; - - /** - * A table that lists the product of Waypoint x ControlPoint x Mark plus a hint as to the number of mark passings. - * The (multi-)selection on this table can be used as either a selection of waypoints or a selection of control points - * or a selection of marks. - */ - protected final CellTable controlPointsTable; - protected final MultiSelectionModel controlPointsSelectionModel; - protected final ListDataProvider controlPointDataProvider; - - /** - * When for a control point's mark a replacement mark is defined (see {@link #updateNewMark(Set, MarkDTO)}), - * the control point needs to be replaced before {@link #saveCourse(SailingServiceAsync, StringMessages) saving}. - * Those control points are added to this set. When the mark is reset to the original mark for all the control - * point's marks, the control point is removed from this set again. {@link #saveCourse(SailingServiceAsync, StringMessages)} - * then is responsible for creating replacement {@link ControlPointDTO}s before sending the new control point list to the - * server. - */ - protected final Set controlPointsNeedingReplacement; - - protected final HorizontalPanel courseActionsPanel; - - protected final VerticalPanel mainPanel; - - protected MarkTableWrapper> marksTable; - - protected final Handler markSelectionChangeHandler; - protected final Button insertWaypointBefore; - protected final Button insertWaypointAfter; - protected final Button saveButton; - protected boolean ignoreWaypointAndOldAndNewMarkSelectionChange; protected final SailingServiceAsync sailingService; protected final ErrorReporter errorReporter; + protected final StringMessages stringMessages; + + protected final HorizontalPanel waypointsBtnsPanel; + protected final HorizontalPanel controlPointsBtnsPanel; + protected final HorizontalPanel marksBtnsPanel; + protected final HorizontalPanel buttonsPanel; + + protected final Button insertWaypointBefore; + protected final Button insertWaypointAfter; + + protected final AdminConsoleTableResources tableRes = GWT.create(AdminConsoleTableResources.class); @Override public Widget asWidget() { @@ -224,119 +63,101 @@ public abstract class CourseManagementWidget implements IsWidget { final StringMessages stringMessages) { this.sailingService = sailingService; this.errorReporter = errorReporter; - mainPanel = new VerticalPanel(); + this.stringMessages = stringMessages; - controlPointsNeedingReplacement = new HashSet(); - Grid grid = new Grid(2, 2); - grid.setCellPadding(5); - - mainPanel.add(grid); + mainPanel = new Grid(3, 3); + mainPanel.setCellPadding(5); + mainPanel.getRowFormatter().setVerticalAlign(0, HasVerticalAlignment.ALIGN_TOP); - Label currentRaceCourseLabel = new Label("Current race course"); - currentRaceCourseLabel.getElement().getStyle().setFontWeight(FontWeight.BOLD); - grid.setWidget(0, 0, currentRaceCourseLabel); - Label availableMarksLabel = new Label("Available marks"); - availableMarksLabel.getElement().getStyle().setFontWeight(FontWeight.BOLD); - grid.setWidget(0, 1, availableMarksLabel); - grid.getRowFormatter().setVerticalAlign(1, HasVerticalAlignment.ALIGN_TOP); + waypoints = new WaypointTableWrapper>( + new SingleSelectionModel(), sailingService, stringMessages, errorReporter); + multiMarkControlPoints = new ControlPointTableWrapper>( + new MultiSelectionModel(), sailingService, stringMessages, errorReporter); + marks = new MarkTableWrapper>( + new MultiSelectionModel(), sailingService, stringMessages, errorReporter); - final AdminConsoleTableResources tableRes = GWT.create(AdminConsoleTableResources.class); - controlPointsTable = new CellTable(/* pageSize */10000, tableRes); - grid.setWidget(1, 0, controlPointsTable); - controlPointsSelectionModel = new MultiSelectionModel(); - controlPointsTable.setSelectionModel(controlPointsSelectionModel); - controlPointsSelectionModel.addSelectionChangeHandler(new Handler() { - @Override - public void onSelectionChange(SelectionChangeEvent event) { - handleControlPointSelectionChange(); - } - }); - TextColumn nameColumn = new TextColumn() { - @Override - public String getValue(ControlPointAndOldAndNewMark cpaoanm) { - return cpaoanm.getControlPoint().getName(); - } - }; - controlPointsTable.addColumn(nameColumn, stringMessages.controlPoint()); - TextColumn passingInstructionsColumn = new TextColumn() { - @Override - public String getValue(ControlPointAndOldAndNewMark cpaoanm) { - String result = ""; - if(cpaoanm.getPassingInstructions() != null) { - result = cpaoanm.getPassingInstructions().name(); - } - return result; - } - }; - controlPointsTable.addColumn(passingInstructionsColumn, "Passing instructions"); - TextColumn oldMarkColumn = new TextColumn() { - @Override - public String getValue(ControlPointAndOldAndNewMark cpaoanm) { - return "" + cpaoanm.getOldMark().getName(); - } - }; - controlPointsTable.addColumn(oldMarkColumn, stringMessages.mark()); - TextColumn newMarkColumn = new TextColumn(){ - - @Override - public String getValue(ControlPointAndOldAndNewMark cpaoanm) { - return "" + cpaoanm.getNewMark().getName(); - } - - }; - controlPointsTable.addColumn(newMarkColumn, stringMessages.newMark()); - - ImagesBarColumn actionColumn = - new ImagesBarColumn( + CaptionPanel waypointsPanel = new CaptionPanel(stringMessages.waypoints()); + CaptionPanel controlPointsPanel = new CaptionPanel(stringMessages.twoMarkControlPoint()); + CaptionPanel marksPanel = new CaptionPanel(stringMessages.mark()); + waypointsPanel.add(waypoints); + controlPointsPanel.add(multiMarkControlPoints); + marksPanel.add(marks); + mainPanel.setWidget(0, 0, waypointsPanel); + mainPanel.setWidget(0, 1, controlPointsPanel); + mainPanel.setWidget(0, 2, marksPanel); + + waypointsBtnsPanel = new HorizontalPanel(); + controlPointsBtnsPanel = new HorizontalPanel(); + marksBtnsPanel = new HorizontalPanel(); + mainPanel.setWidget(1, 0, waypointsBtnsPanel); + mainPanel.setWidget(1, 1, controlPointsBtnsPanel); + mainPanel.setWidget(1, 2, marksBtnsPanel); + + ImagesBarColumn waypointsActionColumn = + new ImagesBarColumn( new CourseManagementWidgetWaypointsImagesBarCell(stringMessages)); - actionColumn.setFieldUpdater(new FieldUpdater() { + waypointsActionColumn.setFieldUpdater(new FieldUpdater() { @Override - public void update(int index, ControlPointAndOldAndNewMark controlPoint, String value) { + public void update(int index, WaypointDTO waypoint, String value) { if (CourseManagementWidgetWaypointsImagesBarCell.ACTION_DELETE.equals(value)) { - removeSelectedWaypoints(sailingService); + removeWaypoint(waypoint); } } }); - controlPointsTable.addColumn(actionColumn, stringMessages.actions()); - controlPointDataProvider = new ListDataProvider(); - controlPointDataProvider.addDataDisplay(controlPointsTable); - - // race course marks table - markSelectionModel = new SingleSelectionModel(); - marksTable = new MarkTableWrapper>(markSelectionModel, sailingService, stringMessages, errorReporter); - markSelectionChangeHandler = new Handler() { + waypoints.getTable().addColumn(waypointsActionColumn); + + waypoints.getSelectionModel().addSelectionChangeHandler(new Handler() { @Override public void onSelectionChange(SelectionChangeEvent event) { - if (!ignoreWaypointAndOldAndNewMarkSelectionChange) { - updateNewMark(controlPointsSelectionModel.getSelectedSet(), markSelectionModel.getSelectedObject()); + WaypointDTO waypoint = waypoints.getSelectionModel().getSelectedObject(); + if (waypoint != null) { + selectControlPoints(waypoint); + selectMarks(waypoint.marks); + } + updateWaypointButtons(); + } + }); + + multiMarkControlPoints.getSelectionModel().addSelectionChangeHandler(new Handler() { + @Override + public void onSelectionChange(SelectionChangeEvent event) { + if (multiMarkControlPoints.getSelectionModel().getSelectedSet().size() > 0) { + ControlPointDTO first = multiMarkControlPoints.getSelectionModel().getSelectedSet().iterator().next(); + selectMarks(first.getMarks()); } } - }; - markSelectionModel.addSelectionChangeHandler(markSelectionChangeHandler); - grid.setWidget(1, 1, marksTable); - - courseActionsPanel = new HorizontalPanel(); - courseActionsPanel.setSpacing(10); + }); + insertWaypointBefore = new Button(stringMessages.insertWaypointBeforeSelected()); insertWaypointBefore.addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { - insertWaypoint(sailingService, stringMessages, tableRes, /* before */ true); + insertWaypoint(/* before */ true); } - }); insertWaypointBefore.setEnabled(false); - courseActionsPanel.add(insertWaypointBefore); + waypointsBtnsPanel.add(insertWaypointBefore); insertWaypointAfter = new Button(stringMessages.insertWaypointAfterSelected()); insertWaypointAfter.addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { - insertWaypoint(sailingService, stringMessages, tableRes, /* before */ false); + insertWaypoint(/* before */ false); } - }); insertWaypointAfter.setEnabled(false); - courseActionsPanel.add(insertWaypointAfter); + waypointsBtnsPanel.add(insertWaypointAfter); + + final Button addControlPoint = new Button(stringMessages.addGate()); + addControlPoint.addClickHandler(new ClickHandler() { + @Override + public void onClick(ClickEvent event) { + addMultiMarkControlPoint(); + } + }); + controlPointsBtnsPanel.add(addControlPoint); + + buttonsPanel = new HorizontalPanel(); + mainPanel.setWidget(2, 2, buttonsPanel); Button refreshBtn = new Button(stringMessages.refresh()); refreshBtn.addClickHandler(new ClickHandler() { @Override @@ -344,178 +165,120 @@ public abstract class CourseManagementWidget implements IsWidget { refresh(); } }); - courseActionsPanel.add(refreshBtn); - saveButton = new Button(stringMessages.save()); - saveButton.addClickHandler(new ClickHandler() { + buttonsPanel.add(refreshBtn); + Button saveBtn = new Button(stringMessages.save()); + saveBtn.addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { - Set oldControlPointsFromTableAlreadyHandled = new HashSet(); - List> controlPoints = new ArrayList>(); - for (ControlPointAndOldAndNewMark cpaoanb : controlPointDataProvider.getList()) { - if (!oldControlPointsFromTableAlreadyHandled.contains(cpaoanb.getControlPoint())) { - oldControlPointsFromTableAlreadyHandled.add(cpaoanb.getControlPoint()); - ControlPointDTO controlPointToAdd; - if (controlPointsNeedingReplacement.contains(cpaoanb.getControlPoint())) { - if (cpaoanb.getControlPoint() instanceof GateDTO) { - controlPointToAdd = createGate((GateDTO) cpaoanb.getControlPoint()); - } else { - controlPointToAdd = cpaoanb.getNewMark(); - } - } else { - controlPointToAdd = cpaoanb.getControlPoint(); - } - controlPoints.add(new Pair(controlPointToAdd, cpaoanb.passingInstructions)); - } - } - saveCourse(controlPoints); + save(); } - }); courseActionsPanel.add(saveButton); -// courseActionsPanel.setVisible(false); - - mainPanel.add(courseActionsPanel); + }); + buttonsPanel.add(saveBtn); } - - private void handleControlPointSelectionChange() { - ignoreWaypointAndOldAndNewMarkSelectionChange = true; - try { - markSelectionModel.setSelected(markSelectionModel.getSelectedObject(), false); - final int selectionSize = controlPointsSelectionModel.getSelectedSet().size(); - insertWaypointAfter.setEnabled(selectionSize==1); - insertWaypointBefore.setEnabled(selectionSize==1 || controlPointDataProvider.getList().isEmpty()); - if (selectionSize == 1) { - MarkDTO newMark = controlPointsSelectionModel.getSelectedSet().iterator().next().getNewMark(); - if (newMark != null) { - for (MarkDTO markDTO : marksTable.getDataProvider().getList()) { - if (markDTO.getName().equals(newMark.getName())) { - markSelectionModel.setSelected(markDTO, true); - } - } - } - } - } finally { - ignoreWaypointAndOldAndNewMarkSelectionChange = false; + + private void removeWaypoint(WaypointDTO waypoint) { + waypoints.getDataProvider().getList().remove(waypoint); + } + + private void selectControlPoints(WaypointDTO waypoint) { + for (ControlPointDTO controlPoint : multiMarkControlPoints.getDataProvider().getList()) { + multiMarkControlPoints.getSelectionModel().setSelected( + controlPoint, waypoint.controlPoint == controlPoint); + } + } + + private void selectMarks(Iterable newMarks) { + marks.getSelectionModel().clear(); + for (MarkDTO toSelect : newMarks) { + marks.getSelectionModel().setSelected(toSelect, true); } } - protected abstract void saveCourse(List> controlPoints); - - /** - * When a gate needs replacement, its entries in {@link #controlPointDataProvider} are looked up, and a new - * {@link GateDTO} is created having the same name as the old gate, but using the new marks as the gate's marks. - */ - protected ControlPointDTO createGate(GateDTO oldGate) { - MarkDTO newLeft = null; - MarkDTO newRight = null; - for (ControlPointAndOldAndNewMark cpaoanb : controlPointDataProvider.getList()) { - if (cpaoanb.getControlPoint() == oldGate) { - MarkDTO newMark = cpaoanb.getNewMark(); - if (newRight != null || newMark.getName().matches("^.*"+REGEX_FOR_LEFT+".*$")) { - newLeft = newMark; - } else { - newRight = newMark; - } - } + protected abstract void save(); + + private T getFirstSelected(SetSelectionModel selectionModel) { + if (selectionModel.getSelectedSet().isEmpty()) { + return null; } - assert newLeft != null && newRight != null; - // if old gate had null ID, the new gate will have a null ID too, causing the server to generate one - return new GateDTO(oldGate.getIdAsString(), oldGate.getName(), newLeft, newRight); + return selectionModel.getSelectedSet().iterator().next(); } - - private void updateNewMark(Set selectedWaypointsAndOldAndNewMarks, MarkDTO selectedNewMark) { - if (selectedWaypointsAndOldAndNewMarks != null) { - for (ControlPointAndOldAndNewMark w : selectedWaypointsAndOldAndNewMarks) { - if (selectedNewMark == null) { - w.setNewMark(w.getOldMark()); - } else { - w.setNewMark(selectedNewMark); - } - if (w.getOldMark().getName().equals(w.getNewMark().getName())) { - checkIfAllMarksOfControlPointAreUnchangedAndIfSoRememberThis(w.getControlPoint()); - } else { - controlPointsNeedingReplacement.add(w.getControlPoint()); - } - final int indexOf = controlPointDataProvider.getList().indexOf(w); - if (indexOf != -1) { - controlPointDataProvider.getList().set(indexOf, w); - } - } + + private void insert(TableWrapper> tableWrapper, T toInsert, boolean beforeSelection) { + T selected = getFirstSelected(tableWrapper.getSelectionModel()); + int index = -1; + if (tableWrapper.getDataProvider().getList().isEmpty()) { + index = 0; + } else if (selected != null) { + index = tableWrapper.getDataProvider().getList().indexOf(selected) + (beforeSelection?0:1); + } + if (index != -1) { + tableWrapper.getDataProvider().getList().add(index, toInsert); } } - private void checkIfAllMarksOfControlPointAreUnchangedAndIfSoRememberThis(ControlPointDTO controlPoint) { - boolean allMarksUnchanged = true; - for (ControlPointAndOldAndNewMark cpaoanb : controlPointDataProvider.getList()) { - if (cpaoanb.getControlPoint() == controlPoint) { - if (!cpaoanb.getOldMark().getName().equals(cpaoanb.getNewMark().getName())) { - allMarksUnchanged = false; - break; - } - } - } - if (allMarksUnchanged) { - controlPointsNeedingReplacement.remove(controlPoint); - } - } - - private void insertWaypoint(final SailingServiceAsync sailingService, StringMessages stringMessages, - AdminConsoleTableResources tableRes, final boolean beforeSelection) { - new ControlPointCreationDialog(stringMessages, tableRes, marksTable.getDataProvider().getList(), new DataEntryDialog.DialogCallback>() { + private void addMultiMarkControlPoint() { + new GateCreationDialog(sailingService, errorReporter, stringMessages, tableRes, + marks.getDataProvider().getList(), new DataEntryDialog.DialogCallback() { @Override - public void cancel() { - // dialog cancelled, do nothing - } + public void cancel() {} @Override - public void ok(Pair result) { - Set selectedElements = controlPointsSelectionModel.getSelectedSet(); - int index = -1; - if (controlPointDataProvider.getList().isEmpty()) { - index = 0; - } else if (!selectedElements.isEmpty()) { - ControlPointAndOldAndNewMark selectedElement = selectedElements.iterator().next(); - index = controlPointDataProvider.getList().indexOf(selectedElement) + (beforeSelection?0:1); - } - if (index != -1) { - for (MarkDTO markDTO : result.getA().getMarks()) { - controlPointDataProvider.getList().add(index++, new ControlPointAndOldAndNewMark(result.getA(), result.getB(), markDTO)); - } - } - handleControlPointSelectionChange(); + public void ok(GateDTO result) { + multiMarkControlPoints.dataProvider.getList().add(result); + } + }).show(); + } + + private void insertWaypoint(final boolean beforeSelection) { + List allControlPoints = new ArrayList<>(); + allControlPoints.addAll(multiMarkControlPoints.getDataProvider().getList()); + allControlPoints.addAll(marks.getDataProvider().getList()); + new WaypointCreationDialog(sailingService, errorReporter, stringMessages, tableRes, + allControlPoints, new DataEntryDialog.DialogCallback() { + @Override + public void cancel() {} + + @Override + public void ok(WaypointDTO result) { + insert(waypoints, result, beforeSelection); } }).show(); } public abstract void refresh(); - protected void updateWaypointTable(RaceCourseDTO raceCourseDTO) { - List waypointsAndOldAndNewMarks = new ArrayList(); - for (WaypointDTO waypointDTO : raceCourseDTO.waypoints) { - ControlPointDTO controlPointDTO = waypointDTO.controlPoint; - for (MarkDTO mark : controlPointDTO.getMarks()) { - ControlPointAndOldAndNewMark waypointAndOldAndNewMark = new ControlPointAndOldAndNewMark(controlPointDTO, waypointDTO.passingInstructions, mark); - waypointsAndOldAndNewMarks.add(waypointAndOldAndNewMark); + protected void updateWaypointsAndControlPoints(RaceCourseDTO raceCourseDTO) { + waypoints.getDataProvider().getList().clear(); + multiMarkControlPoints.getDataProvider().getList().clear(); + waypoints.getDataProvider().getList().addAll(raceCourseDTO.waypoints); + + Map noDuplicateCPs = new HashMap<>(); + for (ControlPointDTO controlPoint : raceCourseDTO.getControlPoints()) { + if (controlPoint instanceof GateDTO) { + noDuplicateCPs.put(controlPoint.getIdAsString(), controlPoint); } } - controlPointDataProvider.getList().clear(); - controlPointDataProvider.getList().addAll(waypointsAndOldAndNewMarks); - controlPointsNeedingReplacement.clear(); - for (ControlPointAndOldAndNewMark w : controlPointsSelectionModel.getSelectedSet()) { - controlPointsSelectionModel.setSelected(w, false); - } + multiMarkControlPoints.getDataProvider().getList().addAll(noDuplicateCPs.values()); + + updateWaypointButtons(); } - - private void removeSelectedWaypoints(final SailingServiceAsync sailingService) { - final Set selectedControlPoints = new HashSet(); - for (ControlPointAndOldAndNewMark cpaoanb : controlPointsSelectionModel.getSelectedSet()) { - selectedControlPoints.add(cpaoanb.getControlPoint()); + + protected List> createWaypointPairs() { + List> result = new ArrayList<>(); + for (WaypointDTO waypoint : waypoints.getDataProvider().getList()) { + result.add(new Util.Pair<>(waypoint.controlPoint, waypoint.passingInstructions)); } - for (Iterator i=controlPointDataProvider.getList().iterator(); i.hasNext(); ) { - ControlPointAndOldAndNewMark next = i.next(); - if (selectedControlPoints.contains(next.getControlPoint())) { - i.remove(); - controlPointsSelectionModel.setSelected(next, false); - } + return result; + } + + protected void updateWaypointButtons() { + if (waypoints.getDataProvider().getList().isEmpty() || + waypoints.getSelectionModel().getSelectedObject() != null) { + insertWaypointAfter.setEnabled(true); + insertWaypointBefore.setEnabled(true); + } else { + insertWaypointBefore.setEnabled(false); + insertWaypointAfter.setEnabled(false); } - handleControlPointSelectionChange(); } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/GateCreationDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/GateCreationDialog.java new file mode 100644 index 00000000000..21093935d9b --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/GateCreationDialog.java @@ -0,0 +1,88 @@ +package com.sap.sailing.gwt.ui.adminconsole; + +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +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.TextBox; +import com.google.gwt.user.client.ui.Widget; +import com.google.gwt.view.client.MultiSelectionModel; +import com.google.gwt.view.client.SelectionChangeEvent; +import com.google.gwt.view.client.SelectionChangeEvent.Handler; +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.GateDTO; +import com.sap.sailing.gwt.ui.shared.MarkDTO; +import com.sap.sse.gwt.client.dialog.DataEntryDialog; + +public class GateCreationDialog extends DataEntryDialog { + private final MarkTableWrapper> marksWrapper; + private final TextBox name; + private final StringMessages stringMessages; + + public GateCreationDialog(SailingServiceAsync sailingService, ErrorReporter errorReporter, + final StringMessages stringMessages, AdminConsoleTableResources tableRes, + List marks, DialogCallback callback) { + super(stringMessages.gate(), stringMessages.gate(), + stringMessages.ok(), stringMessages.cancel(), new DataEntryDialog.Validator() { + @Override + public String getErrorMessage(GateDTO valueToValidate) { + if (valueToValidate == null) { + return stringMessages.selectTwoMarksForGate(); + } else if (valueToValidate.getName() == null || valueToValidate.getName().isEmpty()) { + return stringMessages.pleaseEnterAName(); + } + return null; + } + + }, /* animationEnabled */ false, callback); + + this.stringMessages = stringMessages; + + name = createTextBox(""); + + marksWrapper = new MarkTableWrapper>( + new MultiSelectionModel(), sailingService, stringMessages, errorReporter); + marksWrapper.getDataProvider().getList().addAll(marks); + + marksWrapper.getSelectionModel().addSelectionChangeHandler(new Handler() { + @Override + public void onSelectionChange(SelectionChangeEvent event) { + validate(); + } + }); + } + + @Override + protected GateDTO getResult() { + GateDTO controlPoint = null; + Set selection = marksWrapper.getSelectionModel().getSelectedSet(); + if (selection.size() == 2) { + name.setEnabled(true); + Iterator i = marksWrapper.getSelectionModel().getSelectedSet().iterator(); + MarkDTO first = i.next(); + MarkDTO second = i.next(); + controlPoint = new GateDTO(/* generate UUID on the server */ null, name.getText(), first, second); + } + + return controlPoint; + } + + @Override + protected Widget getAdditionalWidget() { + Grid grid = new Grid(2,1); + + HorizontalPanel nameRow = new HorizontalPanel(); + nameRow.add(new Label(stringMessages.name())); + nameRow.add(name); + grid.setWidget(0, 0, nameRow); + + grid.setWidget(1, 0, marksWrapper); + + return grid; + } +} \ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceCourseManagementPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceCourseManagementPanel.java index 8cdf7e71261..01e1e53c308 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceCourseManagementPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceCourseManagementPanel.java @@ -1,17 +1,13 @@ package com.sap.sailing.gwt.ui.adminconsole; import java.util.Date; -import java.util.List; import com.google.gwt.user.client.Window; import com.google.gwt.user.client.rpc.AsyncCallback; -import com.sap.sailing.domain.common.PassingInstruction; -import com.sap.sailing.domain.common.impl.Util.Pair; import com.sap.sailing.gwt.ui.client.ErrorReporter; import com.sap.sailing.gwt.ui.client.RegattaRefresher; import com.sap.sailing.gwt.ui.client.SailingServiceAsync; import com.sap.sailing.gwt.ui.client.StringMessages; -import com.sap.sailing.gwt.ui.shared.ControlPointDTO; import com.sap.sailing.gwt.ui.shared.RaceCourseDTO; /** @@ -30,10 +26,9 @@ public class RaceCourseManagementPanel extends AbstractRaceManagementPanel { super(sailingService, errorReporter, regattaRefresher, stringMessages); courseManagementWidget = new CourseManagementWidget(sailingService, errorReporter, stringMessages) { - @Override - protected void saveCourse(List> controlPoints) { - sailingService.updateRaceCourse(singleSelectedRace, controlPoints, new AsyncCallback() { + protected void save() { + sailingService.updateRaceCourse(singleSelectedRace, createWaypointPairs(), new AsyncCallback() { @Override public void onFailure(Throwable caught) { errorReporter.reportError(stringMessages.errorUpdatingRaceCourse(caught.getMessage())); @@ -50,13 +45,13 @@ public class RaceCourseManagementPanel extends AbstractRaceManagementPanel { @Override public void refresh() { if (singleSelectedRace != null && selectedRaceDTO != null) { -// courseActionsPanel.setVisible(true); + mainPanel.setVisible(true); // TODO bug 1351: never use System.currentTimeMillis() on the client when trying to compare anything with "server time"; this one is not so urgent as it is reached only in the AdminConsole and we expect administrators to have proper client-side time settings sailingService.getRaceCourse(singleSelectedRace, new Date(), new AsyncCallback() { @Override public void onSuccess(RaceCourseDTO raceCourseDTO) { - updateWaypointTable(raceCourseDTO); - marksTable.refresh(raceCourseDTO.getMarks()); + updateWaypointsAndControlPoints(raceCourseDTO); + marks.refresh(raceCourseDTO.getMarks()); } @Override @@ -67,7 +62,7 @@ public class RaceCourseManagementPanel extends AbstractRaceManagementPanel { } }); } else { - courseActionsPanel.setVisible(false); + mainPanel.setVisible(false); } } }; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceLogTrackingCourseDefinitionDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceLogTrackingCourseDefinitionDialog.java index 381ca77bf53..ca1e07ac26e 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceLogTrackingCourseDefinitionDialog.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceLogTrackingCourseDefinitionDialog.java @@ -1,7 +1,6 @@ package com.sap.sailing.gwt.ui.adminconsole; import java.util.Collection; -import java.util.List; import com.google.gwt.cell.client.FieldUpdater; import com.google.gwt.event.dom.client.ClickEvent; @@ -9,13 +8,10 @@ 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.Panel; -import com.sap.sailing.domain.common.PassingInstruction; import com.sap.sailing.domain.common.dto.PositionDTO; -import com.sap.sailing.domain.common.impl.Util.Pair; 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.ControlPointDTO; import com.sap.sailing.gwt.ui.shared.MarkDTO; import com.sap.sailing.gwt.ui.shared.RaceCourseDTO; import com.sap.sse.gwt.client.dialog.DataEntryDialog; @@ -51,7 +47,7 @@ public class RaceLogTrackingCourseDefinitionDialog extends RaceLogTrackingDialog }).show(); } }); - courseActionsPanel.insert(addMark, courseActionsPanel.getWidgetIndex(RaceLogCourseManagementWidget.this.saveButton)); + marksBtnsPanel.add(addMark); Button cancel = new Button(stringMessages.cancel()); @@ -61,7 +57,7 @@ public class RaceLogTrackingCourseDefinitionDialog extends RaceLogTrackingDialog hide(); } }); - courseActionsPanel.insert(cancel, courseActionsPanel.getWidgetIndex(RaceLogCourseManagementWidget.this.saveButton)); + buttonsPanel.add(cancel); ImagesBarColumn actionColumn = new ImagesBarColumn( @@ -96,12 +92,13 @@ public class RaceLogTrackingCourseDefinitionDialog extends RaceLogTrackingDialog } } }); - marksTable.getTable().addColumn(actionColumn); + marks.getTable().addColumn(actionColumn); } @Override - protected void saveCourse(List> controlPoints) { - sailingService.addCourseDefinitionToRaceLog(leaderboardName, raceColumnName, fleetName, controlPoints, new AsyncCallback() { + protected void save() { + sailingService.addCourseDefinitionToRaceLog(leaderboardName, raceColumnName, fleetName, + createWaypointPairs(), new AsyncCallback() { @Override public void onSuccess(Void result) { hide(); @@ -119,10 +116,7 @@ public class RaceLogTrackingCourseDefinitionDialog extends RaceLogTrackingDialog sailingService.getLastCourseDefinitionInRaceLog(leaderboardName, raceColumnName, fleetName, new AsyncCallback() { @Override public void onSuccess(RaceCourseDTO result) { - updateWaypointTable(result); - if (result.waypoints.isEmpty()) { - insertWaypointBefore.setEnabled(true); - } + updateWaypointsAndControlPoints(result); } @Override @@ -134,7 +128,7 @@ public class RaceLogTrackingCourseDefinitionDialog extends RaceLogTrackingDialog sailingService.getMarksInRaceLog(leaderboardName, raceColumnName, fleetName, new AsyncCallback>() { @Override public void onSuccess(Collection result) { - marksTable.refresh(result); + marks.refresh(result); } @Override diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/TableWrapper.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/TableWrapper.java index fcd1d028880..02a3bfabf27 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/TableWrapper.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/TableWrapper.java @@ -1,5 +1,7 @@ package com.sap.sailing.gwt.ui.adminconsole; +import java.util.Collection; + import com.google.gwt.core.client.GWT; import com.google.gwt.user.cellview.client.CellTable; import com.google.gwt.user.client.ui.IsWidget; @@ -55,4 +57,10 @@ public abstract class TableWrapper> implements Is public ListDataProvider getDataProvider() { return dataProvider; } -} + + public void refresh(Collection newItems) { + dataProvider.getList().clear(); + dataProvider.getList().addAll(newItems); + dataProvider.flush(); + } +} \ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/WaypointCreationDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/WaypointCreationDialog.java new file mode 100644 index 00000000000..cd5c33396ca --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/WaypointCreationDialog.java @@ -0,0 +1,108 @@ +package com.sap.sailing.gwt.ui.adminconsole; + +import java.util.ArrayList; +import java.util.List; + +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.Widget; +import com.google.gwt.view.client.SelectionChangeEvent; +import com.google.gwt.view.client.SingleSelectionModel; +import com.google.gwt.view.client.SelectionChangeEvent.Handler; +import com.sap.sailing.domain.common.PassingInstruction; +import com.sap.sailing.domain.common.impl.Util; +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.ControlPointDTO; +import com.sap.sailing.gwt.ui.shared.MarkDTO; +import com.sap.sailing.gwt.ui.shared.WaypointDTO; +import com.sap.sse.gwt.client.dialog.DataEntryDialog; + +public class WaypointCreationDialog extends DataEntryDialog { + private final ControlPointTableWrapper> controlPointsWrapper; + private final ListBox passingInstructions; + private final StringMessages stringMessages; + + public WaypointCreationDialog(SailingServiceAsync sailingService, ErrorReporter errorReporter, + final StringMessages stringMessages, AdminConsoleTableResources tableRes, + List controlPoints, DialogCallback callback) { + super(stringMessages.waypoint(), stringMessages.waypoint(), + stringMessages.ok(), stringMessages.cancel(), new DataEntryDialog.Validator() { + @Override + public String getErrorMessage(WaypointDTO valueToValidate) { + if (valueToValidate.controlPoint == null) { + return stringMessages.pleaseSelectAControlPoint(); + } + return null; + } + + }, /* animationEnabled */ false, callback); + + this.stringMessages = stringMessages; + + controlPointsWrapper = new ControlPointTableWrapper>( + new SingleSelectionModel(), sailingService, stringMessages, errorReporter); + controlPointsWrapper.getDataProvider().getList().addAll(controlPoints); + + passingInstructions = createListBox(false); + updatePassingInstructions(); + + controlPointsWrapper.getSelectionModel().addSelectionChangeHandler(new Handler() { + @Override + public void onSelectionChange(SelectionChangeEvent event) { + updatePassingInstructions(); + validate(); + } + }); + } + + private void updatePassingInstructions() { + passingInstructions.clear(); + + int numMarks = controlPointsWrapper.getSelectionModel().getSelectedObject() != null ? + Util.size(controlPointsWrapper.getSelectionModel().getSelectedObject().getMarks()) : 0; + + int i = 0; + passingInstructions.insertItem(PassingInstruction.None.name(), i++); + for (PassingInstruction pi : PassingInstruction.relevantValues()) { + for (int numApplicableMarks : pi.applicability) { + if (numApplicableMarks == numMarks) { + passingInstructions.insertItem(pi.name(), i++); + } + } + } + } + + @Override + protected WaypointDTO getResult() { + PassingInstruction pi = passingInstructions.getSelectedIndex() > 0 ? + PassingInstruction.valueOf(passingInstructions.getItemText(passingInstructions.getSelectedIndex())) + : PassingInstruction.None; + + ControlPointDTO controlPoint = controlPointsWrapper.getSelectionModel().getSelectedObject(); + List marks = new ArrayList(); + String name = null; + if (controlPoint != null) { + Util.addAll(controlPoint.getMarks(), marks); + name = controlPoint.getName(); + } + return new WaypointDTO(name, controlPoint, marks, pi); + } + + @Override + protected Widget getAdditionalWidget() { + Grid grid = new Grid(2,1); + + grid.setWidget(0, 0, controlPointsWrapper); + + HorizontalPanel passingInstructionsRow = new HorizontalPanel(); + grid.setWidget(1, 0, passingInstructionsRow); + passingInstructionsRow.add(new Label(stringMessages.passingInstructions() + ":")); + passingInstructionsRow.add(passingInstructions); + + return grid; + } +} \ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/WaypointTableWrapper.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/WaypointTableWrapper.java new file mode 100644 index 00000000000..ea04002dfe2 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/WaypointTableWrapper.java @@ -0,0 +1,51 @@ +package com.sap.sailing.gwt.ui.adminconsole; + +import com.google.gwt.user.cellview.client.TextColumn; +import com.google.gwt.view.client.SelectionModel; +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.ControlPointDTO; +import com.sap.sailing.gwt.ui.shared.GateDTO; +import com.sap.sailing.gwt.ui.shared.MarkDTO; +import com.sap.sailing.gwt.ui.shared.WaypointDTO; + +public class WaypointTableWrapper> extends TableWrapper { + public WaypointTableWrapper(S selectionModel, SailingServiceAsync sailingService, final StringMessages stringMessages, + ErrorReporter errorReporter) { + super(sailingService, stringMessages, errorReporter, selectionModel); + + mainPanel.add(table); + + TextColumn nameColumn = new TextColumn() { + @Override + public String getValue(WaypointDTO d) { + return d.controlPoint.getName(); + } + }; + table.addColumn(nameColumn, stringMessages.controlPoint() + " " + stringMessages.name()); + + TextColumn typeColumn = new TextColumn() { + @Override + public String getValue(WaypointDTO w) { + ControlPointDTO d = w.controlPoint; + if (d instanceof GateDTO) { + return "2x " + stringMessages.mark(); + } else if (d instanceof MarkDTO) { + return stringMessages.mark(); + } else { + return stringMessages.unknown(); + } + } + }; + table.addColumn(typeColumn, stringMessages.type()); + + TextColumn passingInstructionsColumn = new TextColumn() { + @Override + public String getValue(WaypointDTO waypoint) { + return waypoint.passingInstructions != null ? waypoint.passingInstructions.name() : ""; + } + }; + table.addColumn(passingInstructionsColumn, stringMessages.passingInstructions()); + } +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java index 46964f27fb2..d84ec3853fb 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java @@ -364,11 +364,12 @@ public interface StringMessages extends Messages { String controlPoint(); String errorUpdatingRaceCourse(String message); String successfullyUpdatedCourse(); - String selectOneMarkOrTwoMarksForGate(); + String selectTwoMarksForGate(); String errorTryingToObtainTheMarksOfTheRace(String message); String errorTryingToObtainRaceCourse(String message); String insertWaypointBeforeSelected(); String insertWaypointAfterSelected(); + String addGate(); String scoringSchemeLowPointSystem(); String scoringSchemeHighPointSystem(); String scoringSchemeLowPointWinnerGetsZero(); @@ -1007,4 +1008,6 @@ public interface StringMessages extends Messages { String authorPriority(); String removeDenotation(); String competitorRegistrationsMissingProceed(String namesOfRaces); + String pleaseSelectAControlPoint(); + String twoMarkControlPoint(); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties index 1024e4412b1..a6d02f53125 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties @@ -389,11 +389,12 @@ newMark=New Mark controlPoint=Mark/Gate errorUpdatingRaceCourse=Error updating race course: {0} successfullyUpdatedCourse=Successfully updated race course -selectOneMarkOrTwoMarksForGate=Select a single mark or two marks to create a gate +selectTwoMarksForGate=Select two marks to create a gate errorTryingToObtainTheMarksOfTheRace=Error trying to obtain the marks of the race course: {0} errorTryingToObtainRaceCourse=Error trying to obtain the race course: {0} insertWaypointBeforeSelected=Insert waypoint before selected insertWaypointAfterSelected=Insert waypoint after selected +addGate=Add Gate scoringSchemeLowPointSystem=Low Point System scoringSchemeHighPointSystem=High Point System scoringSchemeHighPointEssOverall=High Point System ESS Overall @@ -992,4 +993,6 @@ importWindTooltip=Unchecking this option will drastically lower the amount of da authorName=Author name authorPriority=Author priority removeDenotation=Remove Denotation -competitorRegistrationsMissingProceed=At least one of the selected races ({0}) is missing competitor registrations. Proceed anyway? \ No newline at end of file +competitorRegistrationsMissingProceed=At least one of the selected races ({0}) is missing competitor registrations. Proceed anyway? +pleaseSelectAControlPoint=Please select a control point +twoMarkControlPoint=2-mark-controlpoint \ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties index 3fed08c40da..ea569ce24f6 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties @@ -390,11 +390,12 @@ newMark=Neue Marke controlPoint=Einzelmarke/Gate errorUpdatingRaceCourse=Fehler beim Aktualisieren des neuen Kurses: {0} successfullyUpdatedCourse=Kurs erfolgreich aktualisiert -selectOneMarkOrTwoMarksForGate=Eine einzelne Marke auswählen oder zwei Marken, um ein Gate zu erstellen +selectTwoMarksForGate=Zwei Marken auswählen, um ein Gate zu erstellen errorTryingToObtainTheMarksOfTheRace=Fehler beim Ermitteln der Marken des Regattakurses: {0} errorTryingToObtainRaceCourse=Fehler beim Ermitteln des Rennkurses: {0} insertWaypointBeforeSelected=Wegpunkt nach dem selektierten einfügen insertWaypointAfterSelected=Wegpunkt vor dem selektierten einfügen +addGate=Gate hinzufügen scoringSchemeLowPointSystem=Low Point System scoringSchemeHighPointSystem=High Point System scoringSchemeHighPointEssOverall=High Point System ESS Gesamtwertung @@ -988,4 +989,6 @@ importWindTooltip=Diese Option zu deselektieren führt zu einer wesentlich klein authorName=Benutzername authorPriority=Benutzer-Priorität removeDenotation=Markierung entfernen -competitorRegistrationsMissingProceed=Für mindestens eins der Rennen ({0}) sind keine Teams registriert. Trotzdem fortfahren? \ No newline at end of file +competitorRegistrationsMissingProceed=Für mindestens eins der Rennen ({0}) sind keine Teams registriert. Trotzdem fortfahren? +pleaseSelectAControlPoint=Bitte eine Einzelmark/ein Gate auswählen +twoMarkControlPoint=2-Marken-Kontrollpunkt \ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java index 85fab77da28..dec130de6f6 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java @@ -160,6 +160,7 @@ import com.sap.sailing.domain.common.impl.WindSourceImpl; import com.sap.sailing.domain.common.racelog.FlagPole; import com.sap.sailing.domain.common.racelog.Flags; import com.sap.sailing.domain.common.racelog.RaceLogRaceStatus; +import com.sap.sailing.domain.common.racelog.RacingProcedureType; import com.sap.sailing.domain.common.racelog.tracking.NoCorrespondingServiceRegisteredException; import com.sap.sailing.domain.common.racelog.tracking.NotDenotedForRaceLogTrackingException; import com.sap.sailing.domain.common.racelog.tracking.NotRevokableException; @@ -167,7 +168,6 @@ import com.sap.sailing.domain.common.racelog.tracking.RaceLogTrackingState; import com.sap.sailing.domain.common.racelog.tracking.TransformationException; import com.sap.sailing.domain.common.racelog.tracking.TypeBasedServiceFinder; import com.sap.sailing.domain.common.racelog.tracking.TypeBasedServiceFinderFactory; -import com.sap.sailing.domain.common.racelog.RacingProcedureType; import com.sap.sailing.domain.igtimiadapter.Account; import com.sap.sailing.domain.igtimiadapter.IgtimiConnection; import com.sap.sailing.domain.igtimiadapter.IgtimiConnectionFactory; @@ -1633,6 +1633,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S @Override public RaceCourseDTO getRaceCourse(RegattaAndRaceIdentifier raceIdentifier, Date date) { List waypointDTOs = new ArrayList(); + Map controlPointCache = new HashMap<>(); RaceCourseDTO result = new RaceCourseDTO(waypointDTOs); if (date != null) { TimePoint dateAsTimePoint = new MillisecondsTimePoint(date); @@ -1640,14 +1641,18 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S if (trackedRace != null) { Course course = trackedRace.getRace().getCourse(); for (Waypoint waypoint : course.getWaypoints()) { - List markDTOs = new ArrayList(); - for (Mark mark : trackedRace.getMarks()) { + List marks = new ArrayList<>(); + for (Mark mark : waypoint.getMarks()) { GPSFixTrack track = trackedRace.getOrCreateTrack(mark); Position positionAtDate = track.getEstimatedPosition(dateAsTimePoint, /* extrapolate */false); - markDTOs.add(convertToMarkDTO(mark, positionAtDate)); + marks.add(convertToMarkDTO(mark, positionAtDate)); } - ControlPointDTO controlPointDTO = convertToControlPointDTO(waypoint.getControlPoint(), trackedRace, dateAsTimePoint); - WaypointDTO waypointDTO = new WaypointDTO(waypoint.getName(), controlPointDTO, markDTOs, waypoint.getPassingInstructions()); + ControlPointDTO controlPointDTO = controlPointCache.get(waypoint.getControlPoint().getId()); + if (controlPointDTO == null) { + controlPointDTO = convertToControlPointDTO(waypoint.getControlPoint(), trackedRace, dateAsTimePoint); + controlPointCache.put(waypoint.getControlPoint().getId(), controlPointDTO); + } + WaypointDTO waypointDTO = new WaypointDTO(waypoint.getName(), controlPointDTO, marks, waypoint.getPassingInstructions()); waypointDTOs.add(waypointDTO); } } @@ -1685,29 +1690,34 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S /** * For each {@link ControlPointDTO} in controlPoints tries to find the best-matching waypoint - * from the {@code old} {@code CourseBase}. + * from the {@code old} {@code CourseBase}, and also from the newly created additional waypoints. * If such a waypoint is found, its control point is added to the control point list for the new course. * Otherwise, a new control point is created using the default {@link com.sap.sailing.domain.base.DomainFactory} * instance. */ private List> resolveControlPoints( CourseBase old, List> controlPoints) { + Map newlyCreatedCache = new HashMap(); Iterable waypoints = old.getWaypoints(); List> newControlPoints = new ArrayList>(); - int lastMatchPosition = -1; for (Pair controlPointAndPassingInstruction : controlPoints) { ControlPointDTO controlPointDTO = controlPointAndPassingInstruction.getA(); - ControlPoint matchFromOldCourse = null; - for (int i=lastMatchPosition+1; matchFromOldCourse == null && i(matchFromOldCourse, null)); - lastMatchPosition = i; + ControlPoint match = null; + if (newlyCreatedCache.containsKey(controlPointDTO)) { + match = newlyCreatedCache.get(controlPointDTO); + } else { + for (Waypoint oldWP : waypoints) { + ControlPoint oldControlPoint = oldWP.getControlPoint(); + if (oldControlPoint.getId().toString().equals(controlPointDTO.getIdAsString()) + && markIDsMatch(oldControlPoint.getMarks(), controlPointDTO.getMarks())) { + match = oldControlPoint; + break; + } } } - if (matchFromOldCourse == null) { + if (match != null) { + newControlPoints.add(new Pair(match, controlPointAndPassingInstruction.getB())); + } else { // no match found; create new control point: ControlPoint newControlPoint; if (controlPointDTO instanceof GateDTO) { @@ -1727,6 +1737,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S PassingInstruction passingInstructions = controlPointAndPassingInstruction.getB(); newControlPoints.add(new Pair(newControlPoint, passingInstructions)); } + newlyCreatedCache.put(controlPointDTO, newControlPoint); } } return newControlPoints; @@ -4170,8 +4181,12 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S raceLog.add(event); } - private WaypointDTO convertToWaypointDTO(Waypoint waypoint) { - ControlPointDTO cp = convertToControlPointDTO(waypoint.getControlPoint()); + private WaypointDTO convertToWaypointDTO(Waypoint waypoint, Map controlPointCache) { + ControlPointDTO cp = controlPointCache.get(waypoint.getControlPoint().getId()); + if (cp == null) { + cp = convertToControlPointDTO(waypoint.getControlPoint()); + controlPointCache.put(waypoint.getControlPoint().getId(), cp); + } List marks = new ArrayList(); Util.addAll(cp.getMarks(), marks); return new WaypointDTO(waypoint.getName(), cp, marks, waypoint.getPassingInstructions()); @@ -4179,9 +4194,10 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S private RaceCourseDTO convertToRaceCourseDTO(CourseBase course) { List waypointDTOs = new ArrayList(); + Map controlPointCache = new HashMap<>(); RaceCourseDTO result = new RaceCourseDTO(waypointDTOs); for (Waypoint waypoint : course.getWaypoints()) { - waypointDTOs.add(convertToWaypointDTO(waypoint)); + waypointDTOs.add(convertToWaypointDTO(waypoint, controlPointCache)); } return result; } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/MarkDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/MarkDTO.java index 0e2720bc656..02752f0e306 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/MarkDTO.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/MarkDTO.java @@ -28,4 +28,18 @@ public class MarkDTO extends ControlPointDTO { public Iterable getMarks() { return Collections.singleton(this); } + + @Override + public boolean equals(Object o) { + if (o instanceof MarkDTO) { + MarkDTO other = (MarkDTO) o; + return getIdAsString() != null && getIdAsString().equals(other.getIdAsString()); + } + return false; + } + + @Override + public int hashCode() { + return getIdAsString().hashCode(); + } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/RaceCourseDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/RaceCourseDTO.java index 935d72762ac..7c126a238a2 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/RaceCourseDTO.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/RaceCourseDTO.java @@ -10,11 +10,17 @@ import com.google.gwt.user.client.rpc.IsSerializable; public class RaceCourseDTO implements IsSerializable { public List waypoints; + public List marks; RaceCourseDTO() {} public RaceCourseDTO(List waypoints) { + this(waypoints, null); + } + + public RaceCourseDTO(List waypoints, List marks) { this.waypoints = waypoints; + this.marks = marks; } public List getControlPoints() { @@ -26,6 +32,10 @@ public class RaceCourseDTO implements IsSerializable { } public Collection getMarks() { + if (marks != null) { + return marks; + } + Map marks = new HashMap(); for(WaypointDTO waypoint: waypoints) { for(MarkDTO mark: waypoint.marks) { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/WaypointDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/WaypointDTO.java index 5a775cee274..59b2bf82414 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/WaypointDTO.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/WaypointDTO.java @@ -5,6 +5,10 @@ import java.util.List; import com.sap.sailing.domain.common.PassingInstruction; import com.sap.sailing.domain.common.dto.NamedDTO; +/** + * Equality and hashcode based on object identity + * + */ public class WaypointDTO extends NamedDTO { private static final long serialVersionUID = 7439553659782967746L; @@ -23,4 +27,14 @@ public class WaypointDTO extends NamedDTO { this.controlPoint = controlPoint; this.passingInstructions = passingInstructions; } + + @Override + public boolean equals(Object obj) { + return this == obj; + } + + @Override + public int hashCode() { + return System.identityHashCode(this); + } }