View Javadoc
1   package fr.ifremer.tutti.ui.swing.content.operation.catches.species.frequency;
2   
3   /*
4    * #%L
5    * Tutti :: UI
6    * $Id:$
7    * $HeadURL:$
8    * %%
9    * Copyright (C) 2012 - 2016 Ifremer
10   * %%
11   * This program is free software: you can redistribute it and/or modify
12   * it under the terms of the GNU General Public License as
13   * published by the Free Software Foundation, either version 3 of the
14   * License, or (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 General Public
22   * License along with this program.  If not, see
23   * <http://www.gnu.org/licenses/gpl-3.0.html>.
24   * #L%
25   */
26  
27  import fr.ifremer.tutti.persistence.entities.CaracteristicMap;
28  import fr.ifremer.tutti.persistence.entities.TuttiEntities;
29  import fr.ifremer.tutti.persistence.entities.data.Attachment;
30  import fr.ifremer.tutti.persistence.entities.data.CopyIndividualObservationMode;
31  import fr.ifremer.tutti.persistence.entities.data.SampleCategory;
32  import fr.ifremer.tutti.persistence.entities.protocol.SpeciesProtocol;
33  import fr.ifremer.tutti.persistence.entities.referential.Caracteristic;
34  import fr.ifremer.tutti.persistence.entities.referential.CaracteristicQualitativeValue;
35  import fr.ifremer.tutti.persistence.entities.referential.Species;
36  import fr.ifremer.tutti.service.DecoratorService;
37  import fr.ifremer.tutti.service.sampling.SamplingCodePrefix;
38  import fr.ifremer.tutti.type.WeightUnit;
39  import fr.ifremer.tutti.ui.swing.content.operation.catches.individualobservation.SamplingCodeCellEditor;
40  import fr.ifremer.tutti.ui.swing.content.operation.catches.individualobservation.SamplingCodeCellRenderer;
41  import fr.ifremer.tutti.ui.swing.content.operation.catches.species.edit.SpeciesBatchRowModel;
42  import fr.ifremer.tutti.ui.swing.util.TuttiUI;
43  import fr.ifremer.tutti.ui.swing.util.TuttiUIUtil;
44  import fr.ifremer.tutti.ui.swing.util.attachment.AttachmentCellEditor;
45  import fr.ifremer.tutti.ui.swing.util.attachment.AttachmentCellRenderer;
46  import fr.ifremer.tutti.ui.swing.util.caracteristics.CaracteristicMapCellComponent;
47  import fr.ifremer.tutti.ui.swing.util.comment.CommentCellEditor;
48  import fr.ifremer.tutti.ui.swing.util.comment.CommentCellRenderer;
49  import jaxx.runtime.SwingUtil;
50  import org.apache.commons.lang3.StringUtils;
51  import org.apache.commons.logging.Log;
52  import org.apache.commons.logging.LogFactory;
53  import org.jdesktop.swingx.JXTable;
54  import org.jdesktop.swingx.autocomplete.ComboBoxCellEditor;
55  import org.jdesktop.swingx.decorator.HighlightPredicate;
56  import org.jdesktop.swingx.decorator.Highlighter;
57  import org.jdesktop.swingx.table.DefaultTableColumnModelExt;
58  import org.jdesktop.swingx.table.TableColumnExt;
59  import org.jdesktop.swingx.table.TableColumnModelExt;
60  import org.nuiton.decorator.Decorator;
61  
62  import javax.swing.JComboBox;
63  import java.awt.Color;
64  import java.beans.PropertyChangeListener;
65  import java.io.Closeable;
66  import java.util.ArrayList;
67  import java.util.Collections;
68  import java.util.HashSet;
69  import java.util.Iterator;
70  import java.util.List;
71  import java.util.Map;
72  import java.util.Objects;
73  import java.util.Set;
74  import java.util.function.Function;
75  
76  import static org.nuiton.i18n.I18n.t;
77  
78  /**
79   * To manage individual observations.
80   *
81   * Created on 14/04/16.
82   *
83   * @author Tony Chemit - chemit@codelutin.com
84   */
85  public class IndividualObservationBatchTableHandler implements Closeable {
86  
87      /** Logger. */
88      private static final Log log = LogFactory.getLog(IndividualObservationBatchTableHandler.class);
89  
90      /**
91       * Le controleur principal de l'écran.
92       */
93      private final SpeciesFrequencyUIHandler uiHandler;
94      /**
95       * Le tableau des observations individuelleS.
96       */
97      private final JXTable individualObservationTable;
98      /**
99       * Le modèle globale de l'écran.
100      */
101     private final SpeciesFrequencyUIModel model;
102     /**
103      * Le modèle pour gérer les observations individuelles.
104      */
105     private final IndividualObservationBatchUIModel individualObservationsModel;
106     /**
107      * Le modèle du tableau des mensurations.
108      */
109     private final SpeciesFrequencyTableModel frequencyTableModel;
110     /**
111      * Le modèle du tableau des observations individuelles.
112      */
113     private final IndividualObservationBatchTableModel individualObservationTableModel;
114     /**
115      * Le dictionnaire (indexé par identifiant) des caractéristiques de maturités définies dans le protocole.
116      */
117     private final Map<String, Caracteristic> maturityCaracteristics;
118     /**
119      * Décorateur d'espèce.
120      */
121     private final Decorator<Species> speciesDecorator;
122     private final Decorator<Caracteristic> caracteristicDecorator;
123     private final Decorator<Caracteristic> caracteristicTipDecorator;
124     /**
125      * Pour calculer la recopie d'une observation individuelle vers les mensurations.
126      */
127     private final IndividualObservationToFrequencyEngine individualObservationToFrequencyEngine;
128     /**
129      * Pour calculer les actions à effectuer sur le cache des échantillons.
130      */
131     private final IndividualObservationToSamplingCacheEngine individualObservationToSamplingCacheEngine;
132     /**
133      * Pour écouter le changement de la taille sur une ligne.
134      */
135     private final PropertyChangeListener individualObservationRowSizeChangedListener;
136     /**
137      * Pour écouter les changement de poids sur une ligne.
138      */
139     private final PropertyChangeListener individualObservationRowWeightChangedListener;
140     /**
141      * Pour écouter les changement de code de prélèvement sur une ligne.
142      */
143     private final PropertyChangeListener individualObservationRowSamplingCodeChangedListener;
144     /**
145      * Pour écouter les changement de caractéristiques sur une ligne.
146      */
147     private final PropertyChangeListener individualObservationRowCaracteristicsChangedListener;
148 
149     /**
150      * Editor for the other caracteristics column.
151      * We must pass the updated list of default caracteristic every time we recompute the default caracteristic list.
152      * Otherwise, the user can edit the maturity and sex as other caracteristics.
153      */
154     protected final CaracteristicMapCellComponent.CaracteristicMapCellEditor otherCaracteristicsEditor;
155 
156     public IndividualObservationBatchTableHandler(SpeciesFrequencyUI ui) {
157 
158         this.model = ui.getModel();
159         this.individualObservationsModel = ui.getModel().getIndividualObservationModel();
160         this.uiHandler = ui.getHandler();
161 
162         this.speciesDecorator = uiHandler.getDecorator(Species.class, DecoratorService.WITH_SURVEY_CODE_NO_NAME);
163         this.caracteristicDecorator = uiHandler.getDecorator(Caracteristic.class, DecoratorService.CARACTERISTIC_PARAMETER_ONLY_WITH_UNIT);
164         this.caracteristicTipDecorator = uiHandler.getDecorator(Caracteristic.class, DecoratorService.CARACTERISTIC_WITH_UNIT);
165 
166         this.individualObservationTable = ui.getObsTable();
167         this.frequencyTableModel = uiHandler.getTableModel();
168         this.individualObservationToFrequencyEngine = new IndividualObservationToFrequencyEngine(model);
169         this.individualObservationToSamplingCacheEngine = new IndividualObservationToSamplingCacheEngine(model);
170 
171         List<Caracteristic> maturityCaracteristics = new ArrayList<>(uiHandler.getDataContext().getMaturityCaracteristics());
172 
173         this.maturityCaracteristics = TuttiEntities.splitById(maturityCaracteristics);
174 
175         // Ecoute quand la taille a changé sur une observation
176         this.individualObservationRowSizeChangedListener = event -> {
177 
178             IndividualObservationBatchRowModel source = (IndividualObservationBatchRowModel) event.getSource();
179             Float oldSize = (Float) event.getOldValue();
180 
181             Float weight = source.getWeight();
182 
183             CaracteristicQualitativeValue maturity = individualObservationsModel.getMaturityValue(source);
184             CaracteristicQualitativeValue gender = individualObservationsModel.getGender(source);
185 
186             String samplingCode = source.getSamplingCode();
187 
188             boolean oldValid = source.computeValid(oldSize != null, weight != null);
189             boolean newValid = source.computeValid();
190 
191             IndividualObservationBatchRowState oldState = new IndividualObservationBatchRowState(oldSize, weight, maturity, gender, samplingCode, oldValid);
192             IndividualObservationBatchRowState newState = new IndividualObservationBatchRowState(source.getSize(), weight, maturity, gender, samplingCode, newValid);
193             onSamplingRowChanged(source, oldState, newState);
194             onRowChangedForFrequencies(source, oldState, newState);
195 
196             individualObservationsModel.recomputeRowValidState(source);
197             model.recomputeCanEditLengthStep();
198             model.setModify(true);
199 
200         };
201 
202         // Ecoute quand le poids a changé sur une observation
203         this.individualObservationRowWeightChangedListener = event -> {
204 
205             IndividualObservationBatchRowModel source = (IndividualObservationBatchRowModel) event.getSource();
206             if (source.withSize()) {
207 
208                 Float oldWeight = (Float) event.getOldValue();
209                 Float size = source.getSize();
210 
211                 CaracteristicQualitativeValue maturity = individualObservationsModel.getMaturityValue(source);
212                 CaracteristicQualitativeValue gender = individualObservationsModel.getGender(source);
213 
214                 String samplingCode = source.getSamplingCode();
215 
216                 boolean oldValid = source.computeValid(size != null, oldWeight != null);
217                 boolean newValid = source.computeValid();
218 
219                 IndividualObservationBatchRowState oldState = new IndividualObservationBatchRowState(size, oldWeight, maturity, gender, samplingCode, oldValid);
220                 IndividualObservationBatchRowState newState = new IndividualObservationBatchRowState(size, source.getWeight(), maturity, gender, samplingCode, newValid);
221                 onSamplingRowChanged(source, oldState, newState);
222                 onRowChangedForFrequencies(source, oldState, newState);
223 
224             } else {
225 
226                 individualObservationsModel.recomputeRowValidState(source);
227 
228             }
229 
230             model.recomputeCanEditLengthStep();
231             model.setModify(true);
232 
233         };
234 
235         // Ecoute quand le code de prélèvement a changé
236         this.individualObservationRowSamplingCodeChangedListener = event -> {
237 
238             IndividualObservationBatchRowModel source = (IndividualObservationBatchRowModel) event.getSource();
239             if (source.withSize()) {
240 
241                 String oldSamplingCode = (String) event.getOldValue();
242 
243                 Float size = source.getSize();
244                 Float weight = source.getWeight();
245                 CaracteristicQualitativeValue maturity = individualObservationsModel.getMaturityValue(source);
246                 CaracteristicQualitativeValue gender = individualObservationsModel.getGender(source);
247 
248                 boolean validState = source.computeValid();
249 
250                 IndividualObservationBatchRowState oldState = new IndividualObservationBatchRowState(size, weight, maturity, gender, oldSamplingCode, validState);
251                 IndividualObservationBatchRowState newState = new IndividualObservationBatchRowState(size, weight, maturity, gender, source.getSamplingCode(), validState);
252                 onSamplingRowChanged(source, oldState, newState);
253 
254             } else {
255 
256                 individualObservationsModel.recomputeRowValidState(source);
257 
258             }
259 
260             model.setModify(true);
261 
262         };
263 
264         // Ecoute quand les caractéristiques ont changées
265         this.individualObservationRowCaracteristicsChangedListener = event -> {
266 
267             IndividualObservationBatchRowModel source = (IndividualObservationBatchRowModel) event.getSource();
268             if (source.withSize()) {
269 
270                 CaracteristicMap oldCaracteristicMap = (CaracteristicMap) event.getOldValue();
271 
272                 CaracteristicQualitativeValue oldGender = individualObservationsModel.getGender(oldCaracteristicMap);
273                 CaracteristicQualitativeValue newGender = individualObservationsModel.getGender(source);
274 
275                 if (!Objects.equals(oldGender, newGender)) {
276 
277                     // Le sexe a changé
278 
279                     Float size = source.getSize();
280                     Float weight = source.getWeight();
281                     CaracteristicQualitativeValue maturity = individualObservationsModel.getMaturityValue(source);
282                     String samplingCode = source.getSamplingCode();
283 
284                     boolean validState = source.computeValid();
285 
286                     IndividualObservationBatchRowState oldState = new IndividualObservationBatchRowState(size, weight, maturity, oldGender, samplingCode, validState);
287                     IndividualObservationBatchRowState newState = new IndividualObservationBatchRowState(size, weight, maturity, newGender, samplingCode, validState);
288                     onSamplingRowChanged(source, oldState, newState);
289 
290                 } else if (individualObservationsModel.withMaturityCaracteristic()) {
291 
292                     CaracteristicQualitativeValue oldMaturity = individualObservationsModel.getMaturityValue(oldCaracteristicMap);
293                     CaracteristicQualitativeValue newMaturity = individualObservationsModel.getMaturityValue(source);
294 
295                     if (!Objects.equals(oldMaturity, newMaturity)) {
296 
297                         // La maturité a changée
298 
299                         Float size = source.getSize();
300                         Float weight = source.getWeight();
301                         CaracteristicQualitativeValue gender = individualObservationsModel.getGender(source);
302                         String samplingCode = source.getSamplingCode();
303 
304                         boolean validState = source.computeValid();
305 
306                         IndividualObservationBatchRowState oldState = new IndividualObservationBatchRowState(size, weight, oldMaturity, gender, samplingCode, validState);
307                         IndividualObservationBatchRowState newState = new IndividualObservationBatchRowState(size, weight, newMaturity, gender, samplingCode, validState);
308                         onSamplingRowChanged(source, oldState, newState);
309 
310                     }
311 
312                 }
313 
314             } else {
315 
316                 individualObservationsModel.recomputeRowValidState(source);
317 
318             }
319 
320             model.setModify(true);
321 
322         };
323 
324         // can show / hide some columns in model
325         individualObservationTable.setColumnControlVisible(true);
326 
327         // create obsTable column model
328         DefaultTableColumnModelExt columnModel = new DefaultTableColumnModelExt();
329 
330         {
331             // Rank column
332 
333             uiHandler.addIntegerColumnToModel(columnModel,
334                                               IndividualObservationBatchTableModel.RANK,
335                                               TuttiUI.INT_10_DIGITS_PATTERN,
336                                               individualObservationTable);
337 
338         }
339 
340         { // Size column
341 
342             uiHandler.addFloatColumnToModel(columnModel,
343                                             IndividualObservationBatchTableModel.SIZE,
344                                             TuttiUI.DECIMAL3_PATTERN,
345                                             individualObservationTable);
346         }
347 
348         WeightUnit individualObservationWeightUnit = uiHandler.getConfig().getIndividualObservationWeightUnit();
349 
350         { // Weight column
351 
352             uiHandler.addFloatColumnToModel(columnModel,
353                                             IndividualObservationBatchTableModel.WEIGHT,
354                                             individualObservationWeightUnit,
355                                             individualObservationTable);
356         }
357 
358         Caracteristic sexCaracteristic = individualObservationsModel.getSexCaracteristic();
359 
360         { // sex column
361 
362             uiHandler.addCaracteristicColumnToModel(individualObservationTable,
363                                                     columnModel,
364                                                     sexCaracteristic);
365         }
366 
367         List<Caracteristic> defaultCaracteristic = new ArrayList<>(individualObservationsModel.getProtocolIndividualObservationCaracteristics());
368         // on retire le sexe puisqu'il a été ajouté avant
369         defaultCaracteristic.remove(sexCaracteristic);
370 
371         for (Caracteristic caracteristic : defaultCaracteristic) {
372             uiHandler.addCaracteristicColumnToModel(individualObservationTable,
373                                                     columnModel,
374                                                     caracteristic);
375         }
376 
377 
378         // Maturity caracteristic column
379 
380         Decorator<CaracteristicQualitativeValue> caracteristicQualitativeDecorator = uiHandler.getDecorator(CaracteristicQualitativeValue.class, null);
381         uiHandler.addComboDataColumnToModel(columnModel,
382                                             IndividualObservationBatchTableModel.MATURITY,
383                                             caracteristicQualitativeDecorator,
384                                             new ArrayList<>());
385 
386         { // Other caracteristics column
387 
388             otherCaracteristicsEditor = CaracteristicMapCellComponent.newEditor(ui, new HashSet<>());
389             uiHandler.addColumnToModel(columnModel,
390                                        otherCaracteristicsEditor,
391                                        CaracteristicMapCellComponent.newRender(uiHandler.getContext()),
392                                        IndividualObservationBatchTableModel.OTHER_CARACTERISTICS);
393 
394         }
395 
396         {
397             // Smapling code column
398 
399             uiHandler.addColumnToModel(columnModel,
400                                        SamplingCodeCellEditor.newEditor(model),
401                                        SamplingCodeCellRenderer.newRender(),
402                                        IndividualObservationBatchTableModel.SAMPLING_CODE);
403 
404         }
405 
406         { // Comment column
407 
408             uiHandler.addColumnToModel(columnModel,
409                                        CommentCellEditor.newEditor(ui),
410                                        CommentCellRenderer.newRender(),
411                                        IndividualObservationBatchTableModel.COMMENT);
412 
413         }
414 
415         { // File column
416 
417             uiHandler.addColumnToModel(columnModel,
418                                        AttachmentCellEditor.newEditor(ui),
419                                        AttachmentCellRenderer.newRender(uiHandler.getDecorator(Attachment.class, null)),
420                                        IndividualObservationBatchTableModel.ATTACHMENT);
421         }
422 
423         // create table model
424 
425         // Pour installer les listener sur chaque ligne d'observation individuelle
426         Function<IndividualObservationBatchRowModel, Void> installListenersOnRow = row -> {
427             row.addPropertyChangeListener(IndividualObservationBatchRowModel.PROPERTY_SIZE, individualObservationRowSizeChangedListener);
428             row.addPropertyChangeListener(IndividualObservationBatchRowModel.PROPERTY_WEIGHT, individualObservationRowWeightChangedListener);
429             row.addPropertyChangeListener(IndividualObservationBatchRowModel.PROPERTY_SAMPLING_CODE, individualObservationRowSamplingCodeChangedListener);
430             row.addPropertyChangeListener(IndividualObservationBatchRowModel.PROPERTY_CARACTERISTICS, individualObservationRowCaracteristicsChangedListener);
431             row.addPropertyChangeListener(IndividualObservationBatchRowModel.PROPERTY_DEFAULT_CARACTERISTICS, individualObservationRowCaracteristicsChangedListener);
432             return null;
433         };
434 
435         // Pour désinstaller les listener sur chaque ligne d'observation individuelle.
436         Function<IndividualObservationBatchRowModel, Void> uninstallListenersOnRow = row -> {
437             row.removePropertyChangeListener(IndividualObservationBatchRowModel.PROPERTY_SIZE, individualObservationRowSizeChangedListener);
438             row.removePropertyChangeListener(IndividualObservationBatchRowModel.PROPERTY_WEIGHT, individualObservationRowWeightChangedListener);
439             row.removePropertyChangeListener(IndividualObservationBatchRowModel.PROPERTY_SAMPLING_CODE, individualObservationRowSamplingCodeChangedListener);
440             row.removePropertyChangeListener(IndividualObservationBatchRowModel.PROPERTY_CARACTERISTICS, individualObservationRowCaracteristicsChangedListener);
441             row.removePropertyChangeListener(IndividualObservationBatchRowModel.PROPERTY_DEFAULT_CARACTERISTICS, individualObservationRowCaracteristicsChangedListener);
442             return null;
443         };
444         individualObservationTableModel = new IndividualObservationBatchTableModel(individualObservationWeightUnit,
445                                                                                    model,
446                                                                                    columnModel,
447                                                                                    installListenersOnRow,
448                                                                                    uninstallListenersOnRow);
449         individualObservationsModel.setIndividualObservationTableModel(individualObservationTableModel);
450 
451         individualObservationTable.setModel(individualObservationTableModel);
452         individualObservationTable.setColumnModel(columnModel);
453 
454         // by default do not authorize to change column orders
455         individualObservationTable.getTableHeader().setReorderingAllowed(false);
456 
457         uiHandler.addHighlighters(individualObservationTable);
458 
459         Color cellWithValueColor = uiHandler.getConfig().getColorCellWithValue();
460 
461         Highlighter commentHighlighter = TuttiUIUtil.newBackgroundColorHighlighter(
462                 new HighlightPredicate.AndHighlightPredicate(
463                         new HighlightPredicate.IdentifierHighlightPredicate(IndividualObservationBatchTableModel.COMMENT),
464                         // for not null value
465                         (renderer, adapter) -> {
466                             String value = (String) adapter.getValue();
467                             return StringUtils.isNotBlank(value);
468                         }), cellWithValueColor);
469         individualObservationTable.addHighlighter(commentHighlighter);
470 
471         uiHandler.installTableKeyListener(columnModel, individualObservationTable, false);
472 
473         // always scroll to selected row
474         SwingUtil.scrollToTableSelection(individualObservationTable);
475 
476         individualObservationsModel.addPropertyChangeListener(IndividualObservationBatchUIModel.PROPERTY_ROWS, evt -> individualObservationTableModel.setRows((List<IndividualObservationBatchRowModel>) evt.getNewValue()));
477 
478         // Pour mettre à jour les mensurations suite au changement du mode de recopie des observations individuelles
479         model.addPropertyChangeListener(SpeciesFrequencyUIModel.PROPERTY_COPY_INDIVIDUAL_OBSERVATION_MODE, evt -> {
480 
481             SpeciesFrequencyUIModel source = (SpeciesFrequencyUIModel) evt.getSource();
482             CopyIndividualObservationMode oldCopyMode = (CopyIndividualObservationMode) evt.getOldValue();
483             CopyIndividualObservationMode newCopyMode = (CopyIndividualObservationMode) evt.getNewValue();
484 
485             if (newCopyMode == null) {
486 
487                 return;
488             }
489             boolean nothingCopyMode = CopyIndividualObservationMode.NOTHING == newCopyMode;
490 
491             if (!nothingCopyMode) {
492 
493                 source.setFrequenciesConfigurationMode(FrequencyConfigurationMode.RAFALE);
494                 source.setAddIndividualObservationOnRafale(true);
495 
496             }
497 
498             // si on est en initialisation, ca ne sert à rien de regénérer les lignes, elles sont sensées être chargées
499             if (source.isInitBatchEdition()) {
500 
501                 if (log.isInfoEnabled()) {
502                     log.info("Skip recompute frequencies from indivudal observations (flag initBatchEdition is on), copyIndividualObservationMode changed from " + oldCopyMode + " to " + newCopyMode);
503                 }
504                 return;
505             }
506 
507             model.clear();
508             model.reloadRows();
509 
510             // On change le mode de recopie sur toutes les observation individuelles
511             individualObservationsModel.setCopyIndividualObservationMode(newCopyMode);
512 
513             // Recalcul des mensurations à partir des observations individuelles
514             frequencyTableModel.reloadRowsFromIndividualObservations();
515 
516         });
517 
518         model.addPropertyChangeListener(SpeciesFrequencyUIModel.PROPERTY_LENGTH_STEP_CARACTERISTIC, evt -> {
519 
520             Caracteristic newValue = (Caracteristic) evt.getNewValue();
521             individualObservationTableModel.setLengthstepCaracteristic(newValue);
522 
523         });
524 
525     }
526 
527     @Override
528     public void close() {
529 
530         model.getIndividualObservationUICache().close();
531         model.getSamplingCodeUICache().close();
532 
533     }
534 
535     public List<IndividualObservationBatchRowModel> loadRows(Species species, List<IndividualObservationBatchRowModel> individualObservations) {
536 
537         individualObservationTableModel.setSpecies(species);
538 
539         SamplingCodePrefix samplingCodePrefix = new SamplingCodePrefix(uiHandler.getConfig().getSamplingCodePrefix(), speciesDecorator.toString(species));
540         individualObservationsModel.setSamplingCodePrefix(samplingCodePrefix);
541 
542         return individualObservationTableModel.loadRows(individualObservations);
543 
544     }
545 
546     public void loadSpeciesBatch(SpeciesBatchRowModel speciesBatch, List<IndividualObservationBatchRowModel> individualObservationRows, boolean addToCache) {
547 
548         individualObservationsModel.setRows(individualObservationRows);
549 //        individualObservationsModel.recomputeRowsValidateState();
550 
551         model.getIndividualObservationUICache().init(speciesBatch.getSpecies(), individualObservationsModel.getRows(), addToCache);
552         model.getSamplingCodeUICache().init(speciesBatch.getSpecies(), individualObservationsModel.getRows(), addToCache);
553 
554         individualObservationTable.packAll();
555 
556     }
557 
558     public boolean isSampleCodeMenusEnabled(int modelRowIndex) {
559         return modelRowIndex >= 0 && individualObservationTable.getSelectedRowCount() == 1
560                 && individualObservationTableModel.getRows().get(individualObservationTable.getSelectedRow()).withSamplingCode();
561     }
562 
563     public void initMaturityCaracteristic(SpeciesProtocol speciesProtocol) {
564 
565         Caracteristic maturityCaracteristic;
566 
567         if (speciesProtocol == null) {
568             maturityCaracteristic = null;
569         } else {
570             maturityCaracteristic = maturityCaracteristics.get(speciesProtocol.getMaturityPmfmId());
571         }
572 
573         // Add maturity column if necessary
574 
575         individualObservationsModel.setMaturityCaracteristic(maturityCaracteristic);
576 
577         MaturityColumnIdentifier columnIdentifier = IndividualObservationBatchTableModel.MATURITY;
578         columnIdentifier.setCaracteristic(maturityCaracteristic);
579 
580         TableColumnExt columnModel = ((TableColumnModelExt) individualObservationTable.getColumnModel()).getColumnExt(columnIdentifier);
581 
582         boolean noMaturity = maturityCaracteristic == null;
583 
584         columnModel.setVisible(!noMaturity);
585         ComboBoxCellEditor cellEditor = (ComboBoxCellEditor) columnModel.getCellEditor();
586         JComboBox comboBox = (JComboBox) cellEditor.getComponent();
587 
588         List<CaracteristicQualitativeValue> dataList;
589 
590         String title;
591         String tip;
592 
593         if (noMaturity) {
594             dataList = Collections.emptyList();
595             title = t(columnIdentifier.getHeaderI18nKey());
596             tip = t(columnIdentifier.getHeaderTipI18nKey());
597 
598         } else {
599             dataList = new ArrayList<>(maturityCaracteristic.getQualitativeValue());
600             if (!dataList.isEmpty() && dataList.get(0) != null) {
601                 dataList.add(0, null);
602             }
603             title = caracteristicDecorator.toString(maturityCaracteristic);
604             tip = caracteristicTipDecorator.toString(maturityCaracteristic);
605         }
606         columnModel.setTitle(title);
607         columnModel.setToolTipText(tip);
608         SwingUtil.fillComboBox(comboBox, dataList, null);
609 
610     }
611 
612     public void initDefaultCaracteristics(SpeciesBatchRowModel speciesBatch) {
613 
614         // compute the default caracteristics
615         Set<Caracteristic> defaultCaracteristics = new HashSet<>();
616         defaultCaracteristics.addAll(individualObservationsModel.getProtocolIndividualObservationCaracteristics());
617         defaultCaracteristics.add(individualObservationsModel.getSexCaracteristic());
618         if (individualObservationsModel.withMaturityCaracteristic()) {
619             defaultCaracteristics.add(individualObservationsModel.getMaturityCaracteristic());
620         }
621         // update the caracteristics which are not editable in the other caracteristic editor
622         otherCaracteristicsEditor.setCaracteristicsToSkip(defaultCaracteristics);
623 
624         // create the default caracteristic map
625         CaracteristicMap defaultCaracteristicMap = CaracteristicMap.fromCollection(defaultCaracteristics);
626 
627         // and put the values of the sampling categories, if the categories are in the map
628         CaracteristicMap sampleCategoryValues = new CaracteristicMap();
629         Iterator<SampleCategory<?>> iterator = speciesBatch.iterator();
630         iterator.forEachRemaining(sampleCategory -> {
631             Caracteristic caracteristic = sampleCategory.getCategoryDef().getCaracteristic();
632             defaultCaracteristicMap.replace(caracteristic, sampleCategory.getCategoryValue());
633             sampleCategoryValues.put(caracteristic, sampleCategory.getCategoryValue());
634         });
635 
636         individualObservationTableModel.setDefaultCaracteristics(defaultCaracteristicMap);
637         individualObservationsModel.setNotEditableCaracteristic(sampleCategoryValues.keySet());
638 
639     }
640 
641     private void onSamplingRowChanged(IndividualObservationBatchRowModel row,
642                                       IndividualObservationBatchRowState oldValue,
643                                       IndividualObservationBatchRowState newValue) {
644 
645         boolean needUpdateSamplingNotificationZone = individualObservationToSamplingCacheEngine.computeSamplingCacheUpdate(oldValue, newValue);
646 
647         if (needUpdateSamplingNotificationZone) {
648 
649             // recalcul de la zone de notification
650             individualObservationsModel.getSamplingNotificationZoneModel().setUpdatedRow(row);
651 
652         }
653 
654     }
655 
656     private void onRowChangedForFrequencies(IndividualObservationBatchRowModel row,
657                                             IndividualObservationBatchRowState oldValue,
658                                             IndividualObservationBatchRowState newValue) {
659 
660         boolean needRecomputeRowsValidState = individualObservationToFrequencyEngine.computeFrequencyUpdate(model.getCopyIndividualObservationMode(), oldValue, newValue);
661 
662         if (needRecomputeRowsValidState) {
663 
664             // l'état de validité de la ligne a changé, on recalcule les lignes en erreurs
665             individualObservationsModel.recomputeRowValidState(row);
666 
667         }
668 
669     }
670 
671 }
672