View Javadoc
1   package fr.ifremer.reefdb.ui.swing.content.home.operation;
2   
3   /*
4    * #%L
5    * Reef DB :: UI
6    * $Id:$
7    * $HeadURL:$
8    * %%
9    * Copyright (C) 2014 - 2015 Ifremer
10   * %%
11   * This program is free software: you can redistribute it and/or modify
12   * it under the terms of the GNU Affero General Public License as published by
13   * the Free Software Foundation, either version 3 of the License, or
14   * (at your option) any later version.
15   *
16   * This program is distributed in the hope that it will be useful,
17   * but WITHOUT ANY WARRANTY; without even the implied warranty of
18   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19   * GNU General Public License for more details.
20   *
21   * You should have received a copy of the GNU Affero General Public License
22   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
23   * #L%
24   */
25  
26  import fr.ifremer.quadrige3.core.dao.technical.StringIterator;
27  import fr.ifremer.quadrige3.ui.swing.action.ActionFactory;
28  import fr.ifremer.quadrige3.ui.swing.component.coordinate.CoordinateEditor;
29  import fr.ifremer.quadrige3.ui.swing.table.SwingTable;
30  import fr.ifremer.quadrige3.ui.swing.table.editor.ExtendedComboBoxCellEditor;
31  import fr.ifremer.quadrige3.ui.swing.table.editor.LocalTimeCellEditor;
32  import fr.ifremer.reefdb.decorator.DecoratorService;
33  import fr.ifremer.reefdb.dto.ReefDbBeanFactory;
34  import fr.ifremer.reefdb.dto.ReefDbBeans;
35  import fr.ifremer.reefdb.dto.data.measurement.MeasurementDTO;
36  import fr.ifremer.reefdb.dto.data.sampling.SamplingOperationDTO;
37  import fr.ifremer.reefdb.dto.data.survey.SurveyDTO;
38  import fr.ifremer.reefdb.dto.enums.FilterTypeValues;
39  import fr.ifremer.reefdb.dto.referential.DepartmentDTO;
40  import fr.ifremer.reefdb.dto.referential.SamplingEquipmentDTO;
41  import fr.ifremer.reefdb.dto.referential.pmfm.PmfmDTO;
42  import fr.ifremer.reefdb.service.ReefDbTechnicalException;
43  import fr.ifremer.reefdb.service.StatusFilter;
44  import fr.ifremer.reefdb.ui.swing.content.home.HomeUI;
45  import fr.ifremer.reefdb.ui.swing.content.home.operation.add.AddOperationTableUI;
46  import fr.ifremer.reefdb.ui.swing.content.home.survey.SurveysTableRowModel;
47  import fr.ifremer.reefdb.ui.swing.util.AbstractReefDbBeanUIModel;
48  import fr.ifremer.reefdb.ui.swing.util.ReefDbUI;
49  import fr.ifremer.reefdb.ui.swing.util.table.AbstractReefDbTableUIHandler;
50  import jaxx.runtime.SwingUtil;
51  import org.apache.commons.collections4.CollectionUtils;
52  import org.apache.commons.lang3.StringUtils;
53  import org.apache.commons.logging.Log;
54  import org.apache.commons.logging.LogFactory;
55  import org.jdesktop.swingx.table.TableColumnExt;
56  
57  import javax.swing.BorderFactory;
58  import javax.swing.SortOrder;
59  import javax.swing.SwingWorker;
60  import java.awt.Dimension;
61  import java.util.List;
62  import java.util.Optional;
63  import java.util.concurrent.ExecutionException;
64  import java.util.concurrent.ThreadPoolExecutor;
65  
66  import static org.nuiton.i18n.I18n.t;
67  
68  /**
69   * Controleur pour la zone des prelevements de l'ecran d'accueil
70   */
71  public class OperationsTableUIHandler extends AbstractReefDbTableUIHandler<OperationsTableRowModel, OperationsTableUIModel, OperationsTableUI> {
72  
73      /**
74       * Logger.
75       */
76      private static final Log LOG = LogFactory.getLog(OperationsTableUIHandler.class);
77      private ThreadPoolExecutor executor;
78      private ExtendedComboBoxCellEditor<SamplingEquipmentDTO> samplingEquipmentCellEditor;
79      private ExtendedComboBoxCellEditor<DepartmentDTO> samplingDepartmentCellEditor;
80      private ExtendedComboBoxCellEditor<DepartmentDTO> analystDepartmentCellEditor;
81  
82      /**
83       * {@inheritDoc}
84       */
85      @Override
86      public OperationsTableModel getTableModel() {
87          return (OperationsTableModel) getTable().getModel();
88      }
89  
90      /**
91       * {@inheritDoc}
92       */
93      @Override
94      public SwingTable getTable() {
95          return getUI().getOperationTable();
96      }
97  
98      /**
99       * {@inheritDoc}
100      */
101     @Override
102     public void beforeInit(final OperationsTableUI ui) {
103         super.beforeInit(ui);
104 
105         // create model and register to the JAXX context
106         final OperationsTableUIModel model = new OperationsTableUIModel();
107         ui.setContextValue(model);
108     }
109 
110     /**
111      * {@inheritDoc}
112      */
113     @Override
114     public void afterInit(OperationsTableUI ui) {
115 
116         // Initialiser l UI
117         initUI(ui);
118 
119         createSamplingDepartmentCellEditor();
120         createAnalystDepartmentCellEditor();
121         createSamplingEquipmentCellEditor();
122 
123         iniTable();
124         SwingUtil.setLayerUI(ui.getHomePrelevementsScrollPane(), ui.getTableBlockLayer());
125 
126         // Ajout des listeners
127         initListeners();
128 
129         // Initilisation du bouton editer
130         initActionComboBox(getUI().getPrelevementsPanelEditerCombobox());
131 
132         ui.applyDataBinding(OperationsTableUI.BINDING_PRELEVEMENTS_PANEL_DUPLIQUER_BOUTON_ENABLED);
133         ui.applyDataBinding(OperationsTableUI.BINDING_PRELEVEMENTS_PANEL_EDITER_COMBOBOX_ENABLED);
134         ui.applyDataBinding(OperationsTableUI.BINDING_PRELEVEMENTS_PANEL_SUPPRIMER_BOUTON_ENABLED);
135 
136         // button border
137         getUI().getNextButton().setBorder(
138                 BorderFactory.createCompoundBorder(BorderFactory.createMatteBorder(2, 2, 2, 2, getConfig().getColorHighlightButtonBorder()), ui.getNextButton().getBorder())
139         );
140 
141         executor = ActionFactory.createSingleThreadExecutor(ActionFactory.ExecutionMode.LATEST);
142 
143     }
144 
145     @Override
146     public void onCloseUI() {
147         executor.shutdownNow();
148         super.onCloseUI();
149     }
150 
151     /**
152      * {@inheritDoc}
153      */
154     @Override
155     protected String[] getRowPropertiesToIgnore() {
156         return new String[]{
157                 OperationsTableRowModel.PROPERTY_ERRORS,
158                 OperationsTableRowModel.PROPERTY_DIRTY,
159                 OperationsTableRowModel.PROPERTY_MEASUREMENTS_LOADED
160         };
161     }
162 
163     /**
164      * {@inheritDoc}
165      */
166     @Override
167     protected boolean isRowValid(final OperationsTableRowModel row) {
168         return (!row.isEditable() || super.isRowValid(row)) & isOperationValid(row);
169     }
170 
171     private boolean isOperationValid(OperationsTableRowModel row) {
172 
173         // check name duplicates
174         if (getModel().getRowCount() >= 2) {
175             for (OperationsTableRowModel otherRow : getModel().getRows()) {
176                 if (row == otherRow) continue;
177                 if (StringUtils.isNotBlank(row.getName()) && row.getName().equals(otherRow.getName())) {
178                     // duplicate found
179                     ReefDbBeans.addError(row, t("reefdb.validator.error.samplingOperation.mnemonic.duplicate"), OperationsTableRowModel.PROPERTY_NAME);
180                 }
181             }
182         }
183 
184         // coordinates
185         if (row.getLatitude() == null ^ row.getLongitude() == null) {
186             ReefDbBeans.addError(row, t("reefdb.validator.error.coordinate.invalid"), OperationsTableRowModel.PROPERTY_LONGITUDE, OperationsTableRowModel.PROPERTY_LATITUDE);
187         }
188 
189         // positioning
190         if (row.getLatitude() != null && row.getLongitude() != null && row.getPositioning() == null) {
191             ReefDbBeans.addError(row, t("reefdb.validator.error.positioning.required"), OperationsTableRowModel.PROPERTY_POSITIONING);
192         }
193 
194         // analyst
195         if (row.getAnalyst() == null && row.getMeasurements().stream().anyMatch(measurement -> !ReefDbBeans.isMeasurementEmpty(measurement))) {
196             ReefDbBeans.addError(row, t("reefdb.validator.error.analyst.required"), OperationsTableRowModel.PROPERTY_ANALYST);
197         }
198 
199         // length
200         if (row.getSize() != null && row.getSizeUnit() == null) {
201             ReefDbBeans.addError(row, t("reefdb.validator.error.sizeUnit.required"), OperationsTableRowModel.PROPERTY_SIZE_UNIT);
202         }
203 
204         boolean hasNoError = ReefDbBeans.hasNoBlockingError(row);
205 
206         if (!hasNoError) {
207             ensureColumnsWithErrorAreVisible(row);
208         }
209 
210         return hasNoError;
211     }
212 
213     /**
214      * Initialisation des listeners.
215      */
216     private void initListeners() {
217 
218         // Listener sur le tableau
219         getModel().addPropertyChangeListener(evt -> {
220 
221             if (OperationsTableUIModel.PROPERTY_SINGLE_ROW_SELECTED.equals(evt.getPropertyName())) {
222                 // Si un seul element a ete selectionne
223                 final SamplingOperationDTO prelevement = getModel().getSingleSelectedRow();
224                 if (prelevement != null) {
225 
226                     updateSamplingDepartmentCellEditor(false);
227                     updateAnalystDepartmentCellEditor(false);
228                     updateSamplingEquipmentCellEditor(false);
229 
230                     // Sauvegarde de l identifiant du prelevement
231                     getContext().setSelectedSamplingOperationId(getModel().getSingleSelectedRow().getId());
232                 }
233 
234             } else if (OperationsTableUIModel.PROPERTY_SURVEY_EDITABLE.equals(evt.getPropertyName())) {
235 
236                 // Enable table model if survey is editable
237                 getTableModel().setReadOnly(!getModel().isSurveyEditable());
238             }
239         });
240     }
241 
242     /**
243      * Charge les prelevements.
244      *
245      * @param survey a {@link fr.ifremer.reefdb.ui.swing.content.home.survey.SurveysTableRowModel} object.
246      */
247     public void loadOperations(final SurveysTableRowModel survey) {
248 
249         // before affect new survey, need to save previous one
250         if (getTable().isEditing()) {
251             getTable().getCellEditor().stopCellEditing();
252         }
253 
254         // save actual table context before loading new survey
255         saveTableState();
256 
257         // affect new selected survey
258         getModel().setSurvey(survey);
259 
260         getModel().setLoading(true);
261         executor.execute(new SamplingOperationsLoader(survey));
262 
263     }
264 
265     /**
266      * Initialisation de la table prelevements.
267      */
268     private void iniTable() {
269 
270         // Colonne mnemonique
271         final TableColumnExt colName = addColumn(OperationsTableModel.NAME);
272         colName.setSortable(true);
273         colName.setMinWidth(100);
274 
275         // Colonne engin
276         final TableColumnExt colSamplingEquipment = addColumn(
277                 samplingEquipmentCellEditor,
278                 newTableCellRender(OperationsTableModel.SAMPLING_EQUIPMENT),
279                 OperationsTableModel.SAMPLING_EQUIPMENT
280         );
281         colSamplingEquipment.setSortable(true);
282         colSamplingEquipment.setMinWidth(100);
283 
284         // Colonne Heure
285         final TableColumnExt colTime = addColumn(
286                 new LocalTimeCellEditor(),
287                 newTableCellRender(Integer.class, DecoratorService.TIME_IN_HOURS_MINUTES),
288                 OperationsTableModel.TIME);
289         colTime.setSortable(true);
290         colTime.setMinWidth(50);
291 
292         // Colonne commentaire
293         addCommentColumn(OperationsTableModel.COMMENT);
294 
295         //------ Colonnes optionnelles
296 
297         // Colonne Taille
298         final TableColumnExt colSize = addColumn(
299                 newNumberCellEditor(Double.class, false, ReefDbUI.DECIMAL7_PATTERN),
300                 newNumberCellRenderer(7),
301                 OperationsTableModel.SIZE);
302         colSize.setSortable(true);
303         colSize.setMinWidth(50);
304 
305         // Colonne Unite taille
306         final TableColumnExt colSizeUnit = addExtendedComboDataColumnToModel(
307                 OperationsTableModel.SIZE_UNIT,
308                 getContext().getReferentialService().getUnits(StatusFilter.ACTIVE),
309                 false);
310         colSizeUnit.setSortable(true);
311         colSizeUnit.setMinWidth(100);
312 
313         // Colonne service préleveur
314         final TableColumnExt colSamplingDepartment = addColumn(
315                 samplingDepartmentCellEditor,
316                 newTableCellRender(OperationsTableModel.SAMPLING_DEPARTMENT),
317                 OperationsTableModel.SAMPLING_DEPARTMENT
318         );
319         colSamplingDepartment.setSortable(true);
320         colSamplingDepartment.setMinWidth(100);
321 
322         // Colonne immersion
323         final TableColumnExt colDepth = addColumn(
324                 newNumberCellEditor(Double.class, false, ReefDbUI.DECIMAL2_PATTERN),
325                 newNumberCellRenderer(2),
326                 OperationsTableModel.DEPTH);
327         colDepth.setSortable(true);
328         colDepth.setMinWidth(100);
329 
330         // Colonne immersion min
331         final TableColumnExt colMinDepth = addColumn(
332                 newNumberCellEditor(Double.class, false, ReefDbUI.DECIMAL2_PATTERN),
333                 newNumberCellRenderer(2),
334                 OperationsTableModel.MIN_DEPTH);
335         colMinDepth.setSortable(true);
336         colMinDepth.setMinWidth(100);
337 
338         // Colonne immersion
339         final TableColumnExt colMaxDepth = addColumn(
340                 newNumberCellEditor(Double.class, false, ReefDbUI.DECIMAL2_PATTERN),
341                 newNumberCellRenderer(2),
342                 OperationsTableModel.MAX_DEPTH);
343         colMaxDepth.setSortable(true);
344         colMaxDepth.setMinWidth(100);
345 
346         // Colonne Coordonnées réelles
347         final TableColumnExt colLatitude = addCoordinateColumnToModel(
348                 CoordinateEditor.CoordinateType.LATITUDE_MIN,
349                 OperationsTableModel.LATITUDE);
350         colLatitude.setSortable(true);
351         colLatitude.setMinWidth(100);
352 
353         final TableColumnExt colLongitude = addCoordinateColumnToModel(
354                 CoordinateEditor.CoordinateType.LONGITUDE_MIN,
355                 OperationsTableModel.LONGITUDE);
356         colLongitude.setSortable(true);
357         colLongitude.setMinWidth(100);
358 
359         // Colonne informations de positionnement
360         final TableColumnExt colPositioning = addExtendedComboDataColumnToModel(
361                 OperationsTableModel.POSITIONING,
362                 getContext().getReferentialService().getPositioningSystems(),
363                 false);
364         colPositioning.setSortable(true);
365         colPositioning.setMinWidth(100);
366 
367         final TableColumnExt colPositioningPrecision = addColumn(
368                 OperationsTableModel.POSITIONING_PRECISION);
369         colPositioningPrecision.setSortable(true);
370         colPositioningPrecision.setMinWidth(100);
371         colPositioningPrecision.setEditable(false);
372 
373         // Colonne service analyste
374         final TableColumnExt colAnalystDepartment = addColumn(
375             analystDepartmentCellEditor,
376             newTableCellRender(OperationsTableModel.ANALYST),
377             OperationsTableModel.ANALYST
378         );
379         colAnalystDepartment.setSortable(true);
380         colAnalystDepartment.setMinWidth(100);
381 
382         // Modele de la table
383         final OperationsTableModel tableModel = new OperationsTableModel(getTable().getColumnModel(), true);
384         getTable().setModel(tableModel);
385 
386         // Initialisation de la table
387         initTable(getTable());
388 
389         // Les colonnes optionnelles sont invisibles
390         colSize.setVisible(false);
391         colSizeUnit.setVisible(false);
392         colSamplingDepartment.setVisible(false);
393 //        colonneNiveau.setVisible(false);
394         colDepth.setVisible(false);
395         colMinDepth.setVisible(false);
396         colMaxDepth.setVisible(false);
397         colLatitude.setVisible(false);
398         colLongitude.setVisible(false);
399         colPositioning.setVisible(false);
400         colPositioningPrecision.setVisible(false);
401 
402         // Tri par defaut
403         getTable().setSortOrder(OperationsTableModel.NAME, SortOrder.ASCENDING);
404 
405         // border
406         addEditionPanelBorder();
407     }
408 
409     private void createSamplingEquipmentCellEditor() {
410 
411         samplingEquipmentCellEditor = newExtendedComboBoxCellEditor(null, SamplingEquipmentDTO.class, false);
412 
413         samplingEquipmentCellEditor.setAction("unfilter", "reefdb.common.unfilter", e -> {
414             if (!askBefore(t("reefdb.common.unfilter"), t("reefdb.common.unfilter.confirmation"))) {
415                 return;
416             }
417             // unfilter location
418             updateSamplingEquipmentCellEditor(true);
419             getTable().requestFocus();
420         });
421 
422     }
423 
424     private void updateSamplingEquipmentCellEditor(boolean forceNoFilter) {
425 
426         samplingEquipmentCellEditor.getCombo().setActionEnabled(!forceNoFilter
427                 && getContext().getDataContext().isContextFiltered(FilterTypeValues.SAMPLING_EQUIPMENT));
428 
429         samplingEquipmentCellEditor.getCombo().setData(
430                 getContext().getObservationService().getAvailableSamplingEquipments(forceNoFilter));
431     }
432 
433     private void createSamplingDepartmentCellEditor() {
434 
435         samplingDepartmentCellEditor = newExtendedComboBoxCellEditor(null, DepartmentDTO.class, false);
436 
437         samplingDepartmentCellEditor.setAction("unfilter", "reefdb.common.unfilter", e -> {
438             if (!askBefore(t("reefdb.common.unfilter"), t("reefdb.common.unfilter.confirmation"))) {
439                 return;
440             }
441             // unfilter department
442             updateSamplingDepartmentCellEditor(true);
443             getTable().requestFocus();
444         });
445 
446     }
447 
448     private void updateSamplingDepartmentCellEditor(boolean forceNoFilter) {
449 
450         samplingDepartmentCellEditor.getCombo().setActionEnabled(!forceNoFilter
451                 && getContext().getDataContext().isContextFiltered(FilterTypeValues.DEPARTMENT));
452 
453         samplingDepartmentCellEditor.getCombo().setData(
454                 getContext().getObservationService().getAvailableDepartments(forceNoFilter));
455     }
456 
457     private void createAnalystDepartmentCellEditor() {
458 
459         analystDepartmentCellEditor = newExtendedComboBoxCellEditor(null, DepartmentDTO.class, false);
460 
461         analystDepartmentCellEditor.setAction("unfilter", "reefdb.common.unfilter", e -> {
462             if (!askBefore(t("reefdb.common.unfilter"), t("reefdb.common.unfilter.confirmation"))) {
463                 return;
464             }
465             // unfilter department
466             updateAnalystDepartmentCellEditor(true);
467             getTable().requestFocus();
468         });
469 
470     }
471 
472     private void updateAnalystDepartmentCellEditor(boolean forceNoFilter) {
473 
474         analystDepartmentCellEditor.getCombo().setActionEnabled(!forceNoFilter
475                 && getContext().getDataContext().isContextFiltered(FilterTypeValues.DEPARTMENT));
476 
477         analystDepartmentCellEditor.getCombo().setData(
478                 getContext().getObservationService().getAvailableDepartments(forceNoFilter));
479     }
480 
481     /**
482      * {@inheritDoc}
483      */
484     @Override
485     protected void onRowModified(int rowIndex, OperationsTableRowModel row, String propertyName, Integer propertyIndex, Object oldValue, Object newValue) {
486         super.onRowModified(rowIndex, row, propertyName, propertyIndex, oldValue, newValue);
487 
488         row.setDirty(true);
489 
490         if (OperationsTableRowModel.PROPERTY_NAME.equals(propertyName)) {
491             recomputeRowsValidState();
492 
493         } else if (OperationsTableRowModel.PROPERTY_PMFMS.equals(propertyName)) {
494             // force measurements loaded to true to tell the service to save measurements
495             // in case of duplicated sampling operation, this flag is set to false, so the measurements was not saved (Mantis #0027276)
496             row.setMeasurementsLoaded(true);
497 
498         }
499 
500         saveToSurvey();
501     }
502 
503     /**
504      * <p>addSamplingOperations.</p>
505      */
506     public void addSamplingOperations() {
507 
508         AddOperationTableUI dialog = new AddOperationTableUI(getContext());
509         dialog.getHandler().load(getModel());
510         openDialog(dialog, new Dimension(1000, 232));
511 
512         // Add new rows
513         List<OperationsTableRowModel> newOperations = dialog.getModel().getRows();
514         if (dialog.getModel().isValid() && CollectionUtils.isNotEmpty(newOperations)) {
515 
516             // Use label auto generation, now with specified label prefix (Mantis #47245)
517             String labelPrefix = dialog.getModel().getOperationNamePrefix();
518             StringIterator labelIterator = StringIterator.newStringIteratorByProperty(getModel().getRows(), SamplingOperationDTO.PROPERTY_NAME, labelPrefix);
519             newOperations.forEach(newOperation -> newOperation.setName(labelIterator.next()));
520 
521             getModel().addRows(newOperations);
522             getModel().setModify(true);
523 
524             recomputeRowsValidState();
525             saveToSurvey();
526 
527             // force focus on first empty name (Mantis #40749)
528             for (OperationsTableRowModel row : getModel().getRows()) {
529                 if (StringUtils.isBlank(row.getName())) {
530                     getTable().setTerminateEditOnFocusLost(false); // Trick to prevent focus owner change (even if setFocusOnCell is invoked later)
531                     setFocusOnCell(row);
532                     break;
533                 }
534             }
535         }
536 
537     }
538 
539     /**
540      * <p>removeSamplingOperations.</p>
541      */
542     public void removeSamplingOperations() {
543 
544         if (getModel().getSelectedRows().isEmpty()) {
545             LOG.warn("Aucun prelevement de selectionne");
546             return;
547         }
548 
549         // Demande de confirmation avant la suppression
550         boolean canContinue = askBeforeDelete(t("reefdb.action.delete.samplingOperation.title"), t("reefdb.action.delete.samplingOperation.message"));
551 
552         if (canContinue) {
553 
554             // check measurement
555             boolean hasMeasurement = false;
556             for (OperationsTableRowModel row : getModel().getSelectedRows()) {
557 
558                 if (!row.isMeasurementsEmpty()) {
559                     for (MeasurementDTO measurement : row.getMeasurements()) {
560                         if (measurement.getQualitativeValue() != null || measurement.getNumericalValue() != null) {
561                             hasMeasurement = true;
562                             break;
563                         }
564                     }
565                 }
566             }
567 
568             if (hasMeasurement) {
569                 canContinue = askBeforeDelete(t("reefdb.action.delete.samplingOperation.measurement.title"), t("reefdb.action.delete.samplingOperation.measurement.message"));
570             }
571         }
572 
573         if (canContinue) {
574 
575             // Suppression des lignes
576             getModel().deleteSelectedRows();
577 
578             // Recompute valid state (Mantis #33741)
579             recomputeRowsValidState();
580 
581             forceRevalidateModel();
582 
583             saveToSurvey();
584         }
585     }
586 
587     /**
588      * <p>saveToSurvey.</p>
589      */
590     private void saveToSurvey() {
591 
592         // save analyst in every measurements (Mantis #42782)
593         for (OperationsTableRowModel rowModel : getModel().getRows()) {
594             if (!rowModel.isMeasurementsEmpty()) {
595                 rowModel.getMeasurements().forEach(measurement -> measurement.setAnalyst(rowModel.getAnalyst()));
596             }
597         }
598 
599         // save sampling operations in parent model
600         getModel().getSurvey().setSamplingOperations(getModel().getBeans());
601         getModel().getSurvey().setSamplingOperationsLoaded(true);
602 
603         // force model modify
604         getModel().firePropertyChanged(AbstractReefDbBeanUIModel.PROPERTY_MODIFY, null, true);
605     }
606 
607     /**
608      * {@inheritDoc}
609      */
610     @Override
611     protected void onRowsAdded(List<OperationsTableRowModel> addedRows) {
612         super.onRowsAdded(addedRows);
613 
614         if (addedRows.size() == 1) {
615             OperationsTableRowModel rowModel = addedRows.get(0);
616 
617             rowModel.setDirty(true);
618 
619             // Add default name
620             if (StringUtils.isBlank(rowModel.getName())) {
621                 rowModel.setName(StringIterator.newStringIteratorByProperty(getModel().getRows(), SamplingOperationDTO.PROPERTY_NAME).next());
622             }
623 
624             // Add default service from observation, if any
625             if (rowModel.getSamplingDepartment() == null) {
626                 rowModel.setSamplingDepartment(getModel().getSurvey().getRecorderDepartment());
627             }
628 
629             // create all empty measurements
630             rowModel.addAllPmfms(getModel().getPmfms());
631             for (PmfmDTO pmfm : getModel().getPmfms()) {
632                 MeasurementDTO measurement = ReefDbBeanFactory.newMeasurementDTO();
633                 measurement.setPmfm(pmfm);
634                 rowModel.addMeasurements(measurement);
635             }
636 
637             // The analyst from strategy (Mantis #42619)
638             if (rowModel.getAnalyst() == null) {
639                 rowModel.setAnalyst(
640                         getContext().getProgramStrategyService().getAnalysisDepartmentOfAppliedStrategyBySurvey(
641                                 getModel().getSurvey(),
642                                 rowModel.getPmfms()
643                         ));
644             }
645 
646             getModel().setModify(true);
647             setFocusOnCell(rowModel);
648         }
649     }
650 
651     /**
652      * <p>onNext.</p>
653      */
654     public void onNext() {
655 
656         HomeUI homeUI = getUI().getParentContainer(HomeUI.class);
657 
658         getContext().getActionEngine().runAction(homeUI.getSurveysTable().getObservationEditerGeneralBouton());
659     }
660 
661     private class SamplingOperationsLoader extends SwingWorker<Object, Object> {
662 
663         List<PmfmDTO> pmfms;
664         final SurveyDTO survey;
665 
666         private SamplingOperationsLoader(SurveyDTO survey) {
667             this.survey = survey;
668         }
669 
670         @Override
671         protected Object doInBackground() {
672 
673             if (survey != null) {
674 
675                 // load sampling operations, check loaded first to avoid unnecessary call to service
676                 if (!survey.isSamplingOperationsLoaded()) {
677                     getContext().getObservationService().loadSamplingOperationsFromSurvey(survey, false /* don't load individual measurements */);
678                 }
679 
680                 // Load pmfms from strategies
681                 pmfms = getContext().getProgramStrategyService().getPmfmsForSamplingOperationBySurvey(survey);
682 
683                 // Load other Pmfms in sampling operations
684                 if (!survey.isSamplingOperationsEmpty()) {
685                     // populate pmfms from strategy to sampling operation and vice versa
686                     for (SamplingOperationDTO samplingOperation : survey.getSamplingOperations()) {
687                         ReefDbBeans.fillListsEachOther(samplingOperation.getPmfms(), pmfms);
688                     }
689                 }
690             }
691 
692             return null;
693         }
694 
695         @Override
696         protected void done() {
697             try {
698                 if (isCancelled()) {
699                     return;
700                 }
701                 try {
702                     get();
703                 } catch (InterruptedException | ExecutionException e) {
704                     throw new ReefDbTechnicalException(e.getMessage(), e);
705                 }
706 
707                 // Set model properties
708                 getModel().setPmfms(pmfms);
709 
710                 // Initialiser la table des prelevements
711                 addPmfmColumns(
712                         getModel().getPmfms(),
713                         SamplingOperationDTO.PROPERTY_PMFMS,
714                         DecoratorService.NAME_WITH_UNIT,
715                         OperationsTableModel.TIME);
716 
717                 boolean notEmpty = CollectionUtils.isNotEmpty(getModel().getPmfmColumns());
718 
719                 if (survey != null && !survey.isSamplingOperationsEmpty()) {
720                     // Chargement du tableau avec les prelevements
721                     getModel().setBeans(survey.getSamplingOperations());
722 
723                     if (notEmpty) {
724                         for (OperationsTableRowModel row : getModel().getRows()) {
725                             // set analyst from first non null measurement
726                             Optional<MeasurementDTO> measurementFound = row.getMeasurements().stream().filter(measurement -> measurement.getAnalyst() != null).findFirst();
727                             measurementFound.ifPresent(measurementDTO -> row.setAnalyst(measurementDTO.getAnalyst()));
728 
729                         }
730                     }
731 
732                     recomputeRowsValidState();
733 
734                 } else {
735 
736                     // reset all
737                     getModel().setBeans(null);
738                 }
739                 forceRevalidateModel();
740 
741                 // restore table state from swing session
742                 restoreTableState();
743 
744                 // hide analyst column if no pmfm (Mantis #42619)
745 //                forceColumnVisibleAtLastPosition(OperationsTableModel.ANALYST, notEmpty);
746                 // Don't force position (Mantis #49537)
747                 forceColumnVisible(OperationsTableModel.ANALYST, notEmpty);
748 
749                 // set columns with errors visible
750                 ensureColumnsWithErrorAreVisible(getModel().getRows());
751 
752             } finally {
753                 getModel().setLoading(false);
754             }
755         }
756     }
757 }