View Javadoc
1   package fr.ifremer.tutti.ui.swing.content.operation.catches.species.split;
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.collect.Maps;
26  import fr.ifremer.tutti.persistence.entities.data.SampleCategory;
27  import fr.ifremer.tutti.persistence.entities.data.SampleCategoryModel;
28  import fr.ifremer.tutti.persistence.entities.data.SampleCategoryModelEntry;
29  import fr.ifremer.tutti.persistence.entities.referential.Caracteristic;
30  import fr.ifremer.tutti.persistence.entities.referential.CaracteristicQualitativeValue;
31  import fr.ifremer.tutti.ui.swing.content.operation.catches.species.EditSpeciesBatchPanelUI;
32  import fr.ifremer.tutti.ui.swing.content.operation.catches.species.SpeciesOrBenthosBatchUISupport;
33  import fr.ifremer.tutti.ui.swing.content.operation.catches.species.edit.SpeciesBatchRowModel;
34  import fr.ifremer.tutti.ui.swing.util.TuttiBeanMonitor;
35  import fr.ifremer.tutti.ui.swing.util.TuttiUI;
36  import fr.ifremer.tutti.ui.swing.util.table.AbstractTuttiTableUIHandler;
37  import jaxx.runtime.validator.swing.SwingValidator;
38  import org.apache.commons.logging.Log;
39  import org.apache.commons.logging.LogFactory;
40  import org.jdesktop.swingx.JXTable;
41  import org.jdesktop.swingx.table.DefaultTableColumnModelExt;
42  
43  import javax.swing.DefaultComboBoxModel;
44  import javax.swing.JComboBox;
45  import javax.swing.JComponent;
46  import java.awt.event.ActionEvent;
47  import java.awt.event.ActionListener;
48  import java.beans.PropertyChangeEvent;
49  import java.beans.PropertyChangeListener;
50  import java.io.Serializable;
51  import java.util.ArrayList;
52  import java.util.List;
53  import java.util.Map;
54  import java.util.Objects;
55  import java.util.Optional;
56  
57  /**
58   * Handler of {@link SplitSpeciesBatchUI}.
59   *
60   * @author Tony Chemit - chemit@codelutin.com
61   * @since 0.3
62   */
63  public class SplitSpeciesBatchUIHandler extends AbstractTuttiTableUIHandler<SplitSpeciesBatchRowModel, SplitSpeciesBatchUIModel, SplitSpeciesBatchUI> {
64  
65      /** Logger. */
66      private static final Log log =
67              LogFactory.getLog(SplitSpeciesBatchUIHandler.class);
68  
69      protected final PropertyChangeListener PROPERTY_CATEGORY_CHANGED_LISTENER = new PropertyChangeListener() {
70  
71          @Override
72          public void propertyChange(PropertyChangeEvent evt) {
73  
74              SplitSpeciesBatchUIModel source =
75                      (SplitSpeciesBatchUIModel) evt.getSource();
76  
77              // unselect previous selected category
78              source.setSelectedCategory(null);
79  
80              // fill comboBox with new list
81              List<SampleCategoryModelEntry> data = (List<SampleCategoryModelEntry>) evt.getNewValue();
82              SplitSpeciesBatchUIHandler.this.ui.getCategoryComboBox().setModel(new DefaultComboBoxModel(data.toArray()));
83          }
84      };
85  
86      protected final PropertyChangeListener PROPERTY_SELECTED_CATEGORY_CHANGED_LISTENER = new PropertyChangeListener() {
87  
88          @Override
89          public void propertyChange(PropertyChangeEvent evt) {
90  
91              SplitSpeciesBatchUIModel source =
92                      (SplitSpeciesBatchUIModel) evt.getSource();
93  
94              // when selected category change, sample total weight is reset
95              source.setSampleWeight(null);
96  
97              SampleCategoryModelEntry newValue =
98                      (SampleCategoryModelEntry) evt.getNewValue();
99              generateTableModel(newValue);
100         }
101     };
102 
103     protected final ActionListener CATEGORY_ACTION_LISTENER = new ActionListener() {
104 
105         public void actionPerformed(ActionEvent e) {
106             JComboBox comboBox = (JComboBox) e.getSource();
107             getModel().setSelectedCategory((SampleCategoryModelEntry) comboBox.getSelectedItem());
108         }
109     };
110 
111     protected final PropertyChangeListener PROPERTY_WEIGHT_CHANGED_LISTENER = new PropertyChangeListener() {
112 
113         @Override
114         public void propertyChange(PropertyChangeEvent evt) {
115             if (evt.getNewValue() != null) {
116                 ((SplitSpeciesBatchRowModel) evt.getSource()).setSelected(true);
117             }
118         }
119     };
120 
121 //    protected SpeciesOrBenthosBatchUISupport speciesOrBenthosBatchUISupport;
122 
123     public SplitSpeciesBatchUIHandler() {
124         super(SplitSpeciesBatchRowModel.PROPERTY_SELECTED,
125               SplitSpeciesBatchRowModel.PROPERTY_CATEGORY_VALUE,
126               SplitSpeciesBatchRowModel.PROPERTY_WEIGHT);
127     }
128 
129     //------------------------------------------------------------------------//
130     //-- AbstractTuttiTableUIHandler methods                                --//
131     //------------------------------------------------------------------------//
132 
133     @Override
134     public SplitSpeciesBatchTableModel getTableModel() {
135         return (SplitSpeciesBatchTableModel) getTable().getModel();
136     }
137 
138     @Override
139     public JXTable getTable() {
140         return ui.getTable();
141     }
142 
143     @Override
144     protected boolean isRowValid(SplitSpeciesBatchRowModel row) {
145         return row.isSelected();
146     }
147 
148     @Override
149     protected void saveSelectedRowIfRequired(TuttiBeanMonitor<SplitSpeciesBatchRowModel> rowMonitor,
150                                              SplitSpeciesBatchRowModel row) {
151         if (rowMonitor.wasModified()) {
152 
153             if (row.isValid()) {
154                 if (log.isInfoEnabled()) {
155                     log.info("Change row that was modified and valid");
156                 }
157             }
158 
159             rowMonitor.clearModified();
160         }
161     }
162 
163     @Override
164     protected void onAfterSelectedRowChanged(int oldRowIndex,
165                                              SplitSpeciesBatchRowModel oldRow,
166                                              int newRowIndex,
167                                              SplitSpeciesBatchRowModel newRow) {
168         super.onAfterSelectedRowChanged(oldRowIndex, oldRow, newRowIndex, newRow);
169         if (newRow != null) {
170 
171             // Recompute the valid state of the row
172             recomputeRowValidState(newRow);
173 
174             // Need to recompute the sample weight
175             computeSampleWeight();
176         }
177     }
178 
179     @Override
180     protected void onRowModified(int rowIndex,
181                                  SplitSpeciesBatchRowModel row,
182                                  String propertyName,
183                                  Object oldValue,
184                                  Object newValue) {
185 
186         // Recompute the valid state of the row
187         recomputeRowValidState(row);
188 
189         // Need to recompute the sample weight
190         computeSampleWeight();
191     }
192 
193     //------------------------------------------------------------------------//
194     //-- AbstractTuttiUIHandler methods                                     --//
195     //------------------------------------------------------------------------//
196 
197     @Override
198     public SwingValidator<SplitSpeciesBatchUIModel> getValidator() {
199         return ui.getValidator();
200     }
201 
202     @Override
203     public void beforeInit(SplitSpeciesBatchUI ui) {
204         super.beforeInit(ui);
205         SampleCategoryModel sampleCategoryModel = getDataContext().getSampleCategoryModel();
206 
207         SpeciesOrBenthosBatchUISupport speciesOrBenthosBatchUISupport = ui.getContextValue(SpeciesOrBenthosBatchUISupport.class, ui.getSpeciesOrBenthosContext());
208 
209         SplitSpeciesBatchUIModel model = new SplitSpeciesBatchUIModel(speciesOrBenthosBatchUISupport, sampleCategoryModel);
210 
211         ui.setContextValue(model);
212     }
213 
214     @Override
215     public void afterInit(SplitSpeciesBatchUI ui) {
216 
217         initUI(ui);
218 
219         SplitSpeciesBatchUIModel model = getModel();
220 
221         // when category changed, remove selected category
222         model.addPropertyChangeListener(SplitSpeciesBatchUIModel.PROPERTY_CATEGORY, PROPERTY_CATEGORY_CHANGED_LISTENER);
223 
224         // when selected category changed, regenerate the table model + add inside some default rows
225 
226         model.addPropertyChangeListener(SplitSpeciesBatchUIModel.PROPERTY_SELECTED_CATEGORY, PROPERTY_SELECTED_CATEGORY_CHANGED_LISTENER);
227 
228         ui.getCategoryComboBox().setRenderer(newListCellRender(SampleCategoryModelEntry.class));
229 
230         ui.getCategoryComboBox().addActionListener(CATEGORY_ACTION_LISTENER);
231 
232         generateTableModel(null);
233 
234         initTable(getTable());
235 
236         listenValidatorValid(ui.getValidator(), model);
237     }
238 
239     @Override
240     protected JComponent getComponentToFocus() {
241         return getUI().getCategoryComboBox();
242     }
243 
244     @Override
245     public void onCloseUI() {
246         if (log.isDebugEnabled()) {
247             log.debug("closing: " + ui);
248         }
249 
250         // evict model from validator
251         ui.getValidator().setBean(null);
252 
253         clearValidators();
254 
255         // when canceling always invalid model
256         getModel().setValid(false);
257         getModel().setSelectedCategory(null);
258 
259         EditSpeciesBatchPanelUI parent = getParentContainer(EditSpeciesBatchPanelUI.class);
260         parent.switchToEditBatch();
261 
262     }
263 
264     //------------------------------------------------------------------------//
265     //-- Public methods                                                     --//
266     //------------------------------------------------------------------------//
267 
268     public void editBatch(SpeciesBatchRowModel batch) {
269 
270         // get possible the last used
271         List<SampleCategoryModelEntry> categories = new ArrayList<>();
272 
273         SplitSpeciesBatchUIModel model = getModel();
274 
275         SampleCategoryModelEntry bestSampleCategory = null;
276         if (batch != null) {
277 
278             // get sample category model
279             SampleCategoryModel sampleCategoryModel = getModel().getSampleCategoryModel();
280 
281             // set all categories (the no more available will be removed later)
282             categories.addAll(sampleCategoryModel.getCategory());
283 
284             SampleCategory<?> lastCategory = batch.getFinestCategory();
285 
286             Objects.requireNonNull(lastCategory, "Can't split a species batch with no sample category.");
287 
288             int firstOrder = lastCategory.getCategoryDef().getOrder();
289 
290             for (Integer sampleCategoryId : sampleCategoryModel.getSamplingOrder()) {
291                 SampleCategory<?> sampleCategory = batch.getSampleCategoryById(sampleCategoryId);
292                 int order = sampleCategory.getCategoryDef().getOrder();
293                 if (order < firstOrder || sampleCategory.isValid()) {
294 
295                     // remove category if before the finest one
296                     // remove category if not filled
297                     categories.remove(sampleCategory.getCategoryDef());
298                 }
299             }
300 
301             bestSampleCategory = model.getSpeciesOrBenthosBatchUISupport().getBestFirstSampleCategory(categories, batch.getSpecies());
302         }
303 
304         // connect model to validator
305         ui.getValidator().setBean(model);
306 
307         model.setSampleWeight(null);
308         model.setCategory(categories);
309         model.setSelectedCategory(bestSampleCategory);
310 
311         // keep batch (will be used to push back editing entry)
312         model.setBatch(batch);
313     }
314 
315     public void editBatch(SpeciesBatchRowModel batch, int sampleCategoryId) {
316 
317         Objects.requireNonNull(batch);
318         Objects.requireNonNull(sampleCategoryId);
319 
320         List<SpeciesBatchRowModel> rows = batch.getChildBatch();
321         Objects.requireNonNull(rows);
322 
323         // get possible the last used
324         List<SampleCategoryModelEntry> categories = new ArrayList<>();
325 
326         // get sample category model
327         SampleCategoryModel sampleCategoryModel = getModel().getSampleCategoryModel();
328 
329         // set only the given category
330         SampleCategoryModelEntry selectedCategory = sampleCategoryModel.getCategoryById(sampleCategoryId);
331         categories.add(selectedCategory);
332 
333         SplitSpeciesBatchUIModel model = getModel();
334 
335         // connect model to validator
336         ui.getValidator().setBean(model);
337 
338         model.setSampleWeight(null);
339         model.setCategory(categories);
340         model.setSelectedCategory(selectedCategory);
341 
342         // keep batch (will be used to push back editing entry)
343         model.setBatch(batch);
344 
345         // add existing rows
346         Map<Serializable, SplitSpeciesBatchRowModel> rowsByValue = Maps.uniqueIndex(model.getRows(), SplitSpeciesBatchRowModel::getCategoryValue);
347         for (SpeciesBatchRowModel row : rows) {
348 
349             SampleCategory<?> sampleCategory = row.getSampleCategoryById(sampleCategoryId);
350             Serializable categoryValue = sampleCategory.getCategoryValue();
351             SplitSpeciesBatchRowModel splitRow = rowsByValue.get(categoryValue);
352             splitRow.setWeight(sampleCategory.getCategoryWeight());
353             splitRow.setSelected(true);
354             splitRow.setEditable(false);
355         }
356 
357         computeSampleWeight();
358 
359         getTableModel().fireTableDataChanged();
360     }
361 
362     //------------------------------------------------------------------------//
363     //-- Internal methods                                                   --//
364     //------------------------------------------------------------------------//
365 
366     protected void computeSampleWeight() {
367 
368         Optional<Float> result = getTableModel().getTotalWeight();
369 //        List<SplitSpeciesBatchRowModel> rows = getTableModel().getRows();
370 //        for (SplitSpeciesBatchRowModel row : rows) {
371 //            if (row.isSelected()) {
372 //                Float weight = row.getWeight();
373 //                if (weight != null) {
374 //                    if (result == null) {
375 //                        result = 0f;
376 //                    }
377 //                    result += weight;
378 //                }
379 //            }
380 //        }
381         getModel().setSampleWeight(result.orElse(null));
382     }
383 
384     protected void generateTableModel(SampleCategoryModelEntry category) {
385 
386         // when generate a new table model, then reset previous rows from model
387         getModel().setRows(null);
388 
389         Caracteristic data = null;
390 
391         JXTable table = getTable();
392 
393         DefaultTableColumnModelExt columnModel = new DefaultTableColumnModelExt();
394 
395         { // Selection
396 
397             addBooleanColumnToModel(columnModel, SplitSpeciesBatchTableModel.SELECTED, table);
398         }
399 
400         boolean editableCategoryValue = false;
401 
402         log.debug("category " + category);
403         if (category != null) {
404             log.debug("category " + category.getLabel());
405             if (!category.getCaracteristic().isQualitativeValueEmpty()) {
406 
407                 // qualitative category
408                 data = category.getCaracteristic();
409 
410                 log.debug("data " + data);
411 
412             } else {
413                 editableCategoryValue = true;
414                 addFloatColumnToModel(columnModel,
415                                       SplitSpeciesBatchTableModel.EDITABLE_CATEGORY_VALUE,
416                                       TuttiUI.DECIMAL1_PATTERN,
417                                       table);
418             }
419 
420             if (data != null) {
421 
422                 if (log.isDebugEnabled()) {
423                     log.debug("Got " + data.sizeQualitativeValue() + " qualitative data to add");
424                 }
425                 addColumnToModel(columnModel,
426                                  null,
427                                  newTableCellRender(CaracteristicQualitativeValue.class),
428                                  SplitSpeciesBatchTableModel.READ_ONLY_CATEGORY_VALUE);
429             }
430             { // Weight
431 
432                 addFloatColumnToModel(columnModel,
433                                       SplitSpeciesBatchTableModel.WEIGHT,
434                                       getConfig().getSpeciesWeightUnit(),
435                                       table);
436             }
437         }
438 
439         // create table model
440         SplitSpeciesBatchTableModel tableModel = new SplitSpeciesBatchTableModel(columnModel,
441                                                                                  getModel(),
442                                                                                  editableCategoryValue,
443                                                                                  getModel().isSplitMode());
444 
445         // remove all listener on tables we could add before
446         uninstallTableSaveOnRowChangedSelectionListener();
447         uninstallTableKeyListener(getTable());
448 
449         if (log.isDebugEnabled()) {
450             log.debug("Install new table model " + tableModel);
451         }
452         table.setModel(tableModel);
453         table.setColumnModel(columnModel);
454 
455         // install table listeners
456         installTableSaveOnRowChangedSelectionListener();
457         installTableKeyListener(columnModel, table);
458 
459         // fill datas
460 
461         List<SplitSpeciesBatchRowModel> rows = new ArrayList<>();
462 
463         if (data != null) {
464 
465             // add a row for each qualitative value
466             for (CaracteristicQualitativeValue qualitativeValue : data.getQualitativeValue()) {
467                 log.debug("QV: " + qualitativeValue);
468                 if (log.isDebugEnabled()) {
469                     log.debug("Add QV: " + qualitativeValue);
470                 }
471                 SplitSpeciesBatchRowModel newRow = tableModel.createNewRow();
472                 newRow.setCategoryValue(qualitativeValue);
473                 newRow.addPropertyChangeListener(SplitSpeciesBatchRowModel.PROPERTY_WEIGHT, PROPERTY_WEIGHT_CHANGED_LISTENER);
474                 rows.add(newRow);
475             }
476         }
477 
478         if (log.isDebugEnabled()) {
479             log.debug("Will add " + rows.size() + " rows in table model " +
480                               "(can add a first empty row? " + editableCategoryValue + ").");
481         }
482 
483         getModel().setRows(rows);
484     }
485 
486 }