View Javadoc
1   package fr.ifremer.tutti.ui.swing.content.protocol;
2   
3   /*
4    * #%L
5    * Tutti :: UI
6    * %%
7    * Copyright (C) 2012 - 2014 Ifremer
8    * %%
9    * This program is free software: you can redistribute it and/or modify
10   * it under the terms of the GNU General Public License as
11   * published by the Free Software Foundation, either version 3 of the 
12   * License, or (at your option) any later version.
13   * 
14   * This program is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   * GNU General Public License for more details.
18   * 
19   * You should have received a copy of the GNU General Public 
20   * License along with this program.  If not, see
21   * <http://www.gnu.org/licenses/gpl-3.0.html>.
22   * #L%
23   */
24  
25  import com.google.common.base.Preconditions;
26  import com.google.common.base.Predicate;
27  import com.google.common.collect.HashMultimap;
28  import com.google.common.collect.ImmutableMap;
29  import com.google.common.collect.Lists;
30  import com.google.common.collect.Maps;
31  import com.google.common.collect.Multimap;
32  import com.google.common.collect.Sets;
33  import fr.ifremer.tutti.persistence.entities.TuttiEntities;
34  import fr.ifremer.tutti.persistence.entities.data.FishingOperation;
35  import fr.ifremer.tutti.persistence.entities.data.FishingOperations;
36  import fr.ifremer.tutti.persistence.entities.data.SampleCategoryModel;
37  import fr.ifremer.tutti.persistence.entities.data.SampleCategoryModelEntry;
38  import fr.ifremer.tutti.persistence.entities.protocol.CalcifiedPiecesSamplingDefinition;
39  import fr.ifremer.tutti.persistence.entities.protocol.CaracteristicMappingRow;
40  import fr.ifremer.tutti.persistence.entities.protocol.CaracteristicType;
41  import fr.ifremer.tutti.persistence.entities.protocol.MaturityCaracteristic;
42  import fr.ifremer.tutti.persistence.entities.protocol.OperationFieldMappingRow;
43  import fr.ifremer.tutti.persistence.entities.protocol.SpeciesProtocol;
44  import fr.ifremer.tutti.persistence.entities.protocol.Strata;
45  import fr.ifremer.tutti.persistence.entities.protocol.SubStrata;
46  import fr.ifremer.tutti.persistence.entities.protocol.TuttiProtocol;
47  import fr.ifremer.tutti.persistence.entities.protocol.TuttiProtocols;
48  import fr.ifremer.tutti.persistence.entities.referential.Caracteristic;
49  import fr.ifremer.tutti.persistence.entities.referential.CaracteristicQualitativeValue;
50  import fr.ifremer.tutti.persistence.entities.referential.Species;
51  import fr.ifremer.tutti.persistence.entities.referential.Speciess;
52  import fr.ifremer.tutti.persistence.entities.referential.TuttiLocation;
53  import fr.ifremer.tutti.service.DecoratorService;
54  import fr.ifremer.tutti.service.TuttiDecorator;
55  import fr.ifremer.tutti.ui.swing.content.home.actions.CloneProtocolAction;
56  import fr.ifremer.tutti.ui.swing.content.home.actions.EditProtocolAction;
57  import fr.ifremer.tutti.ui.swing.content.home.actions.ImportProtocolAction;
58  import fr.ifremer.tutti.ui.swing.content.operation.catches.species.edit.SpeciesBatchRowHelper;
59  import fr.ifremer.tutti.ui.swing.content.protocol.calcifiedpiecessampling.CalcifiedPiecesSamplingEditorRowModel;
60  import fr.ifremer.tutti.ui.swing.content.protocol.calcifiedpiecessampling.CalcifiedPiecesSamplingEditorTableModel;
61  import fr.ifremer.tutti.ui.swing.content.protocol.calcifiedpiecessampling.CalcifiedPiecesSamplingEditorUI;
62  import fr.ifremer.tutti.ui.swing.content.protocol.rtp.RtpCellEditor;
63  import fr.ifremer.tutti.ui.swing.content.protocol.rtp.RtpCellRenderer;
64  import fr.ifremer.tutti.ui.swing.content.protocol.zones.tree.AvailableStratasTreeModel;
65  import fr.ifremer.tutti.ui.swing.content.protocol.zones.tree.ZonesTreeModel;
66  import fr.ifremer.tutti.ui.swing.content.protocol.zones.tree.node.StrataNode;
67  import fr.ifremer.tutti.ui.swing.util.AbstractTuttiUIHandler;
68  import fr.ifremer.tutti.ui.swing.util.TuttiUIUtil;
69  import jaxx.runtime.SwingUtil;
70  import jaxx.runtime.swing.JAXXWidgetUtil;
71  import jaxx.runtime.swing.editor.bean.BeanDoubleList;
72  import jaxx.runtime.swing.editor.bean.BeanDoubleListModel;
73  import jaxx.runtime.swing.editor.bean.BeanFilterableComboBox;
74  import jaxx.runtime.swing.editor.bean.BeanUIUtil;
75  import jaxx.runtime.validator.swing.SwingValidator;
76  import org.apache.commons.collections4.CollectionUtils;
77  import org.apache.commons.lang3.StringUtils;
78  import org.apache.commons.logging.Log;
79  import org.apache.commons.logging.LogFactory;
80  import org.jdesktop.swingx.JXTable;
81  import org.jdesktop.swingx.autocomplete.ComboBoxCellEditor;
82  import org.jdesktop.swingx.autocomplete.ObjectToStringConverter;
83  import org.jdesktop.swingx.decorator.HighlightPredicate;
84  import org.jdesktop.swingx.decorator.Highlighter;
85  import org.jdesktop.swingx.table.DefaultTableColumnModelExt;
86  import org.jdesktop.swingx.table.TableColumnExt;
87  import org.nuiton.decorator.Decorator;
88  import org.nuiton.jaxx.application.swing.AbstractApplicationUIHandler;
89  import org.nuiton.jaxx.application.swing.table.ColumnIdentifier;
90  import org.nuiton.jaxx.application.swing.util.CloseableUI;
91  
92  import javax.swing.JComboBox;
93  import javax.swing.JComponent;
94  import javax.swing.JList;
95  import javax.swing.JMenuItem;
96  import javax.swing.JOptionPane;
97  import javax.swing.JTabbedPane;
98  import javax.swing.JTable;
99  import javax.swing.JTree;
100 import javax.swing.ListCellRenderer;
101 import javax.swing.ListSelectionModel;
102 import javax.swing.event.ListSelectionListener;
103 import javax.swing.event.TableModelEvent;
104 import javax.swing.event.TreeModelEvent;
105 import javax.swing.event.TreeModelListener;
106 import javax.swing.table.DefaultTableCellRenderer;
107 import javax.swing.table.JTableHeader;
108 import javax.swing.table.TableCellRenderer;
109 import javax.swing.table.TableColumnModel;
110 import java.awt.Color;
111 import java.awt.Component;
112 import java.beans.PropertyChangeEvent;
113 import java.beans.PropertyChangeListener;
114 import java.util.ArrayList;
115 import java.util.Collection;
116 import java.util.HashSet;
117 import java.util.LinkedHashSet;
118 import java.util.List;
119 import java.util.Map;
120 import java.util.Objects;
121 import java.util.Set;
122 import java.util.TreeSet;
123 import java.util.stream.Collectors;
124 
125 import static org.nuiton.i18n.I18n.n;
126 import static org.nuiton.i18n.I18n.t;
127 
128 
129 /**
130  * @author Tony Chemit - chemit@codelutin.com
131  * @since 0.3
132  */
133 public class EditProtocolUIHandler extends AbstractTuttiUIHandler<EditProtocolUIModel, EditProtocolUI> implements CloseableUI {
134 
135     /** Logger. */
136     private static final Log log = LogFactory.getLog(EditProtocolUIHandler.class);
137 
138     public static String getTitle(boolean exist) {
139 
140         String result;
141         if (exist) {
142             result = t("tutti.editProtocol.title.edit.protocol");
143         } else {
144             result = t("tutti.editProtocol.title.create.protocol");
145         }
146         return result;
147     }
148 
149 //    protected SelectSpeciesUI dialog;
150 
151     protected SampleCategoryModel sampleCategoryModel;
152 
153     protected List<BeanDoubleList<Caracteristic>> allDoubleLists;
154 
155     public JXTable getSpeciesTable() {
156         return ui.getSpeciesTable();
157     }
158 
159     public JXTable getBenthosTable() {
160         return ui.getBenthosTable();
161     }
162 
163     public JXTable getCaracteristicsMappingTable() {
164         return ui.getCaracteristicsMappingTable();
165     }
166 
167     public JXTable getOperationFieldsMappingTable() {
168         return ui.getOperationFieldsMappingTable();
169     }
170 
171     public EditProtocolSpeciesTableModel getSpeciesTableModel() {
172         return (EditProtocolSpeciesTableModel) getSpeciesTable().getModel();
173     }
174 
175     public EditProtocolSpeciesTableModel getBenthosTableModel() {
176         return (EditProtocolSpeciesTableModel) getBenthosTable().getModel();
177     }
178 
179     public boolean isSpeciesSelected(Object selectedItem) {
180         return selectedItem != null && selectedItem instanceof Species;
181     }
182 
183     public EditProtocolCaracteristicsTableModel getCaracteristicMappingTableModel() {
184         return (EditProtocolCaracteristicsTableModel) getCaracteristicsMappingTable().getModel();
185     }
186 
187     public EditProtocolOperationFieldsTableModel getEditProtocolOperationFieldsMappingTableModel() {
188         return (EditProtocolOperationFieldsTableModel) getOperationFieldsMappingTable().getModel();
189     }
190 
191     //------------------------------------------------------------------------//
192     //-- AbstractTuttiUIHandler methods                                     --//
193     //------------------------------------------------------------------------//
194 
195     @Override
196     public SwingValidator<EditProtocolUIModel> getValidator() {
197         return ui.getValidator();
198     }
199 
200     private Multimap<TuttiLocation, TuttiLocation> programStratasAndSubstratas;
201 
202     @Override
203     public void beforeInit(EditProtocolUI ui) {
204         super.beforeInit(ui);
205 
206         this.sampleCategoryModel = getContext().getDataContext().getSampleCategoryModel();
207 
208         incrementsMessage("Chargement des réferentiels");
209 
210         getDataContext().resetValidationDataContext();
211 
212         EditProtocolUIModel model = new EditProtocolUIModel(sampleCategoryModel);
213 
214         // load cache data
215 
216         List<Species> allSpecies = new ArrayList<>(getDataContext().getSpecies());
217         model.setAllSpecies(allSpecies);
218 
219         Multimap<String, Species> allSpeciesByTaxonId = Speciess.splitByReferenceTaxonId(allSpecies);
220         model.setAllSpeciesByTaxonId(allSpeciesByTaxonId);
221 
222         List<Species> referentSpecies = getDataContext().getReferentSpecies();
223         Map<String, Species> allReferentSpeciesByTaxonId = Speciess.splitReferenceSpeciesByReferenceTaxonId(referentSpecies);
224         model.setAllReferentSpeciesByTaxonId(allReferentSpeciesByTaxonId);
225 
226         List<Caracteristic> caracteristics = new ArrayList<>(getDataContext().getCaracteristics());
227         model.setCaracteristics(caracteristics);
228 
229         Map<String, Caracteristic> allCaracteristic = TuttiEntities.splitById(caracteristics);
230         model.setAllCaracteristic(allCaracteristic);
231 
232         // can't load directly model from database here, since we want to
233         // fill only the model with rows (transformed from speciesProtocol)
234         // As we still don't have the table model at this point, wait the
235         // afterUI method to fill model
236 
237         listModelIsModify(model);
238 
239         TuttiLocation programZone = getDataContext().getProgram().getZone();
240 
241         programStratasAndSubstratas = HashMultimap.create(getPersistenceService().getAllFishingOperationStratasAndSubstratas(programZone.getId()));
242 
243         ImmutableMap.Builder<String, String> locationLabelCacheBuilder = ImmutableMap.builder();
244 
245         programStratasAndSubstratas.keySet().forEach(strata -> {
246 
247             locationLabelCacheBuilder.put(strata.getId(), strata.getLabel());
248             programStratasAndSubstratas.get(strata).stream().filter(Objects::nonNull)
249                                        .forEach(subStrata -> locationLabelCacheBuilder.put(subStrata.getId(), subStrata.getLabel()));
250 
251         });
252 
253         ImmutableMap<String, String> locationLabelCache = locationLabelCacheBuilder.build();
254         model.setLocationLabelCache(locationLabelCache);
255 
256         // Pour lancer la validation et le recalcul des caches de zones après l
257         model.addPropertyChangeListener(EditProtocolUIModel.PROPERTY_MODIFYING_ZONES, evt -> {
258 
259             boolean newValue = (boolean) evt.getNewValue();
260             if (!newValue) {
261 
262                 if (log.isInfoEnabled()) {
263                     log.info("Ask to release zone trees caches and revalidate protocol.");
264                 }
265 
266                 EditProtocolUIModel source = (EditProtocolUIModel) evt.getSource();
267                 boolean stratasAvailable = source.getAvailableStratasTreeModel().isAvailableStratas();
268                 source.setAvailableStratas(stratasAvailable);
269 
270                 // and revalidate model,
271                 getValidator().doValidate();
272             }
273         });
274 
275         this.ui.setContextValue(model);
276     }
277 
278     @Override
279     public void afterInit(EditProtocolUI ui) {
280 
281         initUI(this.ui);
282 
283         EditProtocolUIModel model = getModel();
284 
285         TuttiProtocol protocol;
286 
287         if (getContext().isProtocolFilled()) {
288 
289             // load existing protocol
290 
291             incrementsMessage("Chargement du protocole");
292 
293             protocol = getDataContext().getProtocol();
294 
295             if (EditProtocolAction.CLEAN_PROTOCOL_ENTRY.getContextValue(this.ui) != null) {
296 
297                 // clean protocol
298                 protocol = EditProtocolAction.CLEAN_PROTOCOL_ENTRY.getContextValue(this.ui);
299 
300                 EditProtocolAction.CLEAN_PROTOCOL_ENTRY.removeContextValue(this.ui);
301 
302                 model.setCleaned(true);
303                 this.ui.getSaveWarning().setText(t("tutti.editProtocol.warn.clean"));
304             }
305             model.fromEntity(protocol);
306 
307         } else if ((protocol = ImportProtocolAction.IMPORT_PROTOCOL_ENTRY.getContextValue(this.ui)) != null) {
308 
309             incrementsMessage("Import du protocole");
310 
311             // import protocol
312 
313             ImportProtocolAction.IMPORT_PROTOCOL_ENTRY.removeContextValue(this.ui);
314 
315             model.fromEntity(protocol);
316             model.setImported(true);
317             this.ui.getSaveWarning().setText(t("tutti.editProtocol.warn.import"));
318 
319         } else if ((protocol = CloneProtocolAction.CLONE_PROTOCOL_ENTRY.getContextValue(this.ui)) != null) {
320 
321             incrementsMessage("Clone du protocole");
322 
323             // clone protocol
324 
325             CloneProtocolAction.CLONE_PROTOCOL_ENTRY.removeContextValue(this.ui);
326 
327             model.fromEntity(protocol);
328             model.setCloned(true);
329             this.ui.getSaveWarning().setText(t("tutti.editProtocol.warn.clone"));
330 
331         } else {
332 
333             incrementsMessage("Création d'un nouveau protocol");
334 
335             // create new protocol
336 
337             if (log.isDebugEnabled()) {
338                 log.debug("Will create a new protocol");
339             }
340         }
341 
342         if (model.getLengthClassesPmfmId() == null) {
343             model.setLengthClassesPmfmId(new ArrayList<>());
344         }
345 
346         model.setProgramId(getDataContext().getProgramId());
347 
348         SwingValidator validator = this.ui.getValidator();
349         listenValidatorValid(validator, model);
350 
351         registerValidators(validator);
352 
353         Collection<Species> referents = model.getAllReferentSpeciesByTaxonId().values();
354 
355         initBeanFilterableComboBox(this.ui.getSpeciesComboBox(), new ArrayList<>(referents), null);
356         initBeanFilterableComboBox(this.ui.getBenthosComboBox(), new ArrayList<>(referents), null);
357         initBeanFilterableComboBox(ui.getCaracteristicMappingComboBox(), new ArrayList<>(model.getCaracteristics()), null);
358 
359         List<EditProtocolCaracteristicsRowModel> caracteristicMappingRows;
360         List<EditProtocolOperationFieldsRowModel> operationFieldMappingRows;
361         List<EditProtocolSpeciesRowModel> speciesRows;
362         List<EditProtocolSpeciesRowModel> benthosRows;
363 
364         model.addPropertyChangeListener(EditProtocolUIModel.PROPERTY_IMPORT_COLUMNS, evt -> populateImportColumnTableEditors());
365 
366         incrementsMessage("Préparation des interfaces graphiques");
367 
368         List<CalcifiedPiecesSamplingEditorRowModel> cpsRows = new ArrayList<>();
369 
370         // build protocolSpecies and benthos rows
371         if (protocol == null) {
372             caracteristicMappingRows = new ArrayList<>();
373             operationFieldMappingRows = new ArrayList<>();
374             speciesRows = new ArrayList<>();
375             benthosRows = new ArrayList<>();
376 
377         } else {
378 
379             caracteristicMappingRows = toProtocolCaracteristicRows(protocol.getCaracteristicMapping());
380             operationFieldMappingRows = toProtocolOperationFieldsRows(protocol.getOperationFieldMapping());
381 
382             speciesRows = toSpeciesRows(protocol.getSpecies(), cpsRows);
383             benthosRows = toSpeciesRows(protocol.getBenthos(), cpsRows);
384 
385             if (log.isDebugEnabled()) {
386                 log.debug("Will edit protocol with " +
387                                   speciesRows.size() + " protocolSpecies and " +
388                                   benthosRows.size() + " benthos declared.");
389             }
390         }
391 
392         // set to model (will propagate to tableModel)
393         // set a copy of the list to refresh the cps table
394         cpsRows.sort(CalcifiedPiecesSamplingEditorRowModel.COMPARATOR);
395         getModel().setCpsRows(cpsRows);
396 
397         model.setSpeciesRow(speciesRows);
398         model.setBenthosRow(benthosRows);
399         model.setCaracteristicMappingRows(caracteristicMappingRows);
400         model.setOperationFieldMappingRows(operationFieldMappingRows);
401 
402         this.ui.getSpeciesComboBox().reset();
403         this.ui.getBenthosComboBox().reset();
404         this.ui.getCaracteristicMappingComboBox().reset();
405 
406         {
407             // create protocolSpecies table model
408 
409             JXTable table = getSpeciesTable();
410 
411             DefaultTableColumnModelExt columnModel = new DefaultTableColumnModelExt();
412 
413             TableColumnExt speciesColumn = addColumnToModel(columnModel,
414                                                             null,
415                                                             newTableCellRender(Species.class),
416                                                             EditProtocolSpeciesTableModel.SPECIES_ID);
417             speciesColumn.setSortable(true);
418             DecoratorService.SpeciesDecorator speciesDecorator = new DecoratorService.SpeciesDecorator();
419             speciesColumn.putClientProperty(SpeciesBatchRowHelper.SPECIES_DECORATOR, speciesDecorator);
420             speciesColumn.setCellRenderer(newTableCellRender(speciesDecorator));
421 
422             TableColumnExt speciesSurveyCodeColumn = addColumnToModel(columnModel,
423                                                                       null,
424                                                                       null,
425                                                                       EditProtocolSpeciesTableModel.SURVEY_CODE_ID);
426             speciesSurveyCodeColumn.setSortable(true);
427 
428             addLengthClassesColumnToModel(columnModel, model.getLengthClassesPmfmId());
429             addMaturityColumnToModel(columnModel, model.getMaturityPmfmId());
430             addCpsPmfmColumnToModel(columnModel);
431 
432             addBooleanColumnToModel(columnModel, EditProtocolSpeciesTableModel.WEIGHT_ENABLED, table);
433 
434             for (SampleCategoryModelEntry sampleCategoryModelEntry : sampleCategoryModel.getCategory()) {
435 
436                 if (sampleCategoryModelEntry.getOrder() == 0) {
437                     // first category is not editable
438                     continue;
439                 }
440 
441                 MandatorySampleCategoryColumnIdentifier identifier = MandatorySampleCategoryColumnIdentifier.newId(
442                         EditProtocolSpeciesRowModel.PROPERTY_MANDATORY_SAMPLE_CATEGORY_ID,
443                         sampleCategoryModelEntry.getCategoryId(),
444                         sampleCategoryModelEntry.getLabel(),
445                         sampleCategoryModelEntry.getLabel()
446                 );
447 
448                 addBooleanColumnToModel(columnModel, identifier, table);
449             }
450 
451             addIndividualObservationColumnToModel(columnModel, table);
452 
453             addColumnToModel(columnModel,
454                              RtpCellEditor.newEditor(ui),
455                              new RtpCellRenderer(),
456                              EditProtocolSpeciesTableModel.USE_RTP);
457 
458             initTable(table,
459                       columnModel,
460                       speciesColumn,
461                       EditProtocolSpeciesTableModel.USE_RTP,
462                       speciesRows,
463                       e -> {
464                           ListSelectionModel source = (ListSelectionModel) e.getSource();
465                           getModel().setRemoveSpeciesEnabled(!source.isSelectionEmpty());
466                       });
467         }
468 
469         {
470             // create benthos table model
471 
472             JXTable table = getBenthosTable();
473 
474             DefaultTableColumnModelExt columnModel = new DefaultTableColumnModelExt();
475 
476             TableColumnExt speciesColumn = addColumnToModel(columnModel,
477                                                             null,
478                                                             newTableCellRender(Species.class),
479                                                             EditProtocolSpeciesTableModel.SPECIES_ID);
480             speciesColumn.setSortable(true);
481             DecoratorService.SpeciesDecorator speciesDecorator = new DecoratorService.SpeciesDecorator();
482             speciesColumn.putClientProperty(SpeciesBatchRowHelper.SPECIES_DECORATOR, speciesDecorator);
483             speciesColumn.setCellRenderer(newTableCellRender(speciesDecorator));
484 
485             TableColumnExt speciesSurveyCodeColumn = addColumnToModel(columnModel,
486                                                                       null,
487                                                                       null,
488                                                                       EditProtocolSpeciesTableModel.SURVEY_CODE_ID);
489             speciesSurveyCodeColumn.setSortable(true);
490 
491             addLengthClassesColumnToModel(columnModel, model.getLengthClassesPmfmId());
492             addMaturityColumnToModel(columnModel, model.getMaturityPmfmId());
493             addCpsPmfmColumnToModel(columnModel);
494 
495             addBooleanColumnToModel(columnModel, EditProtocolSpeciesTableModel.WEIGHT_ENABLED, table);
496 
497             for (SampleCategoryModelEntry sampleCategoryModelEntry : sampleCategoryModel.getCategory()) {
498 
499                 if (sampleCategoryModelEntry.getOrder() == 0) {
500                     // first category is not editable
501                     continue;
502                 }
503 
504                 MandatorySampleCategoryColumnIdentifier identifier = MandatorySampleCategoryColumnIdentifier.newId(
505                         EditProtocolSpeciesRowModel.PROPERTY_MANDATORY_SAMPLE_CATEGORY_ID,
506                         sampleCategoryModelEntry.getCategoryId(),
507                         sampleCategoryModelEntry.getLabel(),
508                         sampleCategoryModelEntry.getLabel()
509                 );
510 
511                 addBooleanColumnToModel(columnModel, identifier, table);
512             }
513 
514             addIndividualObservationColumnToModel(columnModel, table);
515 
516             addColumnToModel(columnModel,
517                              RtpCellEditor.newEditor(ui),
518                              new RtpCellRenderer(),
519                              EditProtocolSpeciesTableModel.USE_RTP);
520 
521             initTable(table,
522                       columnModel,
523                       speciesColumn,
524                       EditProtocolSpeciesTableModel.USE_RTP,
525                       benthosRows,
526                       e -> {
527                           ListSelectionModel source = (ListSelectionModel) e.getSource();
528                           getModel().setRemoveBenthosEnabled(!source.isSelectionEmpty());
529                       });
530         }
531 
532         BeanDoubleList<Caracteristic> maturityList = this.ui.getMaturityList();
533         model.getMaturityPmfmUsed().attachPredicateToUi(maturityList);
534 
535         BeanDoubleList<Caracteristic> lengthClassesList = this.ui.getLengthClassesList();
536         model.getLengthStepPmfmUsed().attachPredicateToUi(lengthClassesList);
537 
538         allDoubleLists = new ArrayList<>();
539         allDoubleLists.add(lengthClassesList);
540         allDoubleLists.add(ui.getIndividualObservationList());
541         allDoubleLists.add(maturityList);
542 
543         initDoubleList(EditProtocolUIModel.PROPERTY_LENGTH_CLASSES_PMFM_ID,
544                        lengthClassesList,
545                        Lists.newArrayList(model.getCaracteristics()),
546                        model.getLengthClassesPmfmId());
547 
548         JList<Caracteristic> lengthClassesSelectedList = lengthClassesList.getSelectedList();
549         ListCellRenderer<? super Caracteristic> lengthClassesDefaultRenderer = lengthClassesSelectedList.getCellRenderer();
550         lengthClassesSelectedList.setCellRenderer(new LengthStepCaracteristicCellRenderer(getConfig().getColorWarningRow(), model.getLengthStepPmfmUsed(), lengthClassesDefaultRenderer));
551 
552         initDoubleList(EditProtocolUIModel.PROPERTY_INDIVIDUAL_OBSERVATION_PMFM_ID,
553                        this.ui.getIndividualObservationList(),
554                        Lists.newArrayList(model.getCaracteristics()),
555                        model.getIndividualObservationPmfmId());
556 
557         initDoubleList(EditProtocolUIModel.PROPERTY_MATURITY_PMFM_ID,
558                        maturityList,
559                        model.getCaracteristics().stream().filter(caracteristic -> !caracteristic.isQualitativeValueEmpty()).collect(Collectors.toList()),
560                        model.getMaturityPmfmId());
561 
562         JList<Caracteristic> maturitySelectedList = maturityList.getSelectedList();
563         JMenuItem editMaturity = ui.getEditMaturityCaracteristicAction();
564         maturityList.getSelectedListPopup().add(editMaturity);
565 
566         // add listener to enable the maturity edition
567         maturitySelectedList.addListSelectionListener(e -> {
568             boolean editMaturityEnabled = maturitySelectedList.getSelectedIndices().length == 1;
569             editMaturity.setEnabled(editMaturityEnabled);
570         });
571 
572         ListCellRenderer<? super Caracteristic> maturityDefaultRenderer = maturitySelectedList.getCellRenderer();
573         maturitySelectedList.setCellRenderer(new MaturityCaracteristicCellRenderer(model.getMaturityPmfmUsed(), maturityDefaultRenderer));
574 
575 
576         // update lengthstep and maturity list button states when the user goes back to the caracteristic panel
577         // to update the remove state in case the selected maturity has been added or removed from the species or benthos
578         ui.getTabPanel().addChangeListener(e -> {
579             JTabbedPane source = (JTabbedPane) e.getSource();
580             if (ui.getCaracteristicPanel().equals(source.getSelectedComponent())) {
581                 lengthClassesList.getHandler().recomputeButtonStates();
582                 maturityList.getHandler().recomputeButtonStates();
583             }
584         });
585 
586         // init caracteristics mappingtable
587         {
588             JXTable caracteristicsMappingTable = getCaracteristicsMappingTable();
589 
590             DefaultTableColumnModelExt columnModel = new DefaultTableColumnModelExt();
591 
592             addColumnToModel(columnModel,
593                              null,
594                              newTableCellRender(Caracteristic.class),
595                              EditProtocolCaracteristicsTableModel.PSFM_ID);
596 
597             addComboDataColumnToModel(columnModel,
598                                       EditProtocolCaracteristicsTableModel.TYPE,
599                                       getDecorator(CaracteristicType.class, null),
600                                       Lists.newArrayList(CaracteristicType.getTabTypes()));
601 
602             addColumnToModel(columnModel,
603                              EditProtocolCaracteristicsTableModel.IMPORT_FILE_COLUMN);
604 
605             List<Caracteristic> caracteristics = new ArrayList<>(getModel().getCaracteristics());
606             EditProtocolCaracteristicsTableModel tableModel = new EditProtocolCaracteristicsTableModel(columnModel, caracteristics);
607             caracteristicsMappingTable.setModel(tableModel);
608             caracteristicsMappingTable.setColumnModel(columnModel);
609 
610             // by default do not authorize to change column orders
611             caracteristicsMappingTable.getTableHeader().setReorderingAllowed(false);
612 
613             addHighlighters(caracteristicsMappingTable);
614 
615             caracteristicsMappingTable.getSelectionModel().addListSelectionListener(e -> {
616 
617                 int rowIndex = getCaracteristicsMappingTable().getSelectedRow();
618 
619                 boolean enableRemoveCaracteristicMapping = false;
620                 boolean enableMoveUpCaracteristicMapping = false;
621                 boolean enableMoveDownCaracteristicMapping = false;
622 
623                 if (rowIndex != -1) {
624 
625                     // there is a selected row
626                     enableRemoveCaracteristicMapping = true;
627 
628                     enableMoveUpCaracteristicMapping = rowIndex > 0;
629 
630                     enableMoveDownCaracteristicMapping = rowIndex < getCaracteristicsMappingTable().getModel().getRowCount() - 1;
631                 }
632                 EditProtocolUIModel model1 = getModel();
633                 model1.setRemoveCaracteristicMappingEnabled(enableRemoveCaracteristicMapping);
634                 model1.setMoveUpCaracteristicMappingEnabled(enableMoveUpCaracteristicMapping);
635                 model1.setMoveDownCaracteristicMappingEnabled(enableMoveDownCaracteristicMapping);
636 
637             });
638 
639             // always scroll to selected row
640             SwingUtil.scrollToTableSelection(caracteristicsMappingTable);
641 
642             tableModel.setRows(caracteristicMappingRows);
643         }
644 
645         // init operation fields table
646         {
647             JXTable operationFieldsTable = getUI().getOperationFieldsMappingTable();
648 
649             DefaultTableColumnModelExt columnModel = new DefaultTableColumnModelExt();
650 
651             addColumnToModel(columnModel,
652                              null,
653                              new DefaultTableCellRenderer() {
654 
655                                  @Override
656                                  public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
657                                      setText(t("tutti.editFishingOperation.field." + value.toString()));
658                                      return this;
659                                  }
660                              },
661                              EditProtocolOperationFieldsTableModel.FIELD);
662 
663             addColumnToModel(columnModel,
664                              EditProtocolOperationFieldsTableModel.IMPORT_FILE_COLUMN);
665 
666             EditProtocolOperationFieldsTableModel tableModel = new EditProtocolOperationFieldsTableModel(columnModel);
667             operationFieldsTable.setModel(tableModel);
668             operationFieldsTable.setColumnModel(columnModel);
669 
670             // by default do not authorize to change column orders
671             operationFieldsTable.getTableHeader().setReorderingAllowed(false);
672 
673             addHighlighters(operationFieldsTable);
674 
675             tableModel.setRows(operationFieldMappingRows);
676 
677             // always scroll to selected row
678             SwingUtil.scrollToTableSelection(operationFieldsTable);
679         }
680 
681         populateImportColumnTableEditors();
682 
683         initZonesTree();
684 
685         initAvailableStratasTree();
686 
687 
688         // if new protocol can already cancel his creation
689         model.setModify(model.isCreate() || model.isCleaned());
690 
691         this.ui.getCaracteristicPane().addChangeListener(e -> {
692             JTabbedPane tabPanel = (JTabbedPane) e.getSource();
693             int selectedIndex = tabPanel.getSelectedIndex();
694             BeanDoubleList<Caracteristic> selectedDoubleList;
695             log.debug("selected " + selectedIndex);
696             switch (selectedIndex) {
697                 case 0:
698                     selectedDoubleList = EditProtocolUIHandler.this.ui.getLengthClassesList();
699                     break;
700 
701                 case 1:
702                     selectedDoubleList = EditProtocolUIHandler.this.ui.getIndividualObservationList();
703                     break;
704 
705                 case 2:
706                     selectedDoubleList = EditProtocolUIHandler.this.ui.getMaturityList();
707                     break;
708 
709                 default:
710                     selectedDoubleList = null;
711             }
712             if (selectedDoubleList != null) {
713                 selectedDoubleList.getHandler().refreshFilteredElements();
714 
715             } else {
716                 // if the selected tab is the operation caracteristic mapping tab, then update the combobox
717                 List<Caracteristic> selectedCaracteristics = new ArrayList<>(getModel().getCaracteristics());
718                 for (BeanDoubleList<Caracteristic> doubleList : allDoubleLists) {
719                     selectedCaracteristics.removeAll(doubleList.getModel().getSelected());
720                 }
721                 selectedCaracteristics.removeAll(getModel().getUsedCaracteristics());
722                 getUI().getCaracteristicMappingComboBox().setData(selectedCaracteristics);
723             }
724         });
725 
726         this.ui.getTabPanel().addChangeListener(e -> {
727             JTabbedPane tabPanel = (JTabbedPane) e.getSource();
728             int selectedIndex = tabPanel.getSelectedIndex();
729             if (log.isDebugEnabled()) {
730                 log.debug("New tab selected: " + selectedIndex);
731             }
732             if (selectedIndex == 4) {
733 
734                 if (log.isDebugEnabled()) {
735                     log.debug("Update species list on calcifiedPiecesSamplingEditorUI model.");
736                 }
737 
738                 // on met à jour la liste des espèces disponibles dans l'onglet des pièces calcifiées
739                 CalcifiedPiecesSamplingEditorUI calcifiedPiecesSamplingEditorUI = getUI().getCalcifiedPiecesSamplingEditorUI();
740 
741                 List<Species> protocolSpecies = new ArrayList<>();
742 
743                 List<EditProtocolSpeciesRowModel> speciesRows1 = getModel().getSpeciesRow();
744                 if (speciesRows1 != null) {
745 
746                     protocolSpecies.addAll(speciesRows1.stream()
747                                                        .filter(EditProtocolSpeciesRowModel::withLengthStepPmfm)
748                                                        .map(EditProtocolSpeciesRowModel::toSpeciesWithSurveyCode)
749                                                        .collect(Collectors.toList()));
750 
751                 }
752 
753                 List<EditProtocolSpeciesRowModel> benthosRows1 = getModel().getBenthosRow();
754                 if (benthosRows1 != null) {
755 
756                     protocolSpecies.addAll(benthosRows1.stream()
757                                                        .filter(EditProtocolSpeciesRowModel::withLengthStepPmfm)
758                                                        .map(EditProtocolSpeciesRowModel::toSpeciesWithSurveyCode)
759                                                        .collect(Collectors.toList()));
760 
761                 }
762 
763                 protocolSpecies.removeAll(getModel().getCpsRows()
764                                                     .stream()
765                                                     .map(CalcifiedPiecesSamplingEditorRowModel::getProtocolSpecies)
766                                                     .map(EditProtocolSpeciesRowModel::getSpecies)
767                                                     .collect(Collectors.toList()));
768 
769                 // Si on ne vide pas la liste et que les espèces sont égales (sur l'id), rien n'est changé
770                 calcifiedPiecesSamplingEditorUI.getSpeciesComboBox().setData(null);
771                 calcifiedPiecesSamplingEditorUI.getSpeciesComboBox().setData(protocolSpecies);
772 
773             }
774         });
775 
776         model.setVersion(TuttiProtocols.CURRENT_PROTOCOL_VERSION);
777 
778     }
779 
780     protected void addIndividualObservationColumnToModel(DefaultTableColumnModelExt columnModel, JXTable table) {
781         TableCellRenderer renderer = new TableCellRenderer() {
782 
783             TableCellRenderer delegate = table.getDefaultRenderer(Boolean.class);
784 
785             @Override
786             public Component getTableCellRendererComponent(JTable table,
787                                                            Object value,
788                                                            boolean isSelected,
789                                                            boolean hasFocus,
790                                                            int row, int column) {
791                 Component result = delegate.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
792                 result.setEnabled(table.isCellEditable(row, column));
793                 return result;
794             }
795         };
796         addColumnToModel(columnModel,
797                          table.getDefaultEditor(Boolean.class),
798                          renderer,
799                          EditProtocolSpeciesTableModel.INDIVIDUAL_OBSERVATION_ENABLED);
800     }
801 
802     protected void initZonesTree() {
803 
804         EditProtocolUIModel model = getModel();
805 
806         ZonesTreeModel zonesTreeModel = model.getZonesTreeModel();
807 
808         zonesTreeModel.setCreatingModel(true);
809 
810         try {
811 
812             model.getIncomingZones().forEach(zonesTreeModel::addZone);
813 
814         } finally {
815             zonesTreeModel.setCreatingModel(false);
816         }
817 
818         // expand nodes when children are added
819         zonesTreeModel.addTreeModelListener(new TreeModelListener() {
820 
821             @Override
822             public void treeNodesChanged(TreeModelEvent e) {
823                 model.setModify(true);
824             }
825 
826             @Override
827             public void treeNodesInserted(TreeModelEvent e) {
828                 getUI().getZoneEditor().getZonesTree().expandPath(e.getTreePath());
829             }
830 
831             @Override
832             public void treeNodesRemoved(TreeModelEvent e) {
833             }
834 
835             @Override
836             public void treeStructureChanged(TreeModelEvent e) {
837             }
838         });
839 
840         initJTree(ui.getZoneEditor().getZonesTree());
841 
842 
843     }
844 
845     protected void initAvailableStratasTree() {
846 
847         EditProtocolUIModel model = getModel();
848 
849         Set<String> usedStrataLocations = new LinkedHashSet<>();
850         Set<String> usedSubStrataLocations = new LinkedHashSet<>();
851 
852         // Récupération des identifiants utilisés dans les zones entrantes
853         model.getIncomingZones().forEach(zone -> {
854 
855             Collection<Strata> zoneStratas = zone.getStrata();
856 
857             Set<String> strataLocations = zoneStratas.stream()
858                                                      .map(Strata::getId)
859                                                      .collect(Collectors.toSet());
860             usedStrataLocations.addAll(strataLocations);
861 
862             Set<String> subStrataLocations = zoneStratas.stream()
863                                                         .map(Strata::getSubstrata)
864                                                         .flatMap(Collection::stream)
865                                                         .map(SubStrata::getId)
866                                                         .collect(Collectors.toSet());
867             usedSubStrataLocations.addAll(subStrataLocations);
868 
869         });
870 
871         AvailableStratasTreeModel availableStratasTreeModel = model.getAvailableStratasTreeModel();
872 
873         availableStratasTreeModel.setCreatingModel(true);
874 
875         try {
876 
877             programStratasAndSubstratas.keySet().stream().forEach(strataLocation -> {
878 
879                 String strataId = strataLocation.getId();
880 
881                 Collection<TuttiLocation> subStrataLocations = new HashSet<>(programStratasAndSubstratas.get(strataLocation));
882                 subStrataLocations.remove(null);
883                 List<String> subStratas = subStrataLocations.stream()
884                                                             .map(TuttiLocation::getId)
885                                                             .filter(id -> !usedSubStrataLocations.contains(id))
886                                                             .collect(Collectors.toList());
887 
888                 if (!(subStratas.isEmpty() && usedStrataLocations.contains(strataLocation.getId()))) {
889 
890                     StrataNode strataNode = availableStratasTreeModel.getOrCreateStrataNode(strataId);
891                     subStratas.forEach(subStrataId -> availableStratasTreeModel.addSubsStrata(strataNode, subStrataId));
892 
893                 }
894 
895             });
896 
897         } finally {
898 
899             availableStratasTreeModel.setCreatingModel(false);
900 
901         }
902 
903         initJTree(ui.getZoneEditor().getAvailableStratasTree());
904 
905     }
906 
907     @Override
908     protected JComponent getComponentToFocus() {
909         return getUI().getNameField();
910     }
911 
912     public List<EditProtocolSpeciesRowModel> toSpeciesRows(List<SpeciesProtocol> speciesProtocols) {
913         return toSpeciesRows(speciesProtocols, getModel().getCpsRows());
914     }
915 
916     public List<EditProtocolSpeciesRowModel> toSpeciesRows(List<SpeciesProtocol> speciesProtocols,
917                                                            List<CalcifiedPiecesSamplingEditorRowModel> cpsRows) {
918         BeanFilterableComboBox<Species> speciesComboBox = ui.getSpeciesComboBox();
919         Preconditions.checkNotNull(speciesComboBox.getData());
920         BeanFilterableComboBox<Species> benthosComboBox = ui.getBenthosComboBox();
921         Preconditions.checkNotNull(benthosComboBox.getData());
922 
923         EditProtocolUIModel model = getModel();
924 
925         Map<String, Species> allReferentSpeciesByTaxonId = model.getAllReferentSpeciesByTaxonId();
926         Map<String, Caracteristic> allCaracteristic = model.getAllCaracteristic();
927 
928         List<Caracteristic> lengthClassesPmfmId = Lists.newArrayList();
929 
930         Set<Species> speciesSet = Sets.newHashSet();
931         List<EditProtocolSpeciesRowModel> result = Lists.newArrayList();
932         if (CollectionUtils.isNotEmpty(speciesProtocols)) {
933             for (SpeciesProtocol speciesProtocol : speciesProtocols) {
934                 Integer taxonId = speciesProtocol.getSpeciesReferenceTaxonId();
935 
936                 if (taxonId == null) {
937 
938                     continue;
939                 }
940                 String taxonIdStr = String.valueOf(taxonId);
941 
942                 // remove all synonyms from available synonym list
943                 Collection<Species> allSynonyms = model.getAllSynonyms(taxonIdStr);
944                 model.getAllSynonyms().removeAll(allSynonyms);
945 
946                 // get species referent taxon
947                 Species species = allReferentSpeciesByTaxonId.get(taxonIdStr);
948 
949                 // make sure it exists
950                 Preconditions.checkNotNull(species, "Espèce inconnue : " + taxonIdStr);
951                 speciesSet.add(species);
952 
953                 EditProtocolSpeciesRowModel row = createNewProtocolSpeciesRow();
954                 row.setSpecies(species);
955 
956                 String lengthStepPmfmId = speciesProtocol.getLengthStepPmfmId();
957                 Caracteristic lengthStepPmfm = allCaracteristic.get(lengthStepPmfmId);
958                 if (lengthStepPmfmId != null &&
959                         !lengthClassesPmfmId.contains(lengthStepPmfm) &&
960                         !model.containsLengthClassesPmfmId(lengthStepPmfmId)) {
961                     if (log.isInfoEnabled()) {
962                         log.info("Found a new lengthStep pmfm: " + lengthStepPmfmId);
963                     }
964                     lengthClassesPmfmId.add(lengthStepPmfm);
965                 }
966                 row.setLengthStepPmfm(lengthStepPmfm);
967 
968                 String maturityPmfmId = speciesProtocol.getMaturityPmfmId();
969                 Caracteristic maturityPmfm = allCaracteristic.get(maturityPmfmId);
970                 row.setMaturityPmfm(maturityPmfm);
971 
972                 row.fromEntity(speciesProtocol);
973 
974                 // make sure to get a clean copy of the list
975                 row.setMandatorySampleCategoryId(Lists.newArrayList(speciesProtocol.getMandatorySampleCategoryId()));
976                 result.add(row);
977 
978                 // add all the cps rows
979                 Collection<CalcifiedPiecesSamplingDefinition> cpsDefs = speciesProtocol.getCalcifiedPiecesSamplingDefinition();
980                 if (cpsDefs != null) {
981                     cpsDefs.forEach(def -> {
982                         CalcifiedPiecesSamplingEditorRowModel cpsRow = new CalcifiedPiecesSamplingEditorRowModel();
983                         cpsRow.fromEntity(def);
984                         cpsRow.setProtocolSpecies(row);
985                         cpsRow.setValid(true);
986                         cpsRows.add(cpsRow);
987                     });
988                 }
989 
990             }
991 
992             // remove once for all in comboboxes
993             speciesComboBox.removeItems(speciesSet);
994             benthosComboBox.removeItems(speciesSet);
995         }
996 
997         if (CollectionUtils.isNotEmpty(lengthClassesPmfmId)) {
998             // detect some new length step pmfp to add in protocol
999 
1000             // add it to model
1001             model.addAllLengthClassesPmfmId(TuttiEntities.collecIds(lengthClassesPmfmId));
1002 
1003             // add it to ui (no binding here, must do it manually)
1004             BeanDoubleList<Caracteristic> lengthClassesList = ui.getLengthClassesList();
1005             lengthClassesList.getModel().addToSelected(lengthClassesPmfmId);
1006         }
1007         return result;
1008     }
1009 
1010     public EditProtocolSpeciesRowModel createNewProtocolSpeciesRow() {
1011 
1012         EditProtocolSpeciesRowModel result = getModel().newRow();
1013 
1014         result.addPropertyChangeListener(EditProtocolSpeciesRowModel.PROPERTY_LENGTH_STEP_PMFM, evt -> {
1015 
1016             Caracteristic newValue = (Caracteristic) evt.getNewValue();
1017             if (newValue == null) {
1018 
1019                 result.setIndividualObservationEnabled(false);
1020 
1021                 if (getModel().getCpsRows() != null) {
1022 
1023                     List<CalcifiedPiecesSamplingEditorRowModel> speciesCpsRows =
1024                             getModel().getCpsRows()
1025                                       .stream()
1026                                       .filter(row -> row.getProtocolSpecies().equals(result))
1027                                       .collect(Collectors.toList());
1028 
1029                     if (!speciesCpsRows.isEmpty()) {
1030                         String htmlMessage = String.format(
1031                                 AbstractApplicationUIHandler.CONFIRMATION_FORMAT,
1032                                 t("tutti.editProtocol.action.removeLengthStepPmfm.removeCpsRows.message"),
1033                                 t("jaxx.application.common.askBeforeDelete.help"));
1034                         int i = JOptionPane.showConfirmDialog(
1035                                 getUI(),
1036                                 htmlMessage,
1037                                 t("tutti.editProtocol.action.removeLengthStepPmfm.removeCpsRows.title"),
1038                                 JOptionPane.OK_CANCEL_OPTION,
1039                                 JOptionPane.QUESTION_MESSAGE);
1040 
1041                         boolean deleteCpsRows = i == JOptionPane.OK_OPTION;
1042 
1043                         if (deleteCpsRows) {
1044 
1045                             deleteCpsRows(speciesCpsRows);
1046 
1047                         } else {
1048 
1049                             result.setLengthStepPmfm((Caracteristic) evt.getOldValue());
1050 
1051                         }
1052                     }
1053                 }
1054             }
1055         });
1056         return result;
1057     }
1058 
1059     public void deleteCpsRows(Collection<CalcifiedPiecesSamplingEditorRowModel> cpsRowsToDelete) {
1060 
1061         CalcifiedPiecesSamplingEditorUI cpsEditor = getUI().getCalcifiedPiecesSamplingEditorUI();
1062 
1063         List<CalcifiedPiecesSamplingEditorRowModel> cpsRows = getModel().getCpsRows();
1064 
1065         TreeSet<Integer> indexesToDelete =
1066                 new TreeSet<>(cpsRowsToDelete.stream().map(cpsRows::indexOf).collect(Collectors.toSet()));
1067 
1068         cpsRows.removeAll(cpsRowsToDelete);
1069 
1070         CalcifiedPiecesSamplingEditorTableModel cpsTableModel =
1071                 (CalcifiedPiecesSamplingEditorTableModel) cpsEditor.getCpsTable().getModel();
1072         cpsTableModel.fireTableRowsDeleted(indexesToDelete.first(), indexesToDelete.last());
1073     }
1074 
1075     public List<EditProtocolCaracteristicsRowModel> toProtocolCaracteristicRows(List<CaracteristicMappingRow> caracteristicMappingRows) {
1076         BeanFilterableComboBox<Caracteristic> caracteristicMappingComboBox = ui.getCaracteristicMappingComboBox();
1077         Preconditions.checkNotNull(caracteristicMappingComboBox.getData());
1078 
1079         EditProtocolUIModel model = getModel();
1080 
1081         Set<Caracteristic> caracteristicSet = Sets.newHashSet();
1082         List<EditProtocolCaracteristicsRowModel> result = Lists.newArrayList();
1083         if (CollectionUtils.isNotEmpty(caracteristicMappingRows)) {
1084             for (CaracteristicMappingRow caracteristicMappingRow : caracteristicMappingRows) {
1085                 String pmfmId = caracteristicMappingRow.getPmfmId();
1086 
1087                 if (pmfmId == null) {
1088 
1089                     continue;
1090                 }
1091 
1092                 // make sure it exists
1093                 caracteristicSet.add(model.getAllCaracteristic().get(pmfmId));
1094 
1095                 EditProtocolCaracteristicsRowModel row = createEditProtocolCaracteristicsRowModel();
1096                 row.fromEntity(caracteristicMappingRow);
1097                 row.setValid(row.getType() != null);
1098 
1099                 result.add(row);
1100             }
1101 
1102             caracteristicMappingComboBox.removeItems(caracteristicSet);
1103         }
1104 
1105         return result;
1106     }
1107 
1108     public List<EditProtocolOperationFieldsRowModel> toProtocolOperationFieldsRows(Collection<OperationFieldMappingRow> operationFieldMappingRows) {
1109         List<EditProtocolOperationFieldsRowModel> result = Lists.newArrayList();
1110 
1111         if (operationFieldMappingRows == null) {
1112             operationFieldMappingRows = new ArrayList<>();
1113         }
1114         Map<String, OperationFieldMappingRow> rowsByField = Maps.uniqueIndex(operationFieldMappingRows,
1115                                                                              OperationFieldMappingRow::getField);
1116 
1117         n("tutti.editFishingOperation.field.gearShootingStartDay");
1118         n("tutti.editFishingOperation.field.gearShootingStartTime");
1119         n("tutti.editFishingOperation.field.gearShootingEndDay");
1120         n("tutti.editFishingOperation.field.gearShootingEndTime");
1121 
1122         List<String> properties = Lists.newArrayList(
1123                 FishingOperation.PROPERTY_STATION_NUMBER,
1124                 FishingOperation.PROPERTY_FISHING_OPERATION_NUMBER,
1125                 FishingOperations.PROPERTY_GEAR_SHOOTING_START_DAY,
1126                 FishingOperations.PROPERTY_GEAR_SHOOTING_START_TIME,
1127                 FishingOperation.PROPERTY_GEAR_SHOOTING_START_LATITUDE,
1128                 FishingOperation.PROPERTY_GEAR_SHOOTING_START_LONGITUDE,
1129                 FishingOperations.PROPERTY_GEAR_SHOOTING_END_DAY,
1130                 FishingOperations.PROPERTY_GEAR_SHOOTING_END_TIME,
1131                 FishingOperation.PROPERTY_GEAR_SHOOTING_END_LATITUDE,
1132                 FishingOperation.PROPERTY_GEAR_SHOOTING_END_LONGITUDE,
1133                 FishingOperation.PROPERTY_FISHING_OPERATION_RECTILIGNE,
1134                 FishingOperation.PROPERTY_TRAWL_DISTANCE,
1135                 FishingOperation.PROPERTY_FISHING_OPERATION_VALID,
1136                 FishingOperation.PROPERTY_MULTIRIG_AGGREGATION,
1137                 FishingOperation.PROPERTY_RECORDER_PERSON,
1138                 FishingOperation.PROPERTY_GEAR,
1139                 FishingOperation.PROPERTY_VESSEL,
1140                 FishingOperation.PROPERTY_STRATA,
1141                 FishingOperation.PROPERTY_SUB_STRATA,
1142                 FishingOperation.PROPERTY_LOCATION,
1143                 FishingOperation.PROPERTY_SECONDARY_VESSEL
1144         );
1145         //TODO voir avec Vincent comment on gère le cas où aucun des champs clé n'est renseigné
1146 
1147         for (String property : properties) {
1148             OperationFieldMappingRow row = rowsByField.get(property);
1149             EditProtocolOperationFieldsRowModel newRow = createOperationFieldRow(property, row != null ? row.getImportColumn() : null);
1150             result.add(newRow);
1151         }
1152         return result;
1153     }
1154 
1155     @Override
1156     public void onCloseUI() {
1157         if (log.isDebugEnabled()) {
1158             log.debug("closing: " + ui);
1159         }
1160         clearValidators();
1161         ui.getTabPanel().setSelectedIndex(0);
1162 
1163         closeUI(ui.getRtpEditorUI());
1164         closeUI(ui.getZoneEditor());
1165 
1166     }
1167 
1168     @Override
1169     public boolean quitUI() {
1170         return quitScreen(
1171                 getModel().isValid(),
1172                 getModel().isModify(),
1173                 t("tutti.editProtocol.askCancelEditBeforeLeaving.cancelSaveProtocol"),
1174                 t("tutti.editProtocol.askSaveBeforeLeaving.saveProtocol"),
1175                 ui.getSaveButton().getAction()
1176         );
1177     }
1178 
1179     //------------------------------------------------------------------------//
1180     //-- Public methods                                                     --//
1181     //------------------------------------------------------------------------//
1182 
1183     public void addDoubleListListeners() {
1184         EditProtocolUIModel model = getModel();
1185 
1186         for (BeanDoubleList<Caracteristic> list : allDoubleLists) {
1187             String id = (String) list.getClientProperty("_updateListenerId");
1188             UpdateSelectedList updateListener = (UpdateSelectedList) list.getClientProperty("_updateListener");
1189             model.addPropertyChangeListener(id, updateListener);
1190         }
1191     }
1192 
1193     public void removeDoubleListListeners() {
1194         EditProtocolUIModel model = getModel();
1195 
1196         for (BeanDoubleList<Caracteristic> list : allDoubleLists) {
1197             String id = (String) list.getClientProperty("_updateListenerId");
1198             UpdateSelectedList updateListener = (UpdateSelectedList) list.getClientProperty("_updateListener");
1199             model.removePropertyChangeListener(id, updateListener);
1200         }
1201     }
1202 
1203 //    //FIXME tchemit-2014-01-09 Bad place for this!
1204 //    public Species openSelectOtherSpeciesDialog(String title, List<Species> species) {
1205 //        SelectSpeciesUIModel model = dialog.getModel();
1206 //        model.setSpecies(species);
1207 //        model.setSelectedSpecies(null);
1208 //
1209 //        openDialog(dialog, title, new Dimension(400, 130));
1210 //
1211 //        return model.getSelectedSpecies();
1212 //    }
1213 
1214     public EditProtocolCaracteristicsRowModel createEditProtocolCaracteristicsRowModel() {
1215         EditProtocolCaracteristicsRowModel newRow = new EditProtocolCaracteristicsRowModel(getModel().getCaracteristics());
1216 
1217         newRow.addPropertyChangeListener(evt -> {
1218             EditProtocolCaracteristicsRowModel row = (EditProtocolCaracteristicsRowModel) evt.getSource();
1219             EditProtocolUIModel model = getModel();
1220 
1221             if (EditProtocolCaracteristicsRowModel.PROPERTY_IMPORT_COLUMN.equals(evt.getPropertyName())) {
1222                 String oldValue = (String) evt.getOldValue();
1223                 if (oldValue != null) {
1224                     model.decNumberOfRows(oldValue);
1225                 }
1226                 String newValue = (String) evt.getNewValue();
1227                 if (newValue != null) {
1228                     model.incNumberOfRows(newValue);
1229                 }
1230 
1231                 recomputeRowsValidState();
1232 
1233             } else {
1234                 row.setValid(isCaracteristicsRowValid(row));
1235             }
1236 
1237             if (row.isValid()) {
1238                 model.setModify(true);
1239             }
1240         });
1241 
1242         return newRow;
1243     }
1244 
1245     //------------------------------------------------------------------------//
1246     //-- Internal methods                                                   --//
1247     //------------------------------------------------------------------------//
1248 
1249     protected void initDoubleList(String propertyId,
1250                                   BeanDoubleList<Caracteristic> widget,
1251                                   List<Caracteristic> availableCaracteristics,
1252                                   List<String> selectedCaracteristics) {
1253 
1254         initBeanList(widget, availableCaracteristics, new ArrayList<>());
1255 
1256         UpdateSelectedList listener = new UpdateSelectedList(widget, getModel().getAllCaracteristic());
1257         widget.putClientProperty("_updateListener", listener);
1258         widget.putClientProperty("_updateListenerId", propertyId);
1259         listener.select(selectedCaracteristics);
1260 
1261         // add a filter to keep only in universe everything except what is selected by other double lists
1262         List<BeanDoubleList<Caracteristic>> list = new ArrayList<>(allDoubleLists);
1263         list.remove(widget);
1264         widget.getHandler().addFilter(new SelectValuePredicate(list));
1265         widget.getHandler().addFilter(caracteristic -> !getModel().isCaracteristicUsedInMapping(caracteristic));
1266 
1267         widget.getHandler().sortData();
1268     }
1269 
1270     protected void selectCaracteristics(List<String> ids, JComboBox comboBox) {
1271 
1272         Map<String, Caracteristic> allCaracteristic = getModel().getAllCaracteristic();
1273         List<Caracteristic> selection = new ArrayList<>();
1274         if (CollectionUtils.isNotEmpty(ids)) {
1275             for (String id : ids) {
1276                 selection.add(allCaracteristic.get(id));
1277             }
1278         }
1279 
1280         List<Caracteristic> dataToList = new ArrayList<>(selection);
1281 
1282         // add a null value at first position
1283         if (!dataToList.isEmpty() && dataToList.get(0) != null) {
1284             dataToList.add(0, null);
1285         }
1286         SwingUtil.fillComboBox(comboBox, dataToList, null);
1287     }
1288 
1289     protected void addLengthClassesColumnToModel(TableColumnModel model, List<String> selectedIds) {
1290 
1291         Decorator<Caracteristic> decorator = getDecorator(Caracteristic.class, null);
1292 
1293         JComboBox<Caracteristic> comboBox = new JComboBox<>();
1294 
1295         getModel().addPropertyChangeListener(EditProtocolUIModel.PROPERTY_LENGTH_CLASSES_PMFM_ID, evt -> {
1296             List<String> ids = (List<String>) evt.getNewValue();
1297             selectCaracteristics(ids, comboBox);
1298         });
1299         comboBox.setRenderer(newListCellRender(decorator));
1300 
1301         selectCaracteristics(selectedIds, comboBox);
1302         ObjectToStringConverter converter = BeanUIUtil.newDecoratedObjectToStringConverter(decorator);
1303         BeanUIUtil.decorate(comboBox, converter);
1304         ComboBoxCellEditor editor = new ComboBoxCellEditor(comboBox);
1305 
1306         addColumnToModel(model,
1307                          editor,
1308                          newTableCellRender(decorator),
1309                          EditProtocolSpeciesTableModel.LENGTH_STEP_PMFM_ID);
1310     }
1311 
1312     protected void addMaturityColumnToModel(TableColumnModel model, List<String> selectedIds) {
1313 
1314         Decorator<Caracteristic> decorator = getDecorator(Caracteristic.class, null);
1315 
1316         JComboBox<Caracteristic> comboBox = new JComboBox();
1317 
1318         getModel().addPropertyChangeListener(EditProtocolUIModel.PROPERTY_MATURITY_PMFM_ID, evt -> {
1319             List<String> ids = (List<String>) evt.getNewValue();
1320             selectCaracteristics(ids, comboBox);
1321         });
1322 
1323         comboBox.setRenderer(newListCellRender(decorator));
1324 
1325         selectCaracteristics(selectedIds, comboBox);
1326         ObjectToStringConverter converter = BeanUIUtil.newDecoratedObjectToStringConverter(decorator);
1327         BeanUIUtil.decorate(comboBox, converter);
1328         ComboBoxCellEditor editor = new ComboBoxCellEditor(comboBox);
1329 
1330         addColumnToModel(model,
1331                          editor,
1332                          newTableCellRender(decorator),
1333                          EditProtocolSpeciesTableModel.MATURITY_PMFM_ID);
1334     }
1335 
1336     protected void addCpsPmfmColumnToModel(TableColumnModel model) {
1337 
1338         Decorator<CaracteristicQualitativeValue> caracteristicQualitativeValueDecorator =
1339                 getDecorator(CaracteristicQualitativeValue.class, null);
1340 
1341         addComboDataColumnToModel(model,
1342                                   EditProtocolSpeciesTableModel.CALCIFIED_PIECES_SAMPLING_TYPE_PMFM_ID,
1343                                   caracteristicQualitativeValueDecorator,
1344                                   getDataContext().getCpsTypeValues());
1345 
1346     }
1347 
1348     protected void initTable(final JXTable table,
1349                              DefaultTableColumnModelExt columnModel,
1350                              TableColumnExt speciesColumn,
1351                              ColumnIdentifier rtpIdentifier,
1352                              List<EditProtocolSpeciesRowModel> rows,
1353                              ListSelectionListener selectionListener) {
1354 
1355         EditProtocolSpeciesTableModel tableModel = new EditProtocolSpeciesTableModel(getModel()::newRow, columnModel);
1356         table.setModel(tableModel);
1357         table.setColumnModel(columnModel);
1358 
1359         JTableHeader tableHeader = table.getTableHeader();
1360 
1361         // by default do not authorize to change column orders
1362         tableHeader.setReorderingAllowed(false);
1363 
1364         addHighlighters(table);
1365         if (rtpIdentifier != null) {
1366             addRtpHighlighter(table, rtpIdentifier);
1367         }
1368 
1369         // always scroll to selected row
1370         SwingUtil.scrollToTableSelection(table);
1371 
1372         // add selection listener
1373         table.getSelectionModel().addListSelectionListener(selectionListener);
1374 
1375         // when model change, then rebuild the species comparator + set model as modified
1376         tableModel.addTableModelListener(e -> {
1377             getModel().setModify(true);
1378 
1379             int type = e.getType();
1380             if (type == TableModelEvent.DELETE ||
1381                     type == TableModelEvent.INSERT ||
1382                     e.getLastRow() == Integer.MAX_VALUE) {
1383 
1384                 // get species column
1385                 TableColumnExt tableColumn = (TableColumnExt) table.getColumns().get(0);
1386 
1387                 // get column comparator
1388                 TuttiDecorator.TuttiDecoratorComparator<Species> comparator =
1389                         (TuttiDecorator.TuttiDecoratorComparator<Species>)
1390                                 tableColumn.getComparator();
1391 
1392                 // get column comparator
1393                 TuttiDecorator<Species> decorator =
1394                         SpeciesBatchRowHelper.getSpeciesColumnDecorator(tableColumn);
1395 
1396                 boolean comparatorNull = comparator == null;
1397                 if (comparatorNull) {
1398 
1399                     // first time coming here, add the comparator
1400                     comparator = decorator.getCurrentComparator();
1401                 }
1402 
1403                 // init comparator with model species list
1404                 comparator.init(decorator, tableModel.getSpeciesList());
1405 
1406                 if (comparatorNull) {
1407 
1408                     // affect it to colum
1409                     tableColumn.setComparator(comparator);
1410                 }
1411             }
1412         });
1413 
1414         // create popup to change species decorator
1415 
1416         SpeciesBatchRowHelper.installSpeciesColumnComparatorPopup(
1417                 table,
1418                 speciesColumn,
1419                 null,
1420                 t("tutti.species.refTaxCode.tip"),
1421                 t("tutti.species.name.tip")
1422         );
1423 
1424         // at the very end, set rows to model
1425         tableModel.setRows(rows);
1426     }
1427 
1428     protected void addRtpHighlighter(JXTable table, ColumnIdentifier identifier) {
1429         Color cellWithValueColor = getConfig().getColorCellWithValue();
1430 
1431         Highlighter commentHighlighter = TuttiUIUtil.newBackgroundColorHighlighter(
1432                 new HighlightPredicate.AndHighlightPredicate(
1433                         new HighlightPredicate.IdentifierHighlightPredicate(identifier),
1434                         // for not null value
1435                         (renderer, adapter) -> (boolean) adapter.getValue()), cellWithValueColor);
1436         table.addHighlighter(commentHighlighter);
1437     }
1438 
1439     protected EditProtocolOperationFieldsRowModel createOperationFieldRow(String property, String column) {
1440         EditProtocolOperationFieldsRowModel newRow = new EditProtocolOperationFieldsRowModel();
1441         newRow.addPropertyChangeListener(evt -> {
1442             EditProtocolUIModel model = getModel();
1443 
1444             //update number of rows by column
1445             if (EditProtocolOperationFieldsRowModel.PROPERTY_IMPORT_COLUMN.equals(evt.getPropertyName())) {
1446                 String oldValue = (String) evt.getOldValue();
1447                 if (oldValue != null) {
1448                     model.decNumberOfRows(oldValue);
1449                 }
1450                 String newValue = (String) evt.getNewValue();
1451                 if (newValue != null) {
1452                     model.incNumberOfRows(newValue);
1453                 }
1454 
1455                 recomputeRowsValidState();
1456             }
1457         });
1458         newRow.setValid(true);
1459         newRow.setField(property);
1460         newRow.setImportColumn(column);
1461         newRow.setValid(true);
1462         newRow.addPropertyChangeListener(evt -> getModel().setModify(true));
1463         return newRow;
1464     }
1465 
1466     protected void recomputeRowsValidState() {
1467         EditProtocolUIModel model = getModel();
1468 
1469         for (EditProtocolCaracteristicsRowModel row : model.getCaracteristicMappingRows()) {
1470             row.setValid(isCaracteristicsRowValid(row));
1471         }
1472 
1473         for (EditProtocolOperationFieldsRowModel row : model.getOperationFieldMappingRows()) {
1474             row.setValid(isOperationFieldsRowValid(row));
1475         }
1476 
1477         getCaracteristicsMappingTable().repaint();
1478         getOperationFieldsMappingTable().repaint();
1479     }
1480 
1481     protected boolean isOperationFieldsRowValid(EditProtocolOperationFieldsRowModel row) {
1482         EditProtocolUIModel model = getModel();
1483         String importColumn = row.getImportColumn();
1484         return importColumn == null || model.numberOfRows(importColumn) < 2;
1485     }
1486 
1487     protected boolean isCaracteristicsRowValid(EditProtocolCaracteristicsRowModel row) {
1488         EditProtocolUIModel model = getModel();
1489         String importColumn = row.getImportColumn();
1490         return row.getType() != null &&
1491                 (importColumn == null || model.numberOfRows(importColumn) < 2);
1492     }
1493 
1494     protected void populateImportColumnTableEditors() {
1495         Collection<String> importColumns = getModel().getImportColumns();
1496         ArrayList<String> dataToList = new ArrayList<>();
1497 
1498         if (importColumns != null) {
1499             dataToList.addAll(importColumns);
1500 
1501             // add a null value at first position
1502             if (!dataToList.isEmpty() && dataToList.get(0) != null) {
1503                 dataToList.add(0, null);
1504             }
1505 
1506         } else {
1507             dataToList.add(null);
1508         }
1509 
1510         populateImportColumnTableEditor(getUI().getCaracteristicsMappingTable(), 2, dataToList);
1511         populateImportColumnTableEditor(getUI().getOperationFieldsMappingTable(), 1, dataToList);
1512     }
1513 
1514     protected void populateImportColumnTableEditor(JXTable table, int columnIndex, ArrayList<String> dataToList) {
1515         JComboBox comboBox = new JComboBox();
1516         SwingUtil.fillComboBox(comboBox, dataToList, null);
1517 
1518         TableColumnExt col = table.getColumnExt(columnIndex);
1519         ComboBoxCellEditor editor = new ComboBoxCellEditor(comboBox);
1520         col.setCellEditor(editor);
1521     }
1522 
1523     public void permuteCaracteristics(int selectedRow, int newRow) {
1524 
1525         getCaracteristicMappingTableModel().permuteEntry(selectedRow, newRow);
1526         getCaracteristicsMappingTable().getSelectionModel().setSelectionInterval(newRow, newRow);
1527 
1528         // Recompute
1529         getModel().setCaracteristicMappingRows(getCaracteristicMappingTableModel().getRows());
1530         getModel().setModify(true);
1531 
1532     }
1533 
1534     protected static class UpdateSelectedList implements PropertyChangeListener {
1535 
1536         private final BeanDoubleListModel<Caracteristic> model;
1537 
1538         private final Map<String, Caracteristic> caracteristicMap;
1539 
1540         public UpdateSelectedList(BeanDoubleList<Caracteristic> doubleList,
1541                                   Map<String, Caracteristic> caracteristicMap) {
1542             this.model = doubleList.getModel();
1543             this.caracteristicMap = caracteristicMap;
1544         }
1545 
1546         private boolean valueIsAdjusting;
1547 
1548         @Override
1549         public void propertyChange(PropertyChangeEvent evt) {
1550 
1551             if (!valueIsAdjusting) {
1552 
1553                 valueIsAdjusting = true;
1554 
1555                 try {
1556                     List<String> selectedIds = (List<String>) evt.getNewValue();
1557                     if (log.isInfoEnabled()) {
1558                         log.info("[" + evt.getPropertyName() + "] selected ids: " + selectedIds);
1559                     }
1560 
1561                     select(selectedIds);
1562                 } finally {
1563                     valueIsAdjusting = false;
1564                 }
1565             }
1566         }
1567 
1568         public void select(List<String> selectedIds) {
1569 
1570             List<Caracteristic> selection = Lists.newArrayList();
1571             if (CollectionUtils.isNotEmpty(selectedIds)) {
1572                 for (String selectedId : selectedIds) {
1573                     Caracteristic e = caracteristicMap.get(selectedId);
1574                     Preconditions.checkNotNull(e, "Could not find caracteristic with id: " + selectedId);
1575                     selection.add(e);
1576                 }
1577             }
1578             model.setSelected(selection);
1579         }
1580     }
1581 
1582     protected static class SelectValuePredicate implements Predicate<Caracteristic> {
1583 
1584         protected final List<BeanDoubleList<Caracteristic>> lists;
1585 
1586         public SelectValuePredicate(List<BeanDoubleList<Caracteristic>> lists) {
1587             this.lists = lists;
1588         }
1589 
1590         @Override
1591         public boolean apply(Caracteristic input) {
1592 
1593             boolean result = true;
1594             for (BeanDoubleList<Caracteristic> list : lists) {
1595                 if (list.getModel().getSelected().contains(input)) {
1596                     result = false;
1597                     break;
1598                 }
1599             }
1600             return result;
1601         }
1602     }
1603 
1604     protected void initJTree(JTree tree) {
1605         tree.getModel().addTreeModelListener(new ExpandOnInsertTreeModelListener(tree));
1606         JAXXWidgetUtil.expandTree(tree);
1607     }
1608 
1609     private static class ExpandOnInsertTreeModelListener implements TreeModelListener {
1610 
1611         private final JTree tree;
1612 
1613         private ExpandOnInsertTreeModelListener(JTree tree) {
1614             this.tree = tree;
1615         }
1616 
1617         @Override
1618         public void treeNodesChanged(TreeModelEvent e) {
1619         }
1620 
1621         @Override
1622         public void treeNodesInserted(TreeModelEvent e) {
1623             tree.expandPath(e.getTreePath());
1624         }
1625 
1626         @Override
1627         public void treeNodesRemoved(TreeModelEvent e) {
1628         }
1629 
1630         @Override
1631         public void treeStructureChanged(TreeModelEvent e) {
1632         }
1633 
1634     }
1635 
1636     private static class LengthStepCaracteristicCellRenderer implements ListCellRenderer<Caracteristic> {
1637 
1638         private final CaracteristicsCount caracteristicsCount;
1639         private final Color notRemovableColor;
1640 
1641         private final ListCellRenderer<? super Caracteristic> defaultRenderer;
1642 
1643         LengthStepCaracteristicCellRenderer(Color notRemovableColor, CaracteristicsCount caracteristicsCount, ListCellRenderer<? super Caracteristic> defaultRenderer) {
1644             this.notRemovableColor = notRemovableColor;
1645             this.caracteristicsCount = caracteristicsCount;
1646             this.defaultRenderer = defaultRenderer;
1647         }
1648 
1649         @Override
1650         public Component getListCellRendererComponent(JList<? extends Caracteristic> list, Caracteristic value, int index, boolean isSelected, boolean cellHasFocus) {
1651             Component result = defaultRenderer.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
1652 
1653             if (isSelected && caracteristicsCount.isCaracteristicUsed(value)) {
1654                 result.setBackground(notRemovableColor.darker());
1655             }
1656             return result;
1657         }
1658     }
1659 
1660     private class MaturityCaracteristicCellRenderer implements ListCellRenderer<Caracteristic> {
1661 
1662         public static final String TEXT_PATTERN = "<html><body><strong>%s</strong> :<ul><li>%s</li></ul><strong>%s</strong> :<ul><li>%s</li></ul></body></html>";
1663 
1664         private final CaracteristicsCount caracteristicsCount;
1665         private final ListCellRenderer<? super Caracteristic> defaultRenderer;
1666 
1667         private Color validColor = getConfig().getColorCellWithValue();
1668 
1669         private Color invalidColor = getConfig().getColorRowInvalid();
1670 
1671         private Color notRemovableColor = getConfig().getColorWarningRow();
1672 
1673         MaturityCaracteristicCellRenderer(CaracteristicsCount caracteristicsCount, ListCellRenderer<? super Caracteristic> defaultRenderer) {
1674             this.caracteristicsCount = caracteristicsCount;
1675             this.defaultRenderer = defaultRenderer;
1676         }
1677 
1678         @Override
1679         public Component getListCellRendererComponent(JList<? extends Caracteristic> list, Caracteristic value, int index, boolean isSelected, boolean cellHasFocus) {
1680             Component result = defaultRenderer.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
1681 
1682             Color backgroud;
1683             if (isSelected && caracteristicsCount.isCaracteristicUsed(value)) {
1684                 backgroud = notRemovableColor;
1685 
1686             } else if (!EditProtocolUIHandler.this.getModel().isMaturityValid(value)) {
1687                 backgroud = invalidColor;
1688             } else {
1689                 backgroud = validColor;
1690             }
1691             if (isSelected) {
1692                 backgroud = backgroud.darker();
1693             }
1694             result.setBackground(backgroud);
1695 
1696             if (result instanceof JComponent) {
1697 
1698                 ((JComponent) result).setToolTipText(createToolTipText(value));
1699             }
1700             return result;
1701         }
1702 
1703         private String createToolTipText(Caracteristic caracteristic) {
1704             String tooltip = null;
1705 
1706             if (caracteristic != null && getModel().isMaturityValid(caracteristic.getId())) {
1707 
1708                 MaturityCaracteristic maturityCaracteristic = getModel().getMaturityCaracteristic(caracteristic.getId());
1709                 List<String> matureStates = new ArrayList<>();
1710                 List<String> immatureStates = new ArrayList<>();
1711 
1712                 caracteristic.getQualitativeValue().forEach(state -> {
1713                     if (maturityCaracteristic.containsMatureStateIds(state.getId())) {
1714                         matureStates.add(decorate(state));
1715                     } else {
1716                         immatureStates.add(decorate(state));
1717                     }
1718                 });
1719 
1720                 tooltip = String.format(TEXT_PATTERN, t("tutti.editProtocol.field.maturity.immature.tip"), StringUtils.join(immatureStates, "</li><li>"),
1721                                         t("tutti.editProtocol.field.maturity.mature.tip"), StringUtils.join(matureStates, "</li><li>"));
1722             }
1723 
1724             return tooltip;
1725         }
1726 
1727     }
1728 }