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 com.google.common.base.Preconditions;
28  import fr.ifremer.tutti.persistence.entities.data.SpeciesBatch;
29  import fr.ifremer.tutti.persistence.entities.protocol.CalcifiedPiecesSamplingDefinition;
30  import fr.ifremer.tutti.persistence.entities.protocol.Zone;
31  import fr.ifremer.tutti.persistence.entities.referential.Species;
32  import fr.ifremer.tutti.service.DecoratorService;
33  import fr.ifremer.tutti.service.sampling.CalcifiedPiecesSamplingAlgorithmEntryNotFoundException;
34  import fr.ifremer.tutti.service.sampling.IndividualObservationSamplingContext;
35  import fr.ifremer.tutti.service.sampling.IndividualObservationSamplingStatus;
36  import fr.ifremer.tutti.service.sampling.SizeNotDefinedOnIndividualObservationException;
37  import fr.ifremer.tutti.service.sampling.ZoneNotDefinedOnFishingOperationException;
38  import org.apache.commons.logging.Log;
39  import org.apache.commons.logging.LogFactory;
40  import org.nuiton.decorator.Decorator;
41  
42  import javax.swing.JLabel;
43  import javax.swing.ListSelectionModel;
44  import javax.swing.event.ListSelectionListener;
45  import java.awt.Color;
46  import java.beans.PropertyChangeListener;
47  import java.io.Closeable;
48  import java.util.Objects;
49  
50  import static org.nuiton.i18n.I18n.t;
51  
52  /**
53   * Pour gérer la zone de notification des observations individuelles.
54   *
55   * Created on 18/04/16.
56   *
57   * @author Tony Chemit - chemit@codelutin.com
58   */
59  public class SamplingNotificationZoneHandler implements Closeable {
60  
61      /** Logger. */
62      private static final Log log = LogFactory.getLog(SamplingNotificationZoneHandler.class);
63  
64      private final SpeciesFrequencyUIModel uiModel;
65      private final SamplingNotificationZoneModel model;
66      private final IndividualObservationBatchTableModel individualObservationTableModel;
67      private final ListSelectionModel listSelectionModel;
68  
69      private final IndividualObservationUICache individualObservationUICache;
70  
71      private final JLabel samplingWarningLabel;
72      private final JLabel samplingSummaryLabel;
73  
74      private final ListSelectionListener listSelectionListener;
75      private final PropertyChangeListener modelSummaryTextChanged;
76      private final PropertyChangeListener modelSelectedRowChanged;
77      private final PropertyChangeListener modelUpdatedRowChanged;
78      private final PropertyChangeListener modelStatusChanged;
79  
80      private final Decorator<Integer> infiniteDecorator;
81      private final Decorator<Species> speciesDecorator;
82      private final Decorator<Zone> zoneDecorator;
83  
84      private final Color colorHighlightInfoForeground;
85      private final Color colorHighlightInfoBackground;
86  
87      public SamplingNotificationZoneHandler(SpeciesFrequencyUI ui, SamplingNotificationZoneModel model, IndividualObservationUICache individualObservationUICache) {
88          this.uiModel = ui.getModel();
89          this.samplingWarningLabel = ui.getSamplingWarningLabel();
90          this.samplingSummaryLabel = ui.getSamplingResumeLabel();
91          this.individualObservationTableModel = (IndividualObservationBatchTableModel) ui.getObsTable().getModel();
92          this.model = model;
93          this.listSelectionModel = ui.getObsTable().getSelectionModel();
94  
95          SpeciesFrequencyUIHandler uiHandler = ui.getHandler();
96          this.individualObservationUICache = individualObservationUICache;
97          this.infiniteDecorator = uiHandler.getDecorator(Integer.class, DecoratorService.NULL_INFINITE);
98          this.speciesDecorator = uiHandler.getDecorator(Species.class, DecoratorService.WITH_SURVEY_CODE);
99          this.zoneDecorator = uiHandler.getDecorator(Zone.class, null);
100         this.colorHighlightInfoForeground = uiHandler.getConfig().getColorHighlightInfoForeground();
101         this.colorHighlightInfoBackground = uiHandler.getConfig().getColorHighlightInfoBackground();
102 
103         // Ecoute le changement d'état de la zone de notification pour mettre à jour le statut
104         this.modelStatusChanged = event -> {
105 
106             SamplingNotificationZoneStatus newValue = (SamplingNotificationZoneStatus) event.getNewValue();
107 
108             switch (newValue) {
109 
110                 case DISABLED:
111 
112                     samplingWarningLabel.setText(t("tutti.editSpeciesFrequencies.samplingNotification.warning.samplingDisabled"));
113                     samplingWarningLabel.setForeground(null);
114                     samplingWarningLabel.setBackground(Color.LIGHT_GRAY);
115 
116                     break;
117                 case NOT_USING_SAMPLING:
118 
119                     samplingWarningLabel.setText(t("tutti.editSpeciesFrequencies.samplingNotification.warning.samplingNotUsed"));
120                     samplingWarningLabel.setForeground(null);
121                     samplingWarningLabel.setBackground(Color.LIGHT_GRAY);
122 
123                     break;
124                 case NEED_SAMPLING:
125 
126                     samplingWarningLabel.setText(t("tutti.editSpeciesFrequencies.samplingNotification.warning.samplingNeeded"));
127                     samplingWarningLabel.setForeground(colorHighlightInfoForeground);
128                     samplingWarningLabel.setBackground(colorHighlightInfoBackground);
129 
130                     break;
131                 case NO_SAMPLING_REQUIRED:
132 
133                     samplingWarningLabel.setText(t("tutti.editSpeciesFrequencies.samplingNotification.warning.noSamplingRequired"));
134                     samplingWarningLabel.setForeground(null);
135                     samplingWarningLabel.setBackground(Color.LIGHT_GRAY);
136 
137                     break;
138                 case COUNT_ATTAINED:
139 
140                     samplingWarningLabel.setText(t("tutti.editSpeciesFrequencies.samplingNotification.warning.samplingTotalCountAttained"));
141                     samplingWarningLabel.setForeground(null);
142                     samplingWarningLabel.setBackground(Color.LIGHT_GRAY);
143 
144                     break;
145 
146                 case NONE:
147 
148                     samplingWarningLabel.setText(null);
149                     samplingWarningLabel.setForeground(null);
150                     samplingWarningLabel.setBackground(null);
151 
152                     break;
153             }
154         };
155 
156         // Ecoute le changement du texte de la zone de notification pour mettre à jour l'ui
157         this.modelSummaryTextChanged = event -> {
158 
159             String newValue = (String) event.getNewValue();
160             samplingSummaryLabel.setText(newValue);
161 
162         };
163 
164         // Ecoute le changement de sélection d'une observation individuelle pour demander au cache le statut de celle-ci
165         this.modelSelectedRowChanged = event -> {
166 
167             IndividualObservationBatchRowModel selectedRow = (IndividualObservationBatchRowModel) event.getNewValue();
168 
169             if (selectedRow == null) {
170 
171                 hideAll();
172                 return;
173             }
174 
175             IndividualObservationSamplingStatus status = getIndividualObservationSamplingStatus(selectedRow);
176 
177             if (status != null) {
178 
179                 // mise à jour du résumé
180                 String summaryText = getSummaryText(status);
181                 model.setSummaryText(summaryText);
182 
183                 // mise à jour du statut de l'observation individuelle
184                 SamplingNotificationZoneStatus samplingNotificationZoneStatus;
185 
186                 if (status.isOneTotalCountIsAttained()) {
187 
188                     // plus besoin de prélever (max atteint)
189                     samplingNotificationZoneStatus = SamplingNotificationZoneStatus.COUNT_ATTAINED;
190                 } else if (status.isNotUsingSampling()) {
191 
192                     // ne jamais prélever (max à 0)
193                     samplingNotificationZoneStatus = SamplingNotificationZoneStatus.NOT_USING_SAMPLING;
194                 } else {
195 
196                     // rien à afficher (en mode sélection, on ne peut pas dire s'il faut ou non prélever)
197                     samplingNotificationZoneStatus = SamplingNotificationZoneStatus.NONE;
198                 }
199 
200                 model.setSamplingNotificationZoneStatus(samplingNotificationZoneStatus);
201 
202             }
203 
204         };
205 
206         // Ecoute le changement de modification d'une observation individuelle pour demander au cache le statut de celle-ci
207         this.modelUpdatedRowChanged = event -> {
208 
209             IndividualObservationBatchRowModel updatedRow = (IndividualObservationBatchRowModel) event.getNewValue();
210 
211             Objects.requireNonNull(updatedRow);
212 
213             // récupération du status de l'observation individuelle
214             IndividualObservationSamplingStatus status = getIndividualObservationSamplingStatus(updatedRow);
215 
216             if (status != null) {
217 
218                 // mise à jour du résumé
219                 String summaryText = getSummaryText(status);
220                 model.setSummaryText(summaryText);
221 
222                 // mise à jour du statut de l'observation individuelle
223                 SamplingNotificationZoneStatus samplingNotificationZoneStatus;
224 
225                 if (status.isNeedSampling()) {
226 
227                     // demande de prélèvement, bingo!
228                     samplingNotificationZoneStatus = SamplingNotificationZoneStatus.NEED_SAMPLING;
229                 } else if (status.isOneTotalCountIsAttained()) {
230 
231                     // plus besoin de prélever (max atteint)
232                     samplingNotificationZoneStatus = SamplingNotificationZoneStatus.COUNT_ATTAINED;
233                 } else if (status.isNotUsingSampling()) {
234 
235                     // ne jamais prélever (max à 0)
236                     samplingNotificationZoneStatus = SamplingNotificationZoneStatus.NOT_USING_SAMPLING;
237                 } else if (updatedRow.withSamplingCode()) {
238 
239                     // l'observation a un code de prélèvement, on n'affiche pas de notification
240                     samplingNotificationZoneStatus = SamplingNotificationZoneStatus.NONE;
241                 } else {
242 
243                     // pas de prélèvement requis (samplingInterval)
244                     samplingNotificationZoneStatus = SamplingNotificationZoneStatus.NO_SAMPLING_REQUIRED;
245                 }
246 
247                 model.setSamplingNotificationZoneStatus(samplingNotificationZoneStatus);
248 
249             }
250 
251         };
252 
253         // Ecoute la sélection sur le tableau des observations individuelles pour rafraîchir la zone de notification
254         this.listSelectionListener = event -> {
255 
256             ListSelectionModel source = (ListSelectionModel) event.getSource();
257 
258             if (model.isValueAdjusting()) {
259 
260                 // évènement non terminée (en cours d'ajustement)
261                 return;
262             }
263 
264             if (event.getValueIsAdjusting()) {
265 
266                 // évènement non terminée (en cours d'ajustement)
267                 return;
268             }
269 
270             if (source.isSelectionEmpty()) {
271 
272                 // on supprime la sélection dans le modèle
273                 model.setSelectedRow(null);
274                 return;
275             }
276 
277             if (getSelectedRowCount(source) > 1) {
278 
279                 // plusieurs lignes sélectionnées, on ne peut pas afficher d'informations
280                 hideAll();
281                 return;
282             }
283 
284             IndividualObservationBatchRowModel selectedRow = individualObservationTableModel.getEntry(source.getMinSelectionIndex());
285             model.setSelectedRow(selectedRow);
286 
287         };
288 
289     }
290 
291     public void editBatch(SpeciesBatch speciesBatch) {
292 
293         if (log.isInfoEnabled()) {
294             log.info("Edit batch for " + speciesBatch);
295         }
296 
297         // toujours supprimer les listeners
298         listSelectionModel.removeListSelectionListener(listSelectionListener);
299         model.removePropertyChangeListener(SamplingNotificationZoneModel.PROPERTY_SELECTED_ROW, modelSelectedRowChanged);
300         model.removePropertyChangeListener(SamplingNotificationZoneModel.PROPERTY_UPDATED_ROW, modelUpdatedRowChanged);
301         model.removePropertyChangeListener(SamplingNotificationZoneModel.PROPERTY_SAMPLING_NOTIFICATION_ZONE_STATUS, modelStatusChanged);
302         model.removePropertyChangeListener(SamplingNotificationZoneModel.PROPERTY_SUMMARY_TEXT, modelSummaryTextChanged);
303 
304         // on les ajoutent (on peut en avoir besoin pour désactiver la zone de notification)
305         model.addPropertyChangeListener(SamplingNotificationZoneModel.PROPERTY_SELECTED_ROW, modelSelectedRowChanged);
306         model.addPropertyChangeListener(SamplingNotificationZoneModel.PROPERTY_UPDATED_ROW, modelUpdatedRowChanged);
307         model.addPropertyChangeListener(SamplingNotificationZoneModel.PROPERTY_SAMPLING_NOTIFICATION_ZONE_STATUS, modelStatusChanged);
308         model.addPropertyChangeListener(SamplingNotificationZoneModel.PROPERTY_SUMMARY_TEXT, modelSummaryTextChanged);
309 
310         if (!uiModel.isProtocolFilled()) {
311 
312             // pas de protocole
313             stopUsingStatusNotication(t("tutti.editSpeciesFrequencies.samplingNotification.warning.noProtocol"));
314             return;
315         }
316 
317         if (!uiModel.isProtocolUseCalcifiedPieceSampling()) {
318 
319             // pas d'utilisation de l'algorithme de prélèvement des pièces calcifiées
320             stopUsingStatusNotication(t("tutti.editSpeciesFrequencies.samplingNotification.warning.notUsingAlgorithm"));
321             return;
322         }
323 
324         if (!individualObservationUICache.isFishingOperationWithZone()) {
325 
326             // pas de zone définie sur l'opération de pêche
327             stopUsingStatusNotication(t("tutti.editSpeciesFrequencies.samplingNotification.warning.fishingOperationNotInAZone"));
328             return;
329         }
330 
331         if (!individualObservationUICache.isSpeciesDefinedInCalcifiedPiecesSampling()) {
332 
333             // pas de définition d'algorithme sur cette espèce
334             stopUsingStatusNotication(t("tutti.editSpeciesFrequencies.samplingNotification.warning.speciesNotInAlgorithm"));
335             return;
336         }
337 
338         Preconditions.checkState(individualObservationUICache.useCruiseSamplingCache());
339 
340         model.setSelectedRow(null);
341 
342         listSelectionModel.addListSelectionListener(listSelectionListener);
343 
344     }
345 
346     @Override
347     public void close() {
348 
349         listSelectionModel.removeListSelectionListener(listSelectionListener);
350         model.removePropertyChangeListener(SamplingNotificationZoneModel.PROPERTY_SELECTED_ROW, modelSelectedRowChanged);
351         model.removePropertyChangeListener(SamplingNotificationZoneModel.PROPERTY_UPDATED_ROW, modelUpdatedRowChanged);
352         model.removePropertyChangeListener(SamplingNotificationZoneModel.PROPERTY_SAMPLING_NOTIFICATION_ZONE_STATUS, modelStatusChanged);
353         model.removePropertyChangeListener(SamplingNotificationZoneModel.PROPERTY_SUMMARY_TEXT, modelSummaryTextChanged);
354 
355     }
356 
357     private void hideAll() {
358 
359         model.setSummaryText(null);
360         model.setSamplingNotificationZoneStatus(SamplingNotificationZoneStatus.NONE);
361 
362     }
363 
364     private void whenCanNotUseSampling(String message) {
365 
366         model.setSummaryText(message);
367         model.setSamplingNotificationZoneStatus(SamplingNotificationZoneStatus.DISABLED);
368 
369     }
370 
371     private void stopUsingStatusNotication(String message) {
372 
373         whenCanNotUseSampling(message);
374         model.removePropertyChangeListener(SamplingNotificationZoneModel.PROPERTY_SELECTED_ROW, modelSelectedRowChanged);
375         model.removePropertyChangeListener(SamplingNotificationZoneModel.PROPERTY_UPDATED_ROW, modelUpdatedRowChanged);
376         model.removePropertyChangeListener(SamplingNotificationZoneModel.PROPERTY_SAMPLING_NOTIFICATION_ZONE_STATUS, modelStatusChanged);
377         model.removePropertyChangeListener(SamplingNotificationZoneModel.PROPERTY_SUMMARY_TEXT, modelSummaryTextChanged);
378 
379     }
380 
381     private String getSummaryText(IndividualObservationSamplingStatus status) {
382 
383         IndividualObservationSamplingContext individualObservationSamplingContext = status.getIndividualObservationSamplingContext();
384 
385         CalcifiedPiecesSamplingDefinition calcifiedPiecesSamplingDefinition = status.getCalcifiedPiecesSamplingDefinition();
386 
387         int nbForOperation = status.getSamplingCountInFishingOperation();
388         int nbForZone = status.getSamplingCountInZone();
389         int nbForCruise = status.getSamplingCountInCruise();
390 
391         String nbForOperationLabel = getLabelForSamplingNumber(nbForOperation, calcifiedPiecesSamplingDefinition.getOperationLimitation());
392         String nbForZoneLabel = getLabelForSamplingNumber(nbForZone, calcifiedPiecesSamplingDefinition.getZoneLimitation());
393         String nbForCruiseLabel = getLabelForSamplingNumber(nbForCruise, calcifiedPiecesSamplingDefinition.getMaxByLenghtStep());
394 
395         String key = speciesDecorator.toString(individualObservationSamplingContext.getSpecies())
396                 + " " + uiModel.convertFromMm(individualObservationSamplingContext.getLengthStep())
397                 + " " + uiModel.getLengthStepCaracteristicUnit();
398         if (individualObservationSamplingContext.withGender()) {
399             key += " " + individualObservationSamplingContext.getGender().getDescription();
400         }
401         if (individualObservationSamplingContext.withMaturity()) {
402             if (individualObservationSamplingContext.getMaturity()) {
403                 key += " " + t("tutti.editSpeciesFrequencies.samplingNeeded.mature");
404             } else {
405                 key += " " + t("tutti.editSpeciesFrequencies.samplingNeeded.immature");
406             }
407         }
408 
409         String zone = zoneDecorator.toString(individualObservationSamplingContext.getZone());
410 
411         return t("tutti.editSpeciesFrequencies.samplingNeeded.summary", key, nbForOperationLabel, zone, nbForZoneLabel, nbForCruiseLabel);
412 
413     }
414 
415     /**
416      * Returns the number of selected rows.
417      *
418      * @return the number of selected rows, 0 if no rows are selected
419      */
420     private int getSelectedRowCount(ListSelectionModel selectionModel) {
421         int iMin = selectionModel.getMinSelectionIndex();
422         int iMax = selectionModel.getMaxSelectionIndex();
423         int count = 0;
424 
425         for (int i = iMin; i <= iMax; i++) {
426             if (selectionModel.isSelectedIndex(i)) {
427                 count++;
428             }
429         }
430         return count;
431     }
432 
433     private String getLabelForSamplingNumber(int value, Integer max) {
434         return "<strong>" + infiniteDecorator.toString(value) + "</strong> (" + infiniteDecorator.toString(max) + ")";
435     }
436 
437     private IndividualObservationSamplingStatus getIndividualObservationSamplingStatus(IndividualObservationBatchRowModel selectedRow) {
438 
439         IndividualObservationSamplingStatus individualObservationSamplingStatus = null;
440 
441         try {
442 
443             individualObservationSamplingStatus = individualObservationUICache.getIndividualObservationSamplingStatus(selectedRow);
444 
445         } catch (CalcifiedPiecesSamplingAlgorithmEntryNotFoundException e) {
446             whenCanNotUseSampling(t("tutti.editSpeciesFrequencies.samplingNotification.warning.noAlgorithmEntry"));
447         } catch (SizeNotDefinedOnIndividualObservationException e) {
448             whenCanNotUseSampling(t("tutti.editSpeciesFrequencies.samplingNotification.warning.sizeNotDefined"));
449         } catch (ZoneNotDefinedOnFishingOperationException e) {
450             whenCanNotUseSampling(t("tutti.editSpeciesFrequencies.samplingNotification.warning.fishingOperationNotInAZone"));
451         }
452 
453         return individualObservationSamplingStatus;
454 
455     }
456 
457 }