View Javadoc
1   package fr.ifremer.tutti.ui.swing.content.operation.catches.species.edit;
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.collect.ImmutableSet;
27  import com.google.common.collect.Lists;
28  import com.google.common.collect.Sets;
29  import fr.ifremer.tutti.persistence.entities.data.SampleCategory;
30  import fr.ifremer.tutti.persistence.entities.data.SampleCategoryModel;
31  import fr.ifremer.tutti.persistence.entities.referential.Species;
32  import fr.ifremer.tutti.type.WeightUnit;
33  import org.apache.commons.logging.Log;
34  import org.apache.commons.logging.LogFactory;
35  import org.jdesktop.swingx.table.TableColumnModelExt;
36  import org.nuiton.jaxx.application.swing.table.AbstractApplicationTableModel;
37  import org.nuiton.jaxx.application.swing.table.ColumnIdentifier;
38  
39  import javax.swing.table.TableColumn;
40  import java.io.Serializable;
41  import java.util.Enumeration;
42  import java.util.List;
43  import java.util.Set;
44  
45  import static org.nuiton.i18n.I18n.n;
46  
47  /**
48   * @author Tony Chemit - chemit@codelutin.com
49   * @since 0.1
50   */
51  public class SpeciesBatchTableModel extends AbstractApplicationTableModel<SpeciesBatchRowModel> {
52  
53      private static final long serialVersionUID = 1L;
54  
55      /** Logger. */
56      private static final Log log = LogFactory.getLog(SpeciesBatchTableModel.class);
57  
58      public static final ColumnIdentifier<SpeciesBatchRowModel> ID = ColumnIdentifier.newId(
59              SpeciesBatchRowModel.PROPERTY_ID,
60              n("tutti.editSpeciesBatch.table.header.id"),
61              n("tutti.editSpeciesBatch.table.header.id.tip"));
62  
63      public static final ColumnIdentifier<SpeciesBatchRowModel> SPECIES = ColumnIdentifier.newId(
64              SpeciesBatchRowModel.PROPERTY_SPECIES_ROW,
65              n("tutti.editSpeciesBatch.table.header.species"),
66              n("tutti.editSpeciesBatch.table.header.species.tip"));
67  
68      public static final ColumnIdentifier<SpeciesBatchRowModel> WEIGHT = ColumnIdentifier.newId(
69              SpeciesBatchRowModel.PROPERTY_COMPUTED_WEIGHT,
70              n("tutti.editSpeciesBatch.table.header.weight"),
71              n("tutti.editSpeciesBatch.table.header.weight.tip"));
72  
73      public static final ColumnIdentifier<SpeciesBatchRowModel> COMPUTED_NUMBER = ColumnIdentifier.newId(
74              SpeciesBatchRowModel.PROPERTY_COMPUTED_NUMBER,
75              n("tutti.editSpeciesBatch.table.header.computedNumber"),
76              n("tutti.editSpeciesBatch.table.header.computedNumber.tip"));
77  
78      public static final ColumnIdentifier<SpeciesBatchRowModel> COMMENT = ColumnIdentifier.newId(
79              SpeciesBatchRowModel.PROPERTY_COMMENT,
80              n("tutti.editSpeciesBatch.table.header.comment"),
81              n("tutti.editSpeciesBatch.table.header.comment.tip"));
82  
83      public static final ColumnIdentifier<SpeciesBatchRowModel> ATTACHMENT = ColumnIdentifier.newReadOnlyId(
84              SpeciesBatchRowModel.PROPERTY_ATTACHMENT,
85              n("tutti.editSpeciesBatch.table.header.file"),
86              n("tutti.editSpeciesBatch.table.header.file.tip"));
87  
88      public static final ColumnIdentifier<SpeciesBatchRowModel> SPECIES_TO_CONFIRM = ColumnIdentifier.newId(
89              SpeciesBatchRowModel.PROPERTY_SPECIES_TO_CONFIRM,
90              "", /* I18n n'est pas capable de rendre une traduction vide et on le veut (voir https://forge.codelutin.com/issues/6067) */
91              n("tutti.editSpeciesBatch.table.header.toConfirm.tip"));
92  
93      /**
94       * Columns for the frequency edition.
95       *
96       * @since 0.2
97       */
98      protected final Set<ColumnIdentifier<SpeciesBatchRowModel>> frequencyCols;
99  
100     /**
101      * Columns implied in the sample category definition.
102      *
103      * @since 0.2
104      */
105     protected final Set<SampleCategoryColumnIdentifier> sampleCols;
106 
107     /**
108      * Sample categories model.
109      *
110      * @since 2.4
111      */
112     protected final SampleCategoryModel sampleCategoryModel;
113 
114     /**
115      * Weight unit.
116      *
117      * @since 2.5
118      */
119     protected final WeightUnit weightUnit;
120 
121     public SpeciesBatchTableModel(WeightUnit weightUnit,
122                                   SampleCategoryModel sampleCategoryModel,
123                                   TableColumnModelExt columnModel) {
124         super(columnModel, false, false);
125         this.weightUnit = weightUnit;
126         this.sampleCategoryModel = sampleCategoryModel;
127         setNoneEditableCols(ID, SPECIES);
128 
129         frequencyCols = Sets.newHashSet();
130         frequencyCols.add(COMPUTED_NUMBER);
131         frequencyCols.add(WEIGHT);
132 
133         Set<SampleCategoryColumnIdentifier> sampleCols = Sets.newLinkedHashSet();
134 
135         Enumeration<TableColumn> columns = columnModel.getColumns();
136         while (columns.hasMoreElements()) {
137             TableColumn tableColumn = columns.nextElement();
138             Object identifier = tableColumn.getIdentifier();
139             if (identifier instanceof SampleCategoryColumnIdentifier) {
140                 sampleCols.add((SampleCategoryColumnIdentifier) identifier);
141             }
142         }
143         this.sampleCols = ImmutableSet.copyOf(sampleCols);
144     }
145 
146     public Set<SampleCategoryColumnIdentifier> getSampleCols() {
147         return sampleCols;
148     }
149 
150     @Override
151     protected void collectShell(SpeciesBatchRowModel row,
152                                 Set<SpeciesBatchRowModel> collectedRows) {
153         super.collectShell(row, collectedRows);
154 
155         if (!row.isChildBatchsEmpty()) {
156             for (SpeciesBatchRowModel child : row.getChildBatch()) {
157                 collectShell(child, collectedRows);
158             }
159         }
160     }
161 
162     @Override
163     public SpeciesBatchRowModel createNewRow() {
164         SpeciesBatchRowModel result =
165                 new SpeciesBatchRowModel(weightUnit, sampleCategoryModel);
166 
167         // by default empty row is not valid
168         result.setValid(false);
169         return result;
170     }
171 
172     @Override
173     public void setValueAt(Object aValue,
174                            int rowIndex,
175                            int columnIndex,
176                            ColumnIdentifier<SpeciesBatchRowModel> propertyName,
177                            SpeciesBatchRowModel entry) {
178         if (sampleCols.contains(propertyName)) {
179 
180             SampleCategoryColumnIdentifier sampleCategoryColumnIdentifier = (SampleCategoryColumnIdentifier) propertyName;
181             sampleCategoryColumnIdentifier.setWeightValue(entry, aValue);
182 
183             // must find out first ancestor with this category
184             Integer sampleCategoryId = sampleCategoryColumnIdentifier.getSampleCategoryId();
185             if (log.isDebugEnabled()) {
186                 log.debug("Sample category: " + sampleCategoryId + " modified at row: " + rowIndex);
187             }
188             SampleCategory<?> sampleCategory = entry.getSampleCategoryById(sampleCategoryId);
189             SpeciesBatchRowModel firstAncestor = entry.getFirstAncestor(sampleCategory);
190 
191             int firstRowIndex = getRowIndex(firstAncestor);
192             if (log.isDebugEnabled()) {
193                 log.debug("First ancestor row: " + firstRowIndex);
194             }
195 
196             // save this row and his shell
197             updateShell(firstAncestor, columnIndex);
198 
199         } else {
200             super.setValueAt(aValue, rowIndex, columnIndex, propertyName, entry);
201         }
202     }
203 
204     @Override
205     protected boolean isCellEditable(int rowIndex,
206                                      int columnIndex,
207                                      ColumnIdentifier<SpeciesBatchRowModel> propertyName) {
208 
209         boolean result = super.isCellEditable(rowIndex,
210                                               columnIndex,
211                                               propertyName);
212         if (result) {
213 
214             if (frequencyCols.contains(propertyName)) {
215 
216                 // must have filled a species to edit this column
217                 SpeciesBatchRowModel entry = getEntry(rowIndex);
218                 result = entry.isBatchLeaf();
219 
220             } else if (sampleCols.contains(propertyName)) {
221 
222                 // can only edit if a category value is setted
223                 SpeciesBatchRowModel entry = getEntry(rowIndex);
224 
225                 // check from protocol what is possible ?
226                 Species species = entry.getSpecies();
227 
228                 if (species == null) {
229 
230                     // no species, can not edit
231                     result = false;
232                 } else {
233 
234                     // protocol authorize it
235 
236                     // final test: can edit only if sample category is setted
237                     SampleCategory<?> value =
238                             (SampleCategory<?>) propertyName.getValue(entry);
239 
240                     result = value.getCategoryValue() != null;
241 
242                 }
243             }
244         }
245         return result;
246     }
247 
248     /**
249      * Return the next editable row index for frequency from the given
250      * {@code rowIndex}, or {@code null} if none found.
251      *
252      * @param rowIndex the starting index where to look
253      * @return the next editable row index for frequency from the given {@code rowIndex}, or {@code null} if none found.
254      * @since 2.5
255      */
256     public Integer getNextEditableFrequencyRow(Integer rowIndex) {
257         Integer result = null;
258 
259         for (int i = rowIndex, max = getRowCount(); i < max; i++) {
260             SpeciesBatchRowModel entry = getEntry(i);
261             if (entry.isBatchLeaf()) {
262                 result = i;
263                 break;
264             }
265         }
266         return result;
267     }
268 
269     /**
270      * Return the index of the next row that can be inserted as a child of the
271      * given row.
272      *
273      * @param row the parent row
274      * @return the index of the next row that can be inserted as a child of the
275      * given row
276      * @since 2.7
277      */
278     public final int getNextChildRowIndex(SpeciesBatchRowModel row) {
279 
280         // get row index of the given row
281         int parentRowIndex = getRowIndex(row);
282 
283         // must find it
284         Preconditions.checkState(getRowIndex(row) != -1);
285 
286         int result = parentRowIndex;
287 
288         if (!row.isChildBatchsEmpty()) {
289 
290             // take his last child
291             SpeciesBatchRowModel lastChild =
292                     row.getChildBatch().get(row.sizeChildBatchs() - 1);
293 
294             // get the shell of this child (including himself)
295             Set<SpeciesBatchRowModel> childs = Sets.newHashSet();
296             childs.add(lastChild);
297             lastChild.collectShell(childs);
298 
299             // get row with max index
300             for (SpeciesBatchRowModel child : childs) {
301                 int childRowIndex = getRowIndex(child);
302                 result = Math.max(childRowIndex, result);
303             }
304         }
305 
306         // result is the nex row
307         result++;
308 
309         return result;
310     }
311 
312     /**
313      * Return the sample category id of a column or {@code null} if not on a
314      * sample category column.
315      *
316      * @param columnIndex index of the column to look at
317      * @return the sample category id of a column or {@code null} if not on a
318      * sample category column.
319      * @since 2.6
320      */
321     public Integer getSampleCategoryId(int columnIndex) {
322         Integer result = null;
323         ColumnIdentifier<SpeciesBatchRowModel> identifier = getIdentifier(columnIndex);
324         if (sampleCols.contains(identifier)) {
325             SampleCategoryColumnIdentifier sampleId = (SampleCategoryColumnIdentifier) identifier;
326             result = sampleId.getSampleCategoryId();
327         }
328         return result;
329     }
330 
331     /**
332      * Update the sample category value of the given {@code row}.
333      *
334      * @param row         the row to walk through
335      * @param columnIndex index of the column where the sample category is
336      * @param newValue    new sample category value to set
337      * @return the old category value replaced by the new one
338      * @since 2.6
339      */
340     public Serializable updateSampleCategorieValue(SpeciesBatchRowModel row,
341                                                    int columnIndex,
342                                                    Serializable newValue) {
343 
344         SampleCategoryColumnIdentifier identifier = (SampleCategoryColumnIdentifier) getIdentifier(columnIndex);
345 
346         Serializable oldValue = identifier.getCategoryValue(row);
347 
348         // set category value
349         identifier.setCategoryValue(row, newValue);
350 
351         // update row shell
352         updateShell(row, columnIndex);
353 
354         return oldValue;
355 
356     }
357 
358     /**
359      * Return previous sibling row, or {@code null} if not exist.
360      *
361      * @param row where to start
362      * @return the previous sibling row, or {@code null} if not exist.
363      * @since 2.6
364      */
365     public SpeciesBatchRowModel getPreviousSibling(SpeciesBatchRowModel row) {
366         SpeciesBatchRowModel result = null;
367         SpeciesBatchRowModel parentBatch = row.getParentBatch();
368         if (parentBatch != null) {
369             List<SpeciesBatchRowModel> childBatch = parentBatch.getChildBatch();
370             int i = childBatch.indexOf(row);
371             if (i > 0) {
372                 result = childBatch.get(i - 1);
373             }
374         }
375         return result;
376     }
377 
378     /**
379      * Update all the given cells for the  given column.
380      *
381      * @param shell       shell to update
382      * @param columnIndex the column index to update
383      * @since 2.6
384      */
385     public void updateShell(Set<SpeciesBatchRowModel> shell, int columnIndex) {
386         for (SpeciesBatchRowModel batchRowModel : shell) {
387             int currentRowIndex = getRowIndex(batchRowModel);
388             if (log.isDebugEnabled()) {
389                 log.debug("Update shell row: " + currentRowIndex);
390             }
391             fireTableCellUpdated(currentRowIndex, columnIndex);
392         }
393     }
394 
395     /**
396      * Return the list of used species in the table (used to fill the
397      * comparator cache for species sort)
398      *
399      * @return the list of used species in the table.
400      * @since 2.8
401      */
402     public List<Species> getSpeciesList() {
403         List<Species> result = Lists.newArrayList();
404         for (SpeciesBatchRowModel row : rows) {
405             if (row.isBatchRoot()) {
406                 result.add(row.getSpecies());
407             }
408         }
409         return result;
410     }
411 
412     /**
413      * Update all the cells of the given row shell for a given column.
414      *
415      * @param entry       the first row to update
416      * @param columnIndex the column index to update
417      * @since 2.6
418      */
419     protected void updateShell(SpeciesBatchRowModel entry, int columnIndex) {
420 
421         Set<SpeciesBatchRowModel> shell = Sets.newHashSet();
422         entry.collectShell(shell);
423         shell.add(entry);
424         updateShell(shell, columnIndex);
425     }
426 }