View Javadoc
1   package fr.ifremer.reefdb.ui.swing.content.manage.program.programs;
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.referential.StatusCode;
27  import fr.ifremer.quadrige3.ui.swing.table.SwingTable;
28  import fr.ifremer.reefdb.dao.technical.Daos;
29  import fr.ifremer.reefdb.dto.ReefDbBeans;
30  import fr.ifremer.reefdb.dto.configuration.programStrategy.ProgramDTO;
31  import fr.ifremer.reefdb.service.ReefDbTechnicalException;
32  import fr.ifremer.reefdb.ui.swing.content.manage.program.ProgramsUI;
33  import fr.ifremer.reefdb.ui.swing.content.manage.program.programs.departments.DepartmentsDialogUI;
34  import fr.ifremer.reefdb.ui.swing.content.manage.program.programs.users.UsersDialogUI;
35  import fr.ifremer.reefdb.ui.swing.util.ReefDbUIs;
36  import fr.ifremer.reefdb.ui.swing.util.table.AbstractReefDbTableModel;
37  import fr.ifremer.reefdb.ui.swing.util.table.AbstractReefDbTableUIHandler;
38  import fr.ifremer.reefdb.ui.swing.util.table.renderer.StatusRenderer;
39  import jaxx.runtime.validator.swing.SwingValidator;
40  import org.apache.commons.lang3.StringUtils;
41  import org.apache.commons.logging.Log;
42  import org.apache.commons.logging.LogFactory;
43  import org.jdesktop.swingx.table.TableColumnExt;
44  
45  import javax.swing.SortOrder;
46  import javax.swing.SwingUtilities;
47  import javax.swing.SwingWorker;
48  import java.awt.event.MouseAdapter;
49  import java.awt.event.MouseEvent;
50  import java.util.Collection;
51  import java.util.List;
52  import java.util.concurrent.ExecutionException;
53  
54  import static org.nuiton.i18n.I18n.t;
55  
56  /**
57   * Controleur pour la zone des programmes.
58   */
59  public class ProgramsTableUIHandler extends AbstractReefDbTableUIHandler<ProgramsTableRowModel, ProgramsTableUIModel, ProgramsTableUI> {
60  
61      /**
62       * Logger.
63       */
64      private static final Log LOG = LogFactory.getLog(ProgramsTableUIHandler.class);
65  
66      private StrategyAndLocationLoader strategyAndLocationLoader;
67      private boolean newRowSelected;
68  
69      /**
70       * <p>Constructor for ProgramsTableUIHandler.</p>
71       */
72      public ProgramsTableUIHandler() {
73          super(ProgramsTableRowModel.PROPERTY_NAME,
74                  ProgramsTableRowModel.PROPERTY_COMMENT,
75                  ProgramsTableRowModel.PROPERTY_CODE);
76      }
77  
78      /**
79       * {@inheritDoc}
80       */
81      @Override
82      protected String[] getRowPropertiesToIgnore() {
83          return new String[]{ProgramsTableRowModel.PROPERTY_STRATEGIES, ProgramsTableRowModel.PROPERTY_STRATEGIES_LOADED,
84                  ProgramsTableRowModel.PROPERTY_LOCATIONS, ProgramsTableRowModel.PROPERTY_LOCATIONS_LOADED,
85                  ProgramsTableRowModel.PROPERTY_ERRORS};
86      }
87  
88      /**
89       * {@inheritDoc}
90       */
91      @Override
92      public AbstractReefDbTableModel<ProgramsTableRowModel> getTableModel() {
93          return (ProgramsTableModel) getTable().getModel();
94      }
95  
96      /**
97       * {@inheritDoc}
98       */
99      @Override
100     public SwingTable getTable() {
101         return getUI().getProgramsTable();
102     }
103 
104     /**
105      * {@inheritDoc}
106      */
107     @Override
108     public void beforeInit(final ProgramsTableUI ui) {
109         super.beforeInit(ui);
110 
111         // create model and register to the JAXX context
112         final ProgramsTableUIModel model = new ProgramsTableUIModel();
113         ui.setContextValue(model);
114     }
115 
116     /**
117      * {@inheritDoc}
118      */
119     @Override
120     public void afterInit(ProgramsTableUI ui) {
121 
122         // Initialiser l UI
123         initUI(ui);
124 
125         // Initialiser le tableau
126         initTable();
127 
128         // Ajout des listeners
129         initListeners();
130 
131         // Initilisation du bouton editer
132         initActionComboBox(getUI().getEditCombobox());
133 
134         // Register validator
135         registerValidators(getValidator());
136         listenValidatorValid(getValidator(), getModel());
137 
138     }
139 
140     /**
141      * {@inheritDoc}
142      */
143     @Override
144     public boolean isRowValid(final ProgramsTableRowModel row) {
145         row.getErrors().clear();
146 
147         return !row.isEditable() || (super.isRowValid(row) && isProgramValid(row));
148     }
149 
150     private boolean isProgramValid(ProgramsTableRowModel row) {
151 
152         // check if a national program has at least one manager
153         if (!row.isLocal() && row.sizeManagerPersons() == 0) {
154             ReefDbBeans.addError(row, t("reefdb.programs.validation.error.noManager"), ProgramsTableRowModel.PROPERTY_CODE);
155         }
156 
157         // check if at least one location is set
158         if (row.isLocationsLoaded() && row.isLocationsEmpty()) {
159             ReefDbBeans.addError(row, t("reefdb.programs.validation.error.noLocationOnProg"), ProgramsTableRowModel.PROPERTY_CODE);
160         }
161 
162         // check sub-models
163         if (!row.isStrategiesValid() || !row.isLocationsValid()) {
164             ReefDbBeans.addError(row, t("reefdb.program.tables.error"), ProgramsTableRowModel.PROPERTY_CODE);
165         }
166 
167         return row.isErrorsEmpty();
168     }
169 
170     /**
171      * load programs in table
172      *
173      * @param programs programs to load
174      */
175     public void loadPrograms(Collection<ProgramDTO> programs) {
176 
177         getModel().setSingleSelectedRow(null);
178         getModel().setBeans(programs);
179 
180         boolean isAdmin = getContext().isAuthenticatedAsAdmin();
181         getModel().getRows().forEach(program -> {
182             // a program is editable if save is enabled
183             program.setEditable(getModel().isSaveEnabled());
184 
185             // determinate if current user is manager
186             program.setCurrentUserIsManager(
187                     isAdmin && (
188                             program.isLocal() ||
189                                     program.getManagerPersons().stream().anyMatch(personDTO -> personDTO.getId().equals(getContext().getDataContext().getRecorderPersonId()))
190                     )
191             );
192 
193         });
194 
195         autoSelectRow();
196     }
197 
198     /**
199      * Clear program table
200      */
201     public void clearTable() {
202 
203         getModel().setBeans(null);
204 
205     }
206 
207     /**
208      * {@inheritDoc}
209      */
210     @Override
211     protected void onRowsAdded(List<ProgramsTableRowModel> addedRows) {
212         super.onRowsAdded(addedRows);
213 
214         // should be only one row
215         if (addedRows.size() == 1) {
216             final ProgramsTableRowModel row = addedRows.get(0);
217 
218             if (checkCodeOnNewProgramRow(row)) {
219 
220                 row.setStatus(Daos.getStatus(StatusCode.LOCAL_ENABLE));
221                 row.setCurrentUserIsManager(true);
222                 row.setNewCode(true);
223                 setFocusOnCell(row);
224 
225             } else {
226 
227                 // the code is invalid, delete this row
228                 SwingUtilities.invokeLater(() -> {
229                     getModel().deleteRow(row);
230                     getModel().setSingleSelectedRow(null);
231                 });
232             }
233         }
234 
235     }
236 
237     /**
238      * {@inheritDoc}
239      */
240     @Override
241     protected void onRowModified(int rowIndex, ProgramsTableRowModel row, String propertyName, Integer propertyIndex, Object oldValue, Object newValue) {
242         super.onRowModified(rowIndex, row, propertyName, propertyIndex, oldValue, newValue);
243 
244         row.setDirty(true);
245     }
246 
247     /**
248      * Add a code on a new row, do nothing on an existing row, delete a new row without code
249      *
250      * @param selectedRow
251      * @return true if new code or already existing code
252      */
253     private boolean checkCodeOnNewProgramRow(final ProgramsTableRowModel selectedRow) {
254 
255         // Restricted access to admin (Mantis #31020)
256         if (!getContext().isAuthenticatedAsAdmin()) {
257             getContext().getDialogHelper().showErrorDialog(t("reefdb.error.authenticate.accessDenied"));
258             return false;
259         }
260 
261         // Ask user for a new code
262         boolean edit = StringUtils.isNotBlank(selectedRow.getCode());
263         String newCode = (String) getContext().getDialogHelper().showInputDialog(
264                 getUI(),
265                 t("reefdb.property.program.code"),
266                 edit ? t("reefdb.program.editCode.title") : t("reefdb.program.setCode.title"),
267                 null,
268                 selectedRow.getCode());
269 
270         if (StringUtils.isBlank(newCode)) {
271             return false;
272         }
273 
274         newCode = newCode.trim();
275         // Check if the program code already exist in UI
276         for (ProgramsTableRowModel programCheck : getModel().getRows()) {
277             if (selectedRow == programCheck) continue;
278             if (newCode.equalsIgnoreCase(programCheck.getCode())) {
279                 // Error messages
280                 getContext().getDialogHelper().showErrorDialog(
281                         t("reefdb.error.alreadyExists.referential", t("reefdb.property.program"), newCode,
282                                 ReefDbBeans.isLocalStatus(programCheck.getStatus()) ? t("reefdb.property.referential.local") : t("reefdb.property.referential.national")),
283                         edit ? t("reefdb.program.editCode.title") : t("reefdb.program.setCode.title"));
284                 return false;
285             }
286         }
287 
288         ProgramDTO existingProgram = getContext().getProgramStrategyService().isProgramExists(newCode);
289         if (existingProgram != null) {
290             // Error messages
291             getContext().getDialogHelper().showErrorDialog(
292                     t("reefdb.error.alreadyExists.referential", t("reefdb.property.program"), newCode,
293                             ReefDbBeans.isLocalStatus(existingProgram.getStatus()) ? t("reefdb.property.referential.local") : t("reefdb.property.referential.national")),
294                     edit ? t("reefdb.program.editCode.title") : t("reefdb.program.setCode.title"));
295             return false;
296         }
297 
298         selectedRow.setCode(newCode);
299         return true;
300     }
301 
302     /**
303      * <p>editProgramCode.</p>
304      */
305     public void editProgramCode() {
306         ProgramsTableRowModel row = getModel().getSingleSelectedRow();
307         if (row != null && row.isLocal() && row.isNewCode()) {
308             if (checkCodeOnNewProgramRow(row)) {
309                 row.setDirty(true);
310             }
311         }
312     }
313 
314     /**
315      * Initialisation des listeners.
316      */
317     private void initListeners() {
318 
319         // Listener sur le tableau
320         getModel().addPropertyChangeListener(ProgramsTableUIModel.PROPERTY_SINGLE_ROW_SELECTED, evt -> {
321 
322             final ProgramsUI adminProgrammeUI = getProgramsUI();
323             final ProgramsTableRowModel selectedProgram = getModel().getSingleSelectedRow();
324 
325             // Si un seul element a ete selectionne
326             if (selectedProgram != null && StringUtils.isNotBlank(selectedProgram.getCode())) {
327 
328                 newRowSelected = true;
329 
330                 // Suppression des informations du contexte
331                 getContext().setSelectedProgramCode(selectedProgram.getCode());
332 
333                 if (strategyAndLocationLoader != null && !strategyAndLocationLoader.isDone()) {
334                     strategyAndLocationLoader.cancel(true);
335                 }
336                 adminProgrammeUI.getStrategiesTableUI().getModel().setLoading(true);
337                 adminProgrammeUI.getLocationsTableUI().getModel().setLoading(true);
338                 strategyAndLocationLoader = new StrategyAndLocationLoader(selectedProgram);
339                 strategyAndLocationLoader.execute();
340 
341             }
342         });
343 
344         getModel().addPropertyChangeListener(ProgramsTableUIModel.PROPERTY_SELECTED_ROWS, evt -> {
345 
346             if (getModel().getSelectedRows().size() == 1) {
347                 ProgramsTableRowModel selected = getModel().getSelectedRows().iterator().next();
348                 getUI().getEditCombobox().setEnabled(selected.getStatus() != null && !selected.isLocal());
349             } else {
350                 getUI().getEditCombobox().setEnabled(false);
351             }
352         });
353 
354         // add a click listener on table to handle unselect strategy table
355         getTable().addMouseListener(new MouseAdapter() {
356             @Override
357             public void mouseClicked(MouseEvent e) {
358                 if (!newRowSelected) {
359                     getModel().firePropertyChanged(ProgramsTableUIModel.PROPERTY_SINGLE_ROW_SELECTED, null, null);
360                 }
361                 newRowSelected = false;
362             }
363 
364         });
365 
366         // Add listener on saveEnabled property to disable change (Mantis #47532)
367         getModel().addPropertyChangeListener(ProgramsTableUIModel.PROPERTY_SAVE_ENABLED, evt -> {
368             getModel().getRows().forEach(programsTableRowModel -> programsTableRowModel.setEditable(getModel().isSaveEnabled()));
369             getUI().getProgramsTable().invalidate();
370             getUI().getProgramsTable().repaint();
371             autoSelectRow();
372         });
373 
374     }
375 
376     /**
377      * <p>removeLocations.</p>
378      *
379      * @param locationIds a {@link java.util.List} object.
380      */
381     public void removeLocations(List<Integer> locationIds) {
382         ProgramsTableRowModel programModel = getModel().getSingleSelectedRow();
383         if (programModel != null) {
384             programModel.getLocations().removeIf(location -> locationIds.contains(location.getId()));
385             recomputeRowValidState(programModel);
386         }
387     }
388 
389     /**
390      * <p>showManagers.</p>
391      */
392     public void showManagers() {
393 
394         if (getModel().getSingleSelectedRow() == null) {
395             return;
396         }
397 
398         UsersDialogUI usersDialogUI = new UsersDialogUI(getUI());
399         usersDialogUI.getModel().setManagerList(true);
400         usersDialogUI.getModel().setProgram(getModel().getSingleSelectedRow());
401 
402         openDialog(usersDialogUI);
403 
404     }
405 
406     /**
407      * <p>showRecorderPersons.</p>
408      */
409     public void showRecorderPersons() {
410 
411         if (getModel().getSingleSelectedRow() == null) {
412             return;
413         }
414 
415         UsersDialogUI usersDialogUI = new UsersDialogUI(getUI());
416         usersDialogUI.getModel().setManagerList(false);
417         usersDialogUI.getModel().setProgram(getModel().getSingleSelectedRow());
418 
419         openDialog(usersDialogUI);
420 
421     }
422 
423     /**
424      * <p>showRecorderDepartments.</p>
425      */
426     public void showRecorderDepartments() {
427 
428         if (getModel().getSingleSelectedRow() == null) {
429             return;
430         }
431 
432         DepartmentsDialogUI usersDialogUI = new DepartmentsDialogUI(getUI());
433         usersDialogUI.getModel().setProgram(getModel().getSingleSelectedRow());
434 
435         openDialog(usersDialogUI);
436 
437     }
438 
439     /**
440      * Initialisation de le tableau.
441      */
442     private void initTable() {
443 
444         // Column code
445         final TableColumnExt columnCode = addColumn(
446                 ProgramsTableModel.CODE);
447         columnCode.setSortable(true);
448 
449         // Column libelle
450         final TableColumnExt columnLibelle = addColumn(
451                 ProgramsTableModel.NAME);
452         columnLibelle.setSortable(true);
453 
454         // Column description
455         final TableColumnExt columnDescription = addCommentColumn(ProgramsTableModel.DESCRIPTION, ProgramsTableRowModel.PROPERTY_DESCRIPTION, true);
456         columnDescription.setSortable(false);
457 
458         // Comment, creation and update dates
459         addCommentColumn(ProgramsTableModel.COMMENT);
460         TableColumnExt creationDateCol = addDatePickerColumnToModel(ProgramsTableModel.CREATION_DATE, getConfig().getDateTimeFormat(), false);
461         fixColumnWidth(creationDateCol, 120);
462         TableColumnExt updateDateCol = addDatePickerColumnToModel(ProgramsTableModel.UPDATE_DATE, getConfig().getDateTimeFormat(), false);
463         fixColumnWidth(updateDateCol, 120);
464 
465 
466         final TableColumnExt departmentHermeticCol = addBooleanColumnToModel(ProgramsTableModel.DEPARTMENT_HERMETIC, getTable());
467         fixColumnWidth(departmentHermeticCol, ReefDbUIs.REEFDB_CHECKBOX_WIDTH);
468         departmentHermeticCol.setSortable(true);
469 
470         // Column local
471         final TableColumnExt columnStatus = addColumn(null, new StatusRenderer(), ProgramsTableModel.STATUS);
472         fixColumnWidth(columnStatus, ReefDbUIs.REEFDB_CHECKBOX_WIDTH);
473         columnStatus.setSortable(true);
474 
475         // Modele de la table
476         final ProgramsTableModel tableModel = new ProgramsTableModel(getTable().getColumnModel());
477         getTable().setModel(tableModel);
478 
479         // Columns non editable
480         tableModel.setNoneEditableCols(ProgramsTableModel.CODE, ProgramsTableModel.STATUS);
481 
482         // Les columns obligatoire sont toujours presentes
483         columnStatus.setHideable(false);
484         columnCode.setHideable(false);
485         columnLibelle.setHideable(false);
486         columnDescription.setHideable(false);
487 
488         // Initialisation de la table
489         initTable(getTable());
490 
491         creationDateCol.setVisible(false);
492         updateDateCol.setVisible(false);
493 
494         // Number rows visible
495         getTable().setVisibleRowCount(4);
496 
497         // Tri par defaut
498         getTable().setSortOrder(ProgramsTableModel.CODE, SortOrder.ASCENDING);
499     }
500 
501     /**
502      * Selection d une ligne dans le tableau suivant l identifiant de la ligne.
503      */
504     public void autoSelectRow() {
505 
506         String previousSelectedProgramCode = getContext().getSelectedProgramCode();
507 
508         if (StringUtils.isBlank(previousSelectedProgramCode) && getModel().getRowCount() > 1) {
509             return;
510         }
511 
512         // force deselection first
513         getModel().setSingleSelectedRow(null);
514 
515         ProgramsTableRowModel rowToSelect = null;
516         if (getModel().getRowCount() == 1) {
517 
518             // unique row
519             rowToSelect = getModel().getRows().get(0);
520 
521         } else {
522 
523             // find row with the previous selected program code
524             for (final ProgramsTableRowModel row : getModel().getRows()) {
525                 if (row.getCode().equals(previousSelectedProgramCode)) {
526                     rowToSelect = row;
527                     break;
528                 }
529             }
530         }
531 
532         // Selected row
533         if (rowToSelect != null) {
534             ProgramsTableRowModel finalRowToSelect = rowToSelect;
535             SwingUtilities.invokeLater(() -> {
536                 selectRow(finalRowToSelect);
537                 getModel().setSingleSelectedRow(finalRowToSelect);
538             });
539         }
540     }
541 
542     /**
543      * Save update (data in memory) on slave table in master table
544      */
545     public void keepModificationOnStrategiesTable() {
546 
547         if (getModel().getSingleSelectedRow() != null) {
548             getModel().getSingleSelectedRow().setStrategies(getProgramsUI().getStrategiesTableUI().getHandler().getModel().getBeans());
549             getModel().getSingleSelectedRow().setStrategiesLoaded(true);
550         }
551 
552     }
553 
554     /**
555      * <p>getProgramsUI.</p>
556      *
557      * @return a {@link fr.ifremer.reefdb.ui.swing.content.manage.program.ProgramsUI} object.
558      */
559     public ProgramsUI getProgramsUI() {
560         return getUI().getParentContainer(ProgramsUI.class);
561     }
562 
563     /**
564      * {@inheritDoc}
565      */
566     @Override
567     public SwingValidator<ProgramsTableUIModel> getValidator() {
568         return getUI().getValidator();
569     }
570 
571     private class StrategyAndLocationLoader extends SwingWorker<Object, Object> {
572 
573         private final ProgramsUI adminProgrammeUI = getProgramsUI();
574         private final ProgramsTableRowModel selectedProgram;
575 
576         public StrategyAndLocationLoader(ProgramsTableRowModel selectedProgram) {
577             this.selectedProgram = selectedProgram;
578         }
579 
580         @Override
581         protected Object doInBackground() {
582 
583             getContext().getProgramStrategyService().loadStrategiesAndLocations(selectedProgram);
584 
585             return null;
586         }
587 
588         @Override
589         protected void done() {
590             if (isCancelled()) {
591                 return;
592             }
593             try {
594                 get();
595             } catch (InterruptedException | ExecutionException e) {
596                 throw new ReefDbTechnicalException(e.getMessage(), e);
597             }
598 
599             // Update strategies table
600             adminProgrammeUI.getStrategiesTableUI().getHandler().load(selectedProgram);
601 
602             // Update applied strategies table
603             adminProgrammeUI.getLocationsTableUI().getHandler().loadMonitoringLocationsFromProgram(selectedProgram);
604 
605             // Suppression des PSFMs
606             adminProgrammeUI.getPmfmsTableUI().getHandler().clearTable();
607 
608             // revalidate program
609             recomputeRowValidState(selectedProgram);
610             adminProgrammeUI.getHandler().getValidator().doValidate();
611 
612         }
613     }
614 }