View Javadoc
1   package fr.ifremer.dali.ui.swing.content.manage.rule.controlrule;
2   
3   /*
4    * #%L
5    * Dali :: 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 com.google.common.collect.Lists;
27  import com.google.common.collect.Maps;
28  import com.google.common.collect.Sets;
29  import fr.ifremer.dali.dto.DaliBeans;
30  import fr.ifremer.dali.dto.configuration.control.ControlFeatureDTO;
31  import fr.ifremer.dali.dto.configuration.control.ControlRuleDTO;
32  import fr.ifremer.dali.dto.configuration.control.RulePmfmDTO;
33  import fr.ifremer.dali.dto.enums.ControlElementValues;
34  import fr.ifremer.dali.dto.enums.ControlFeatureMeasurementValues;
35  import fr.ifremer.dali.dto.enums.ControlFunctionValues;
36  import fr.ifremer.dali.dto.referential.PersonDTO;
37  import fr.ifremer.dali.dto.referential.pmfm.PmfmDTO;
38  import fr.ifremer.dali.dto.referential.pmfm.QualitativeValueDTO;
39  import fr.ifremer.dali.service.StatusFilter;
40  import fr.ifremer.dali.ui.swing.content.manage.filter.select.SelectFilterUI;
41  import fr.ifremer.dali.ui.swing.content.manage.referential.pmfm.qualitativevalue.SelectQualitativeValueUI;
42  import fr.ifremer.dali.ui.swing.content.manage.rule.RulesUI;
43  import fr.ifremer.dali.ui.swing.content.manage.rule.controlrule.precondition.RulePreconditionUI;
44  import fr.ifremer.dali.ui.swing.content.manage.rule.controlrule.precondition.RulePreconditionUIModel;
45  import fr.ifremer.dali.ui.swing.content.manage.rule.rulelist.RuleListRowModel;
46  import fr.ifremer.dali.ui.swing.util.AbstractDaliBeanUIModel;
47  import fr.ifremer.dali.ui.swing.util.DaliUI;
48  import fr.ifremer.dali.ui.swing.util.table.AbstractDaliTableModel;
49  import fr.ifremer.dali.ui.swing.util.table.AbstractDaliTableUIHandler;
50  import fr.ifremer.quadrige3.core.dao.system.filter.FilterTypeId;
51  import fr.ifremer.quadrige3.core.dao.technical.decorator.DecoratorComparator;
52  import fr.ifremer.quadrige3.ui.swing.table.AbstractRowUIModel;
53  import fr.ifremer.quadrige3.ui.swing.table.SwingTable;
54  import fr.ifremer.quadrige3.ui.swing.table.editor.ButtonCellEditor;
55  import fr.ifremer.quadrige3.ui.swing.table.editor.FilterableComboBoxCellEditor;
56  import fr.ifremer.quadrige3.ui.swing.table.editor.PredicatedCellEditor;
57  import fr.ifremer.quadrige3.ui.swing.table.renderer.ButtonCellRenderer;
58  import fr.ifremer.quadrige3.ui.swing.table.renderer.MultipleCellRenderer;
59  import fr.ifremer.quadrige3.ui.swing.table.renderer.PredicatedCellRenderer;
60  import jaxx.runtime.SwingUtil;
61  import org.apache.commons.collections4.CollectionUtils;
62  import org.apache.commons.lang3.StringUtils;
63  import org.apache.commons.logging.Log;
64  import org.apache.commons.logging.LogFactory;
65  import org.jdesktop.swingx.table.TableColumnExt;
66  import org.springframework.dao.DataRetrievalFailureException;
67  
68  import javax.swing.JOptionPane;
69  import javax.swing.JTable;
70  import javax.swing.table.TableCellEditor;
71  import javax.swing.table.TableCellRenderer;
72  import java.awt.Dimension;
73  import java.util.*;
74  import java.util.function.Predicate;
75  
76  import static org.nuiton.i18n.I18n.t;
77  
78  /**
79   * Controller pour le tableau des regles
80   */
81  public class ControlRuleTableUIHandler extends
82          AbstractDaliTableUIHandler<ControlRuleRowModel, ControlRuleTableUIModel, ControlRuleTableUI> {
83  
84      /**
85       * Logger.
86       */
87      private static final Log LOG = LogFactory.getLog(ControlRuleTableUIHandler.class);
88  
89      private FilterableComboBoxCellEditor<ControlFeatureDTO> controlFeatureEditor;
90  
91      /**
92       * {@inheritDoc}
93       */
94      @Override
95      public void beforeInit(final ControlRuleTableUI ui) {
96          super.beforeInit(ui);
97  
98          // create model and register to the JAXX context
99          final ControlRuleTableUIModel model = new ControlRuleTableUIModel();
100         ui.setContextValue(model);
101     }
102 
103     /**
104      * {@inheritDoc}
105      */
106     @Override
107     public void afterInit(final ControlRuleTableUI ui) {
108 
109         // Initialisation de l ecran
110         initUI(ui);
111 
112         // Desactivation des boutons
113         getUI().getRemoveControlRuleButton().setEnabled(false);
114 
115         // Initialisation du tableau
116         initTable();
117 
118         // Initialisation des listeners
119         initListeners();
120 
121     }
122 
123     /**
124      * Initialisation du tableau.
125      */
126     private void initTable() {
127 
128         // Le tableau
129         final SwingTable table = getTable();
130 
131         // Code
132         TableColumnExt codeCol = addColumn(ControlRuleTableModel.CODE);
133         codeCol.setSortable(true);
134         codeCol.setEditable(false);
135 
136         // Fonction
137         TableColumnExt functionCol = addFilterableComboDataColumnToModel(
138                 ControlRuleTableModel.FUNCTION,
139                 getContext().getSystemService().getFunctionsControlSystem(),
140                 false);
141         functionCol.setSortable(true);
142         functionCol.setPreferredWidth(200);
143 
144         // Element Controle
145         TableColumnExt controlElementCol = addFilterableComboDataColumnToModel(
146                 ControlRuleTableModel.CONTROL_ELEMENT,
147                 getContext().getSystemService().getControlElements(),
148                 false);
149         controlElementCol.setSortable(true);
150         controlElementCol.setPreferredWidth(200);
151 
152         // Caracteristique Controle
153         controlFeatureEditor = newFilterableComboBoxCellEditor(new ArrayList<>(), ControlFeatureDTO.class, false);
154         TableColumnExt controleFeatureCol = addColumn(
155                 controlFeatureEditor,
156                 newTableCellRender(ControlFeatureDTO.class),
157                 ControlRuleTableModel.CONTROL_FEATURE);
158         controleFeatureCol.setSortable(true);
159         controleFeatureCol.setPreferredWidth(200);
160 
161         // Actif
162         final TableColumnExt activeCol = addBooleanColumnToModel(ControlRuleTableModel.ACTIVE, table);
163         activeCol.setSortable(true);
164 
165         // Bloquante
166         final TableColumnExt blockingCol = addBooleanColumnToModel(ControlRuleTableModel.BLOCKING, table);
167         blockingCol.setSortable(true);
168 
169         // Min
170         TableColumnExt minColumn = addColumn(ControlRuleTableModel.MIN);
171         minColumn.setSortable(true);
172 
173         // Max
174         TableColumnExt maxColumn = addColumn(ControlRuleTableModel.MAX);
175         maxColumn.setSortable(true);
176 
177         // specific renderer for min & max columns
178         Map<Class, TableCellRenderer> minMaxRenderers = Maps.newHashMap();
179         minMaxRenderers.put(Double.class, newNumberCellRenderer(10));
180         minMaxRenderers.put(Date.class, newDateCellRenderer(getConfig().getDateFormat()));
181         MultipleCellRenderer smartCellRenderer = new MultipleCellRenderer(minMaxRenderers);
182         minColumn.setCellRenderer(smartCellRenderer);
183         maxColumn.setCellRenderer(smartCellRenderer);
184 
185         // specific editors for min & max columns
186         Map<Predicate<ControlRuleRowModel>, TableCellEditor> minMaxEditors = Maps.newHashMap();
187         minMaxEditors.put(controlRule -> ControlFunctionValues.MIN_MAX_DATE.equals(controlRule.getFunction()), newDateCellEditor(getConfig().getDateFormat()));
188         minMaxEditors.put(controlRule -> ControlFunctionValues.MIN_MAX.equals(controlRule.getFunction()), newNumberCellEditor(Double.class, true, DaliUI.SIGNED_HIGH_DECIMAL_DIGITS_PATTERN));
189         PredicatedCellEditor<ControlRuleRowModel> minMaxCellEditor = new PredicatedCellEditor<>(minMaxEditors);
190         minColumn.setCellEditor(minMaxCellEditor);
191         maxColumn.setCellEditor(minMaxCellEditor);
192         // TODO maybe choose Integer or Double (with precision) depending on witch feature is controlled
193 
194         // Valeurs Autorisees
195         TableColumnExt allowedValuesCol = addColumn(ControlRuleTableModel.ALLOWED_VALUES);
196         allowedValuesCol.setSortable(true);
197 
198         allowedValuesCol.setCellEditor(new PredicatedCellEditor<>(buildAllowedValuesEditors()));
199 
200         // the same predicate for renderers
201         allowedValuesCol.setCellRenderer(new PredicatedCellRenderer<>(buildAllowedValuesRenderers()));
202 
203         ControlRuleTableModel tableModel = new ControlRuleTableModel(getTable().getColumnModel());
204         table.setModel(tableModel);
205 
206         // Initialisation du tableau
207         initTable(table);
208 
209     }
210 
211     private Map<Predicate<ControlRuleRowModel>, TableCellEditor> buildAllowedValuesEditors() {
212         Map<Predicate<ControlRuleRowModel>, TableCellEditor> allowedValuesEditors = Maps.newHashMap();
213 
214         // specific editor for allowed values if controlled attribute is a qualitative value
215         allowedValuesEditors.put(ControlRuleRowModel.qualitativeValuePredicate, new ButtonCellEditor() {
216 
217             private String allowedValues;
218 
219             @Override
220             public void onButtonCellAction(int row, int column) {
221                 int rowModelIndex = getTable().convertRowIndexToModel(row);
222                 ControlRuleRowModel rowModel = getTableModel().getEntry(rowModelIndex);
223                 allowedValues = rowModel.getAllowedValues();
224 
225                 if (CollectionUtils.isNotEmpty(rowModel.getRulePmfms())) {
226 
227                     // build available qualitative values
228                     Set<QualitativeValueDTO> availableList = Sets.newHashSet();
229                     for (RulePmfmDTO rulePmfm : rowModel.getRulePmfms()) {
230                         // the PmfmDTO list is not from referential but from RulePmfm, so we need to get the real pmfm
231                         List<PmfmDTO> refPmfms = getContext().getReferentialService().searchPmfms(StatusFilter.ALL,
232                                 rulePmfm.getPmfm().getParameter().getCode(), // only parameter is mandatory
233                                 rulePmfm.getPmfm().getMatrix() != null ? rulePmfm.getPmfm().getMatrix().getId() : null,
234                                 rulePmfm.getPmfm().getFraction() != null ? rulePmfm.getPmfm().getFraction().getId() : null,
235                                 rulePmfm.getPmfm().getMethod() != null ? rulePmfm.getPmfm().getMethod().getId() : null,
236                                 rulePmfm.getPmfm().getUnit() != null ? rulePmfm.getPmfm().getUnit().getId() : null,
237                                 null,
238                                 null);
239                         // gather all qualitative values
240                         for (PmfmDTO refPmfm : refPmfms) {
241                             availableList.addAll(refPmfm.getQualitativeValues());
242                         }
243                     }
244                     // build selected
245                     List<QualitativeValueDTO> selectedList = Lists.newArrayList();
246                     if (StringUtils.isNotBlank(allowedValues)) {
247                         Set<Integer> qvIds = DaliBeans.getIntegerSetFromString(allowedValues, getConfig().getValueSeparator());
248                         selectedList.addAll(getContext().getReferentialService().getQualitativeValues(qvIds));
249                     }
250 
251                     // open select qualitative values ui
252                     SelectQualitativeValueUI selectQualitativeValueUI = new SelectQualitativeValueUI(getContext());
253                     List<QualitativeValueDTO> orderedAvailableList = new ArrayList<>(availableList);
254                     orderedAvailableList.sort(new DecoratorComparator<>(getDecorator(QualitativeValueDTO.class, null)));
255                     selectQualitativeValueUI.getModel().setAvailableList(orderedAvailableList);
256                     selectQualitativeValueUI.getModel().setSelectedList(selectedList);
257                     selectQualitativeValueUI.getHandler().setEnabled(getRuleList().isEditable());
258                     openDialog(selectQualitativeValueUI, new Dimension(640, 480));
259 
260                     if (getRuleList().isEditable() && selectQualitativeValueUI.getModel().isValid()) {
261                         // if user validates, build allowed values from qualitative value ids
262                         allowedValues = DaliBeans.joinIds(selectQualitativeValueUI.getModel().getSelectedList(), getConfig().getValueSeparator());
263                     }
264                     getTable().editingStopped(null);
265                 }
266             }
267 
268             @Override
269             public Object getCellEditorValue() {
270                 return allowedValues;
271             }
272         });
273 
274         // specific editor for allowed values if controlled attribute is a person
275         allowedValuesEditors.put(ControlRuleRowModel.observersPredicate, new ButtonCellEditor() {
276 
277             private String allowedValues;
278 
279             @Override
280             public void onButtonCellAction(int row, int column) {
281                 int rowModelIndex = getTable().convertRowIndexToModel(row);
282                 ControlRuleRowModel rowModel = getTableModel().getEntry(rowModelIndex);
283                 allowedValues = rowModel.getAllowedValues();
284 
285                 SelectFilterUI userUI = new SelectFilterUI(getContext(), FilterTypeId.QUSER.getValue());
286                 List<PersonDTO> selectedPersons = Lists.newArrayList();
287                 if (StringUtils.isNotBlank(allowedValues)) {
288                     Set<Integer> userIds = DaliBeans.getIntegerSetFromString(allowedValues, getConfig().getValueSeparator());
289                     for (Integer userId : userIds) {
290                         try {
291                             selectedPersons.add(getContext().getUserService().getUser(userId));
292                         } catch (DataRetrievalFailureException ignored) {
293                         }
294                     }
295                 }
296                 userUI.getModel().setSelectedElements(selectedPersons);
297                 userUI.getHandler().setEnabled(getRuleList().isEditable());
298 
299                 openDialog(userUI);
300 
301                 if (getRuleList().isEditable() && userUI.getModel().isValid()) {
302                     // if user validates, build allowed values from qualitative value ids
303                     allowedValues = DaliBeans.joinIds(userUI.getModel().getSelectedElements(), getConfig().getValueSeparator());
304                 }
305                 getTable().editingStopped(null);
306             }
307 
308             @Override
309             public Object getCellEditorValue() {
310                 return allowedValues;
311             }
312         });
313 
314         // specific editor for preconditions
315         allowedValuesEditors.put(ControlRuleRowModel.preconditionPredicate, new ButtonCellEditor() {
316             @Override
317             public void onButtonCellAction(int row, int column) {
318                 int rowModelIndex = getTable().convertRowIndexToModel(row);
319                 ControlRuleRowModel rowModel = getTableModel().getEntry(rowModelIndex);
320 
321                 if (rowModel.sizeRulePmfms() != 2) return;
322 
323                 RulePreconditionUI rulePreconditionUI = new RulePreconditionUI(getContext());
324                 RulePreconditionUIModel model = rulePreconditionUI.getModel();
325                 model.setQvMap(getContext().getRuleListService().buildQualitativeValueMapFromPreconditions(rowModel.getPreconditions()));
326                 model.setBasePmfm(rowModel.getRulePmfms(0).getPmfm());
327                 model.setUsedPmfm(rowModel.getRulePmfms(1).getPmfm());
328                 rulePreconditionUI.getHandler().setEnabled(getRuleList().isEditable());
329                 openDialog(rulePreconditionUI, new Dimension(600, 800));
330 
331                 if (getRuleList().isEditable() && model.isModify() && model.isValid()) {
332                     getContext().getRuleListService().buildPreconditionsFromQualitativeValueMap(rowModel, model.getQvMap());
333                     getModel().setModify(true);
334                 }
335 
336             }
337         });
338 
339         return allowedValuesEditors;
340     }
341 
342     private Map<Predicate<ControlRuleRowModel>, TableCellRenderer> buildAllowedValuesRenderers() {
343 
344         Map<Predicate<ControlRuleRowModel>, TableCellRenderer> allowedValuesRenderers = Maps.newHashMap();
345 
346         // renderer for qualitative values
347         allowedValuesRenderers.put(ControlRuleRowModel.qualitativeValuePredicate, new AllowedValuesCellRenderer("qualitative-value"));
348 
349         // renderer for users
350         allowedValuesRenderers.put(ControlRuleRowModel.observersPredicate, new AllowedValuesCellRenderer("person"));
351 
352         // renderer for preconditions
353         allowedValuesRenderers.put(ControlRuleRowModel.preconditionPredicate, new ButtonCellRenderer(SwingUtil.createActionIcon("associated-qualitative-value"), true));
354 
355         return allowedValuesRenderers;
356     }
357 
358     private void initListeners() {
359 
360         // Table listener
361         getModel().addPropertyChangeListener(ControlRuleTableUIModel.PROPERTY_SINGLE_ROW_SELECTED, evt -> {
362 
363             // Si un seul element a ete selectionne
364             final ControlRuleRowModel controlRule = getModel().getSingleSelectedRow();
365 
366             configureColumns(controlRule);
367 
368             boolean editable = controlRule != null && controlRule.isEditable();
369 
370             // Chargement des PSFMControle
371             loadPmfmTable(controlRule, editable);
372 
373             // Chargement des commentaires/messages affiches
374             loadControlRuleInformation(controlRule, editable);
375         });
376     }
377 
378     /**
379      * {@inheritDoc}
380      */
381     @Override
382     protected String[] getRowPropertiesToIgnore() {
383         return new String[]{ControlRuleRowModel.PROPERTY_ERRORS, ControlRuleRowModel.PROPERTY_PMFM_VALID};
384     }
385 
386     /**
387      * {@inheritDoc}
388      */
389     @Override
390     protected void onRowModified(int rowIndex, ControlRuleRowModel row, String propertyName, Integer propertyIndex, Object oldValue, Object newValue) {
391 
392         if (ControlRuleRowModel.PROPERTY_FUNCTION.equals(propertyName) || ControlRuleRowModel.PROPERTY_CONTROL_ELEMENT.equals(propertyName)) {
393             configureColumns(row);
394         }
395 
396         if (ControlRuleRowModel.PROPERTY_FUNCTION.equals(propertyName) && row.getFunction() != null) {
397             // reset min max or allowed values
398             if (!ControlFunctionValues.MIN_MAX.equals(row.getFunction()) && !ControlFunctionValues.MIN_MAX_DATE.equals(row.getFunction())) {
399                 row.setMin(null);
400                 row.setMax(null);
401             }
402             if (!ControlFunctionValues.IS_AMONG.equals(row.getFunction())) {
403                 row.setAllowedValues(null);
404             }
405             // reset preconditions
406             if (!DaliBeans.isPreconditionRule(row)) {
407                 row.setPreconditions(null);
408             }
409             // reset rulePmfm ids (Mantis #45625)
410             resetRulePmfmIds(row);
411 
412         }
413 
414         if (ControlRuleRowModel.PROPERTY_CONTROL_ELEMENT.equals(propertyName)) {
415             row.setControlFeature(null);
416         }
417 
418         if (ControlRuleRowModel.PROPERTY_CONTROL_FEATURE.equals(propertyName)) {
419 
420             // reset pmfm list if not mandatory
421             if (!DaliBeans.isPmfmMandatory(row) && !DaliBeans.isPreconditionRule(row)) {
422                 row.setRulePmfms(null);
423             }
424 
425             // reload pmfms
426             loadPmfmTable(row, row.isEditable());
427         }
428 
429         if (ControlRuleRowModel.PROPERTY_RULE_PMFMS.equals(propertyName) && ControlFunctionValues.PRECONDITION.equals(row.getFunction())) {
430             // Clear preconditions when the pmfms are not valid (Mantis #42773)
431             if (CollectionUtils.size(newValue) < 2) {
432                 row.getPreconditions().clear();
433             }
434         }
435 
436         super.onRowModified(rowIndex, row, propertyName, propertyIndex, oldValue, newValue);
437         saveToParentModel();
438     }
439 
440     private void resetRulePmfmIds(ControlRuleRowModel row) {
441         if (row == null) return;
442         row.getRulePmfms().forEach(rulePmfm -> rulePmfm.setId(null));
443     }
444 
445     private void loadPmfmTable(ControlRuleDTO controlRule, boolean editable) {
446         getRulesUI().getControlPmfmTableUI().getHandler().loadPmfms(controlRule, getTable().isEditable() && editable);
447     }
448 
449     private void saveToParentModel() {
450         getRuleList().setControlRules(getModel().getBeans());
451         recomputeRowsValidState(false);
452 
453         getModel().firePropertyChanged(AbstractDaliBeanUIModel.PROPERTY_MODIFY, null, true);
454 
455     }
456 
457     /**
458      * {@inheritDoc}
459      */
460     @Override
461     protected void onRowsAdded(List<ControlRuleRowModel> addedRows) {
462         super.onRowsAdded(addedRows);
463 
464         if (addedRows.size() == 1) {
465             ControlRuleRowModel row = addedRows.get(0);
466 
467             // create a new rule by service
468             ControlRuleDTO newRule = getContext().getRuleListService().newControlRule(getRuleList());
469             // convert it to the added row
470             row.fromBean(newRule);
471 
472             getModel().setModify(true);
473             setFocusOnCell(row);
474         }
475     }
476 
477     private RuleListRowModel getRuleList() {
478         return getModel().getParentModel().getRuleListUIModel().getSingleSelectedRow();
479     }
480 
481     /**
482      * <p>removeRules.</p>
483      */
484     public void removeRules() {
485         if (getContext().getDialogHelper().showConfirmDialog(
486                 t("dali.rule.controlRule.delete.message"),
487                 JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) {
488 
489             getModel().deleteSelectedRows();
490             saveToParentModel();
491         }
492     }
493 
494     /**
495      * {@inheritDoc}
496      */
497     @Override
498     protected boolean isRowValid(ControlRuleRowModel row) {
499         return super.isRowValid(row) && isControlRuleValid(row);
500     }
501 
502     private boolean isControlRuleValid(ControlRuleRowModel row) {
503 
504         row.getErrors().clear();
505 
506         // check code length (rule_cd : 40 characters max)
507         if (StringUtils.length(row.getCode()) > 40) {
508             DaliBeans.addError(row, t("dali.rule.controlRule.code.tooLong", 40), ControlRuleRowModel.PROPERTY_CODE);
509         }
510 
511         // check mandatory PMFM
512         row.setPmfmValid(true);
513         if (DaliBeans.isPmfmMandatory(row)) {
514 
515             if (ControlFunctionValues.PRECONDITION.equals(row.getFunction())) {
516                 if (row.sizeRulePmfms() != 2 || (!(row.getRulePmfms(0).getPmfm().getParameter().isQualitative() && row.getRulePmfms(1).getPmfm().getParameter().isQualitative()))) {
517                     row.setPmfmValid(false);
518                     DaliBeans.addError(row, t("dali.rule.rulePrecondition.invalidQualitativePmfm"), ControlRuleRowModel.PROPERTY_CODE);
519                 }
520             } else {
521                 if (row.isRulePmfmsEmpty()) {
522                     row.setPmfmValid(false);
523                     DaliBeans.addError(row, t("dali.rule.controlRule.noPmfm"), ControlRuleRowModel.PROPERTY_CODE);
524                 }
525             }
526 
527         }
528 
529         // check min max
530         if (ControlFunctionValues.MIN_MAX.equals(row.getFunction())) {
531             Number min = (row.getMin() instanceof Number) ? (Number) row.getMin() : null;
532             Number max = (row.getMax() instanceof Number) ? (Number) row.getMax() : null;
533             if (min == null && max == null) {
534                 DaliBeans.addError(row, t("dali.rule.controlRule.minMaxEmpty"),
535                         ControlRuleRowModel.PROPERTY_MIN, ControlRuleRowModel.PROPERTY_MAX);
536             } else if (min != null && max != null && min.doubleValue() > max.doubleValue()) {
537                 DaliBeans.addError(row, t("dali.rule.controlRule.minGreaterThanMax"),
538                         ControlRuleRowModel.PROPERTY_MIN, ControlRuleRowModel.PROPERTY_MAX);
539             }
540         }
541 
542         // check min max date
543         if (ControlFunctionValues.MIN_MAX_DATE.equals(row.getFunction())) {
544             Date min = (row.getMin() instanceof Date) ? (Date) row.getMin() : null;
545             Date max = (row.getMax() instanceof Date) ? (Date) row.getMax() : null;
546             if (min == null && max == null) {
547                 DaliBeans.addError(row, t("dali.rule.controlRule.minMaxDateEmpty"),
548                         ControlRuleRowModel.PROPERTY_MIN, ControlRuleRowModel.PROPERTY_MAX);
549             } else if (min != null && max != null && min.after(max)) {
550                 DaliBeans.addError(row, t("dali.rule.controlRule.minDateGreaterThanMaxDate"),
551                         ControlRuleRowModel.PROPERTY_MIN, ControlRuleRowModel.PROPERTY_MAX);
552             }
553         }
554 
555         // check allowed values separator
556         if (ControlFunctionValues.IS_AMONG.equals(row.getFunction())) {
557             if (StringUtils.isNotBlank(row.getAllowedValues())) {
558                 if (row.getAllowedValues().contains(" ") && !row.getAllowedValues().contains(getConfig().getValueSeparator())) {
559                     // the value separator is not present in allowedValues
560                     DaliBeans.addError(row,
561                             t("dali.rule.controlRule.allowedValues.noSeparator", getConfig().getValueSeparator()),
562                             ControlRuleRowModel.PROPERTY_ALLOWED_VALUES);
563                 }
564             } else {
565                 // no allowed values
566                 DaliBeans.addWarning(row,
567                         t("dali.rule.controlRule.allowedValues.empty"),
568                         ControlRuleRowModel.PROPERTY_ALLOWED_VALUES);
569 
570             }
571         }
572 
573         // check preconditions validity to avoid save action to be performed with empty preconditions (Mantis #41964)
574         if (ControlFunctionValues.PRECONDITION.equals(row.getFunction()) && CollectionUtils.isEmpty(row.getPreconditions()) && row.isPmfmValid()) {
575             DaliBeans.addError(row,
576                     t("dali.rule.controlRule.preconditions.empty"),
577                     ControlRuleRowModel.PROPERTY_ALLOWED_VALUES);
578         }
579 
580         return DaliBeans.hasNoBlockingError(row);
581     }
582 
583     private void configureColumns(ControlRuleDTO rule) {
584         if (rule == null) {
585             return;
586         }
587 
588         // configure control feature combo
589         if (rule.getControlElement() != null) {
590             List<ControlFeatureDTO> controlFeatures = getContext().getSystemService().getControlFeatures(rule.getControlElement());
591             controlFeatureEditor.getCombo().setData(controlFeatures);
592         } else {
593             controlFeatureEditor.getCombo().setData(null);
594         }
595 
596         // special case for precondition
597         if (ControlFunctionValues.PRECONDITION.equals(rule.getFunction())) {
598             rule.setControlElement(ControlElementValues.MEASUREMENT.toControlElementDTO());
599             rule.setControlFeature(ControlFeatureMeasurementValues.QUALITATIVE_VALUE.toControlFeatureDTO());
600         }
601 
602         getTable().repaint();
603     }
604 
605     /**
606      * <p>loadControlRuleInformation.</p>
607      *
608      * @param controlRule a {@link fr.ifremer.dali.dto.configuration.control.ControlRuleDTO} object.
609      */
610     private void loadControlRuleInformation(final ControlRuleDTO controlRule, boolean editable) {
611 
612         final RulesUI rulesUI = getRulesUI();
613 
614 
615         if (controlRule != null) {
616             rulesUI.getRuleMessageEditor().setText(controlRule.getMessage());
617             rulesUI.getRuleDescriptionEditor().setText(controlRule.getDescription());
618             rulesUI.getRuleMessageEditor().setEnabled(getTable().isEditable() && editable);
619             rulesUI.getRuleDescriptionEditor().setEnabled(getTable().isEditable() && editable);
620         } else {
621             rulesUI.getRuleMessageEditor().setText(null);
622             rulesUI.getRuleDescriptionEditor().setText(null);
623             rulesUI.getRuleMessageEditor().setEnabled(false);
624             rulesUI.getRuleDescriptionEditor().setEnabled(false);
625         }
626     }
627 
628     /**
629      * <p>clearControlRuleInformation.</p>
630      */
631     public void clearControlRuleInformation() {
632 
633         final RulesUI rulesUI = getRulesUI();
634 
635         rulesUI.getRuleMessageEditor().setEnabled(false);
636         rulesUI.getRuleDescriptionEditor().setEnabled(false);
637 
638         rulesUI.getRuleMessageEditor().setText(null);
639         rulesUI.getRuleDescriptionEditor().setText(null);
640     }
641 
642     /**
643      * <p>loadReglesControle.</p>
644      *
645      * @param controlRules a {@link Collection} object.
646      * @param readOnly     read only ?
647      */
648     public void loadControlRules(final Collection<ControlRuleDTO> controlRules, boolean readOnly) {
649 
650         // Stop table edition to avoid rendering exception (Mantis #49268)
651         getTable().editingStopped(null);
652 
653         // Load control rules
654         getModel().setBeans(controlRules);
655         getModel().getRows().forEach(controlRuleRowModel -> controlRuleRowModel.setEditable(!readOnly));
656 
657         // Activate add button
658         getUI().getAddControlRuleButton().setEnabled(!readOnly);
659 
660         recomputeRowsValidState(false);
661     }
662 
663     public boolean canDeleteSelectedRows() {
664         return getModel().getSelectedRows().stream().allMatch(AbstractRowUIModel::isEditable);
665     }
666 
667     /**
668      * <p>supprimerReglesControle.</p>
669      */
670     public void clearTable() {
671 
672         getModel().setBeans(null);
673 
674         getUI().getAddControlRuleButton().setEnabled(false);
675     }
676 
677     /**
678      * <p>editRuleListCode.</p>
679      */
680     public void editRuleCode() {
681         ControlRuleRowModel row = getModel().getSingleSelectedRow();
682         if (row != null && row.isNewCode()) {
683             if (checkNewCode(row)) {
684                 getRuleList().setDirty(true);
685             }
686         }
687     }
688 
689     private boolean checkNewCode(ControlRuleRowModel row) {
690 
691         boolean edit = StringUtils.isNotBlank(row.getCode());
692 
693         // ask for new code
694         String newCode = (String) getContext().getDialogHelper().showInputDialog(
695                 getUI(),
696                 t("dali.rule.controlRule.setCode"),
697                 edit ? t("dali.rule.controlRule.editCode.title") : t("dali.rule.controlRule.setCode.title"),
698                 null,
699                 row.getCode());
700 
701         if (checkCodeDuplicates(newCode, row, edit)) {
702             row.setCode(newCode);
703             return true;
704         }
705 
706         return false;
707     }
708 
709     /**
710      * <p>checkCodeDuplicates.</p>
711      *
712      * @param newCode    a {@link java.lang.String} object.
713      * @param currentRow a {@link ControlRuleRowModel} object.
714      * @param edit       a boolean.
715      * @return a boolean.
716      */
717     private boolean checkCodeDuplicates(String newCode, ControlRuleRowModel currentRow, boolean edit) {
718 
719         if (StringUtils.isBlank(newCode)) {
720             return false;
721         }
722 
723         newCode = newCode.trim();
724 
725         // check if new code is already in UI
726         for (ControlRuleRowModel otherRow : getModel().getRows()) {
727             if (currentRow == otherRow) continue;
728             if (newCode.equalsIgnoreCase(otherRow.getCode())) {
729                 getContext().getDialogHelper().showErrorDialog(
730                         t("dali.error.alreadyExists.referential", t("dali.rule.controlRule.title"), newCode, t("dali.property.referential.national")),
731                         edit ? t("dali.rule.controlRule.editCode.title") : t("dali.rule.controlRule.setCode.title")
732                 );
733                 return false;
734             }
735         }
736 
737         // check if new code is already in bdd
738         if (getContext().getRuleListService().ruleCodeExists(newCode)) {
739             getContext().getDialogHelper().showErrorDialog(
740                     t("dali.error.alreadyExists.referential", t("dali.rule.controlRule.title"), newCode, t("dali.property.referential.national")),
741                     edit ? t("dali.rule.controlRule.editCode.title") : t("dali.rule.controlRule.setCode.title")
742             );
743             return false;
744         }
745 
746         return true;
747     }
748 
749     /**
750      * {@inheritDoc}
751      */
752     @Override
753     public AbstractDaliTableModel<ControlRuleRowModel> getTableModel() {
754         return (ControlRuleTableModel) getTable().getModel();
755     }
756 
757     /**
758      * {@inheritDoc}
759      */
760     @Override
761     public SwingTable getTable() {
762         return ui.getControlRuleTable();
763     }
764 
765     /**
766      * <p>getRulesUI.</p>
767      *
768      * @return a {@link fr.ifremer.dali.ui.swing.content.manage.rule.RulesUI} object.
769      */
770     private RulesUI getRulesUI() {
771         return getUI().getParentContainer(RulesUI.class);
772     }
773 
774     private class AllowedValuesCellRenderer extends ButtonCellRenderer<String> {
775 
776         AllowedValuesCellRenderer(String actionIconName) {
777             super(SwingUtil.createActionIcon(actionIconName), false);
778         }
779 
780         @Override
781         protected String getText(JTable table, String value, int row, int column) {
782             return value == null ? "0" : String.valueOf(new StringTokenizer(value, getConfig().getValueSeparator()).countTokens());
783         }
784     }
785 }