...
 
Commits (3)
......@@ -171,7 +171,7 @@
<maven.compiler.debug>true</maven.compiler.debug>
<!-- Quadrige3 Core version -->
<quadrige3-core.version>3.6.11</quadrige3-core.version>
<quadrige3-core.version>3.6.13</quadrige3-core.version>
<!-- Last ReefDb launcher version -->
<launcherVersion>3.0.3</launcherVersion>
......
......@@ -905,9 +905,9 @@ public final class ReefDbConfiguration extends QuadrigeCoreConfiguration {
String configOption = applicationConfig.getOption(ReefDbConfigurationOption.PMFM_IDS_TRANSITION_LENGTH.getKey());
if (StringUtils.isBlank(configOption)) return result;
List<String> triplets = Splitter.on(";").splitToList(configOption);
List<String> triplets = Splitter.on(";").omitEmptyStrings().splitToList(configOption);
for (String triplet : triplets) {
List<String> pmfmsIdsStr = Splitter.on(",").splitToList(triplet);
List<String> pmfmsIdsStr = Splitter.on(",").omitEmptyStrings().trimResults().splitToList(triplet);
if (CollectionUtils.size(pmfmsIdsStr) == 3) {
try {
Integer[] pmfmIds = new Integer[3];
......
......@@ -42,6 +42,7 @@ import fr.ifremer.reefdb.dto.configuration.programStrategy.PmfmStrategyDTO;
import fr.ifremer.reefdb.dto.configuration.programStrategy.ProgramDTO;
import fr.ifremer.reefdb.dto.data.measurement.MeasurementAware;
import fr.ifremer.reefdb.dto.data.measurement.MeasurementDTO;
import fr.ifremer.reefdb.dto.data.sampling.SamplingOperationDTO;
import fr.ifremer.reefdb.dto.data.survey.CampaignDTO;
import fr.ifremer.reefdb.dto.data.survey.SurveyDTO;
import fr.ifremer.reefdb.dto.enums.*;
......@@ -90,36 +91,6 @@ public class ReefDbBeans extends QuadrigeBeans {
}
}
/**
* <p>computeAddress.</p>
*
* @param street a {@link String} object.
* @param zipCode a {@link String} object.
* @param city a {@link String} object.
* @return a {@link String} object.
*/
public static String computeAddress(String street, String zipCode, String city) {
StringBuilder addressBuilder = new StringBuilder();
if (StringUtils.isNotBlank(street)) {
addressBuilder.append(street.replaceAll("[ ]+", " "));
if (StringUtils.isNotBlank(zipCode) || StringUtils.isNotBlank(city)) {
addressBuilder.append(", ");
}
}
if (StringUtils.isNotBlank(zipCode)) {
addressBuilder.append(zipCode);
if (StringUtils.isNotBlank(city)) {
addressBuilder.append(" ");
}
}
if (StringUtils.isNotBlank(city)) {
addressBuilder.append(city);
}
return addressBuilder.toString();
}
public static String toQuotedString(String string) {
return "'" + string.replaceAll("'", "''") + "'";
}
......@@ -189,6 +160,47 @@ public class ReefDbBeans extends QuadrigeBeans {
return bean.toString();
}
public static List<MeasurementDTO> duplicate(List<MeasurementDTO> measurements) {
if (measurements == null)
return new ArrayList<>();
return measurements.stream().map(ReefDbBeans::duplicate).collect(Collectors.toList());
}
public static MeasurementDTO duplicate(MeasurementDTO measurement) {
// Clone object
MeasurementDTO clone = clone(measurement);
// Remove Id & individualId
clone.setId(null);
clone.setIndividualId(null);
// remove errors
clone.setErrors(null);
return clone;
}
public static SamplingOperationDTO duplicate(final SamplingOperationDTO samplingOperation) {
// Clone object
final SamplingOperationDTO clone = clone(samplingOperation);
// Remove ID
clone.setId(null);
// clone coordinate
clone.setCoordinate(clone(samplingOperation.getCoordinate()));
// remove errors
clone.setErrors(null);
// duplicate measurements
clone.setMeasurements(duplicate(samplingOperation.getMeasurements()));
clone.setIndividualMeasurements(duplicate(samplingOperation.getIndividualMeasurements()));
return clone;
}
/**
* <p>filterNotEmptyAppliedPeriod.</p>
*
......
......@@ -26,7 +26,6 @@ package fr.ifremer.reefdb.service.observation;
import fr.ifremer.quadrige3.core.ProgressionCoreModel;
import fr.ifremer.reefdb.dto.configuration.programStrategy.ProgStratDTO;
import fr.ifremer.reefdb.dto.configuration.programStrategy.ProgramDTO;
import fr.ifremer.reefdb.dto.data.measurement.MeasurementDTO;
import fr.ifremer.reefdb.dto.data.sampling.SamplingOperationDTO;
import fr.ifremer.reefdb.dto.data.survey.CampaignDTO;
import fr.ifremer.reefdb.dto.data.survey.SurveyDTO;
......@@ -128,24 +127,6 @@ public interface ObservationService {
@PreAuthorize("hasPermission(null, T(fr.ifremer.quadrige3.core.security.QuadrigeUserAuthority).USER)")
SurveyDTO duplicateSurvey(SurveyDTO survey, boolean fullDuplication, boolean copyCoordinates, boolean duplicateSurveyMeasurements);
/**
* Duplicate prelevement.
*
* @param samplingOperation Prelevement to duplicate
* @return Prelvement duplicated
*/
@PreAuthorize("hasPermission(null, T(fr.ifremer.quadrige3.core.security.QuadrigeUserAuthority).USER)")
SamplingOperationDTO duplicateSamplingOperation(SamplingOperationDTO samplingOperation);
/**
* Duplicate a list of measurements.
*
* @param measurements Measurements to duplicate
* @return Measurements duplicate
*/
@PreAuthorize("hasPermission(null, T(fr.ifremer.quadrige3.core.security.QuadrigeUserAuthority).USER)")
List<MeasurementDTO> duplicateMeasurements(List<MeasurementDTO> measurements);
/**
* <p>getAvailablePrograms.</p>
*
......
......@@ -538,59 +538,6 @@ public class ObservationServiceImpl implements ObservationInternalService {
}
/**
* {@inheritDoc}
*/
@Override
public SamplingOperationDTO duplicateSamplingOperation(final SamplingOperationDTO samplingOperation) {
// Duplicate sampling operation
final SamplingOperationDTO duplicatedSamplingOperation = ReefDbBeans.clone(samplingOperation);
// Remove ID
duplicatedSamplingOperation.setId(null);
// duplicate coordinate
duplicatedSamplingOperation.setCoordinate(ReefDbBeans.clone(samplingOperation.getCoordinate()));
// remove errors
duplicatedSamplingOperation.setErrors(null);
// duplicate measurements
duplicatedSamplingOperation.setMeasurements(duplicateMeasurements(samplingOperation.getMeasurements()));
duplicatedSamplingOperation.setIndividualMeasurements(duplicateMeasurements(samplingOperation.getIndividualMeasurements()));
return duplicatedSamplingOperation;
}
/**
* {@inheritDoc}
*/
@Override
public List<MeasurementDTO> duplicateMeasurements(List<MeasurementDTO> measurements) {
List<MeasurementDTO> duplicatedMeasurements = Lists.newArrayList();
if (CollectionUtils.isNotEmpty(measurements)) {
for (MeasurementDTO measurement : measurements) {
// duplicated measurement
final MeasurementDTO duplicatedMeasurement = ReefDbBeans.clone(measurement);
// Remove Id & individualId
duplicatedMeasurement.setId(null);
duplicatedMeasurement.setIndividualId(null);
// remove errors
duplicatedMeasurement.setErrors(null);
duplicatedMeasurements.add(duplicatedMeasurement);
}
}
return duplicatedMeasurements;
}
/**
* {@inheritDoc}
*/
......
......@@ -23,12 +23,15 @@ package fr.ifremer.reefdb.ui.swing.content.home.operation;
* #L%
*/
import fr.ifremer.reefdb.dto.ReefDbBeans;
import fr.ifremer.reefdb.dto.data.sampling.SamplingOperationDTO;
import fr.ifremer.reefdb.ui.swing.action.AbstractReefDbAction;
/**
* Duplicate sampling operation action.
* {@link Deprecated} Use fr.ifremer.reefdb.dto.ReefDbBeans#duplicate(fr.ifremer.reefdb.dto.data.sampling.SamplingOperationDTO) outside an action
*/
@Deprecated
public class DuplicateOperationAction extends AbstractReefDbAction<OperationsTableUIModel, OperationsTableUI, OperationsTableUIHandler> {
/**
......@@ -60,12 +63,10 @@ public class DuplicateOperationAction extends AbstractReefDbAction<OperationsTab
if (operationsTableRowModel != null) {
// Duplicate operation
final SamplingOperationDTO duplicatePrelevement = getContext().getObservationService().duplicateSamplingOperation(operationsTableRowModel.toBean());
if (duplicatePrelevement != null) {
// Add duplicate operation to table
newRow = getModel().addNewRow(duplicatePrelevement);
}
final SamplingOperationDTO duplicateOperation = ReefDbBeans.duplicate(operationsTableRowModel.toBean());
// Add duplicate operation to table
newRow = getModel().addNewRow(duplicateOperation);
}
}
......
......@@ -252,7 +252,7 @@ public class OperationsTableUIHandler extends AbstractReefDbTableUIHandler<Opera
}
// save actual table context before loading new survey
getContext().saveComponentInSwingSession(getTable(), getTableModel().getStateContext());
saveTableState();
// affect new selected survey
getModel().setSurvey(survey);
......@@ -739,7 +739,7 @@ public class OperationsTableUIHandler extends AbstractReefDbTableUIHandler<Opera
forceRevalidateModel();
// restore table state from swing session
getContext().restoreComponentFromSwingSession(getTable());
restoreTableState();
// hide analyst column if no pmfm (Mantis #42619)
// forceColumnVisibleAtLastPosition(OperationsTableModel.ANALYST, notEmpty);
......
......@@ -439,13 +439,11 @@ public class AddOperationTableUIHandler extends AbstractReefDbTableUIHandler<Ope
for (int i = 0; i < nbOperation; i++) {
// Ajout du prelevement dans la liste
final SamplingOperationDTO newOperation = getContext().getObservationService().duplicateSamplingOperation(operation);
if (newOperation != null) {
operations.add(newOperation);
final SamplingOperationDTO newOperation = ReefDbBeans.duplicate(operation);
operations.add(newOperation);
// do it at creation time
newOperation.setMeasurementsLoaded(true);
}
// do it at creation time
newOperation.setMeasurementsLoaded(true);
}
// Ajout des prelevements
......
package fr.ifremer.reefdb.ui.swing.content.observation.operation.measurement.grouped;
/*
* #%L
* Reef DB :: UI
* $Id:$
* $HeadURL:$
* %%
* Copyright (C) 2014 - 2015 Ifremer
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
import fr.ifremer.reefdb.ui.swing.action.AbstractReefDbAction;
/**
* Action dupliquer mesure (ecran prelevements/mesure).
*/
public class DuplicateOperationMeasurementsAction extends AbstractReefDbAction<OperationMeasurementsGroupedTableUIModel, OperationMeasurementsGroupedTableUI, OperationMeasurementsGroupedTableUIHandler> {
/**
* La ligne ajoutee.
*/
private OperationMeasurementsGroupedRowModel row;
/**
* Constrcuteur.
*
* @param handler Controller
*/
public DuplicateOperationMeasurementsAction(final OperationMeasurementsGroupedTableUIHandler handler) {
super(handler, false);
}
/** {@inheritDoc} */
@Override
public boolean prepareAction() throws Exception {
return super.prepareAction() && getModel().getSelectedRows().size() == 1;
}
/** {@inheritDoc} */
@Override
public void doAction() {
// Selected measurement
final OperationMeasurementsGroupedRowModel rowToDuplicate = getModel().getSelectedRows().iterator().next();
if (rowToDuplicate != null) {
row = new OperationMeasurementsGroupedRowModel(false);
row.setSamplingOperation(rowToDuplicate.getSamplingOperation());
row.setTaxonGroup(rowToDuplicate.getTaxonGroup());
row.setTaxon(rowToDuplicate.getTaxon());
row.setInputTaxonId(rowToDuplicate.getInputTaxonId());
row.setInputTaxonName(rowToDuplicate.getInputTaxonName());
row.setComment(rowToDuplicate.getComment());
row.setIndividualPmfms(rowToDuplicate.getIndividualPmfms());
// add also analyst (MAntis #45054)
row.setAnalyst(rowToDuplicate.getAnalyst());
// duplicate measurements by service
row.setIndividualMeasurements(getContext().getObservationService().duplicateMeasurements(rowToDuplicate.getIndividualMeasurements()));
}
}
/** {@inheritDoc} */
@Override
public void postSuccessAction() {
super.postSuccessAction();
// Add duplicate measurement to table
getModel().insertRowAfterSelected(row);
// save directly to sampling operation
getHandler().saveMeasurementsInModel(row);
// send update event on sampling operation
if (row.getSamplingOperation() != null) {
getModel().firePropertyChanged(OperationMeasurementsGroupedTableUIModel.PROPERTY_SAMPLING_OPERATION, null, row.getSamplingOperation());
}
getHandler().recomputeRowsValidState();
getModel().setModify(true);
// Ajouter le focus sur la cellule de la ligne cree
getHandler().setFocusOnCell(row);
}
}
......@@ -60,7 +60,7 @@
<JPanel id='tableauBasPanelButtons' layout='{new BorderLayout()}' constraints="BorderLayout.PAGE_END">
<JPanel layout='{new FlowLayout()}' constraints='BorderLayout.LINE_START'>
<JButton id='addButton' alignmentX='{Component.CENTER_ALIGNMENT}' onActionPerformed="model.insertNewRowAfterSelected()"/>
<JButton id='duplicateButton' alignmentX='{Component.CENTER_ALIGNMENT}'/>
<JButton id='duplicateButton' alignmentX='{Component.CENTER_ALIGNMENT}' onActionPerformed="handler.duplicateSelectedRow()"/>
<JButton id='initDataGridButton' alignmentX='{Component.CENTER_ALIGNMENT}' onActionPerformed="handler.initializeDataGrid()"/>
<JButton id='multiEditButton' alignmentX='{Component.CENTER_ALIGNMENT}' onActionPerformed="handler.editSelectedMeasurements()"/>
<JButton id='deleteButton' alignmentX='{Component.CENTER_ALIGNMENT}' onActionPerformed="handler.removeIndividualMeasurements()"/>
......
......@@ -52,7 +52,6 @@
actionIcon: copy;
text: "reefdb.action.duplicate.label";
toolTipText: "reefdb.action.duplicate.measurement.tip";
_applicationAction: {DuplicateOperationMeasurementsAction.class};
enabled: {model.getSurvey().isEditable() && !model.getSelectedRows().isEmpty() && (model.getSelectedRows().size() == 1) && !model.getPmfms().isEmpty()};
}
......
......@@ -150,6 +150,22 @@ public class OperationMeasurementsGroupedTableUIHandler
// update some controls
getUI().processDataBinding(OperationMeasurementsGroupedTableUI.BINDING_INIT_DATA_GRID_BUTTON_ENABLED);
break;
// TODO ? force sort on sampling operation and transition column
// case OperationMeasurementsGroupedTableUIModel.PROPERTY_PIT_TRANSITION_LENGTH_PMFM_ID:
//
// // Try to apply sort on sampling operation and transition column
// if (getModel().getPitTransitionLengthPmfmId() != null) {
// TableColumnExt samplingColumn = getTable().getColumnExt(OperationMeasurementsGroupedTableModel.SAMPLING);
// PmfmTableColumn transitionColumn = findPmfmColumnByPmfmId(getModel().getPmfmColumns(), getModel().getPitTransitionLengthPmfmId());
// if (samplingColumn != null && transitionColumn != null) {
// getTable().getRowSorter().setSortKeys(ImmutableList.of(
// new RowSorter.SortKey(samplingColumn.getModelIndex(), SortOrder.ASCENDING),
// new RowSorter.SortKey(transitionColumn.getModelIndex(), SortOrder.ASCENDING)
// ));
// }
// }
// break;
}
});
......@@ -185,11 +201,10 @@ public class OperationMeasurementsGroupedTableUIHandler
@Override
protected List<? extends MeasurementAware> getMeasurementAwareModels() {
List<SamplingOperationDTO> samplingOperations = Lists.newArrayList(getModel().getSamplingOperations());
// sort by name
samplingOperations.sort(Comparator.comparing(SamplingOperationDTO::getName));
return samplingOperations;
return getModel().getSamplingOperations().stream()
// sort by name
.sorted(Comparator.comparing(SamplingOperationDTO::getName))
.collect(Collectors.toList());
}
@Override
......@@ -205,12 +220,13 @@ public class OperationMeasurementsGroupedTableUIHandler
@Override
protected void onRowModified(int rowIndex, OperationMeasurementsGroupedRowModel row, String propertyName, Integer propertyIndex, Object oldValue, Object newValue) {
// update individual id when sampling operation changes
// when sampling operation changes
if (OperationMeasurementsGroupedRowModel.PROPERTY_SAMPLING_OPERATION.equals(propertyName)) {
// remove measurement from old sampling
if (oldValue != null) {
SamplingOperationDTO oldSamplingOperation = (SamplingOperationDTO) oldValue;
// fixme: il rest edes mesures dans le operation !!!!!!!!!!!!!!
oldSamplingOperation.removeAllIndividualMeasurements(row.getIndividualMeasurements());
oldSamplingOperation.setDirty(true);
......@@ -235,6 +251,14 @@ public class OperationMeasurementsGroupedTableUIHandler
super.onRowModified(rowIndex, row, propertyName, propertyIndex, oldValue, newValue);
}
@Override
protected void onDuplicatedRowAdded(OperationMeasurementsGroupedRowModel row) {
// send update event on sampling operation
if (row.getSamplingOperation() != null) {
getModel().firePropertyChanged(OperationMeasurementsGroupedTableUIModel.PROPERTY_SAMPLING_OPERATION, null, row.getSamplingOperation());
}
}
@Override
protected void resetIndividualMeasurementIds(OperationMeasurementsGroupedRowModel row) {
......@@ -421,15 +445,14 @@ public class OperationMeasurementsGroupedTableUIHandler
for (Integer transition : initModel.getResultMap().get(samplingOperation)) {
// Compute next individual id
incrementNextIndividualId(samplingOperation, individualId);
OperationMeasurementsGroupedRowModel row = getTableModel().createNewRow();
// Initialize the row
row.setSamplingOperation(samplingOperation);
// Affect individual pmfms from parent model
row.setIndividualPmfms(new ArrayList<>(getModel().getPmfms()));
OperationMeasurementsGroupedRowModel row = createNewRow(false, samplingOperation);
// Set the individualId
row.setIndividualId(individualId.get());
// Affect individual pmfms from parent model
row.setIndividualPmfms(new ArrayList<>(getModel().getPmfms()));
// Create empty measurements
ReefDbBeans.createEmptyMeasurements(row);
// Get the measurement
......@@ -452,14 +475,6 @@ public class OperationMeasurementsGroupedTableUIHandler
}
}
private void incrementNextIndividualId(SamplingOperationDTO samplingOperation, AtomicInteger nextIndividualId) {
do {
nextIndividualId.incrementAndGet();
} while (getModel().getRows().stream().anyMatch(row ->
samplingOperation.equals(row.getSamplingOperation()) && nextIndividualId.get() == row.getIndividualId()));
}
public void editSelectedMeasurements() {
OperationMeasurementsMultiEditUI editUI = new OperationMeasurementsMultiEditUI(getUI());
......
......@@ -44,6 +44,7 @@ public class OperationMeasurementsGroupedTableUIModel
* property identifier used to propagate row sampling operation property change to parent
*/
public static final String PROPERTY_SAMPLING_OPERATION = "samplingOperation";
public static final String PROPERTY_PIT_TRANSITION_LENGTH_PMFM_ID = "pitTransitionLengthPmfmId";
// pmfm ids for grid initialization (pit protocol)
private Integer pitTransectOriginPmfmId;
......@@ -85,6 +86,7 @@ public class OperationMeasurementsGroupedTableUIModel
public void setPitTransitionLengthPmfmId(Integer pitTransitionLengthPmfmId) {
this.pitTransitionLengthPmfmId = pitTransitionLengthPmfmId;
firePropertyChange(PROPERTY_PIT_TRANSITION_LENGTH_PMFM_ID, null, pitTransitionLengthPmfmId);
}
public boolean isPitTransitionGridInitializationEnabled() {
......
......@@ -170,7 +170,7 @@ public class OperationMeasurementsUngroupedTableUIHandler extends AbstractReefDb
filterSamplingOperations();
// restore table from swing session
getContext().restoreComponentFromSwingSession(getTable());
restoreTableState();
// hide analyst if no pmfm
// forceColumnVisibleAtLastPosition(OperationMeasurementsUngroupedTableModel.ANALYST, notEmpty);
......
package fr.ifremer.reefdb.ui.swing.content.observation.shared;
/*-
* #%L
* Reef DB :: UI
* $Id:$
* $HeadURL:$
* %%
* Copyright (C) 2014 - 2019 Ifremer
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import fr.ifremer.quadrige3.core.dao.technical.Assert;
import fr.ifremer.quadrige3.core.dao.technical.factorization.pmfm.AllowedQualitativeValuesMap;
import fr.ifremer.quadrige3.ui.core.dto.DirtyAware;
import fr.ifremer.quadrige3.ui.swing.table.editor.ExtendedComboBoxCellEditor;
import fr.ifremer.reefdb.decorator.DecoratorService;
import fr.ifremer.reefdb.dto.ErrorDTO;
import fr.ifremer.reefdb.dto.ReefDbBeanFactory;
import fr.ifremer.reefdb.dto.ReefDbBeans;
import fr.ifremer.reefdb.dto.configuration.control.ControlRuleDTO;
import fr.ifremer.reefdb.dto.configuration.control.PreconditionRuleDTO;
import fr.ifremer.reefdb.dto.configuration.control.RuleGroupDTO;
import fr.ifremer.reefdb.dto.configuration.control.RulePmfmDTO;
import fr.ifremer.reefdb.dto.data.measurement.MeasurementAware;
import fr.ifremer.reefdb.dto.data.measurement.MeasurementDTO;
import fr.ifremer.reefdb.dto.enums.ControlElementValues;
import fr.ifremer.reefdb.dto.enums.ControlFeatureMeasurementValues;
import fr.ifremer.reefdb.dto.enums.FilterTypeValues;
import fr.ifremer.reefdb.dto.referential.DepartmentDTO;
import fr.ifremer.reefdb.dto.referential.TaxonDTO;
import fr.ifremer.reefdb.dto.referential.TaxonGroupDTO;
import fr.ifremer.reefdb.dto.referential.pmfm.PmfmDTO;
import fr.ifremer.reefdb.dto.referential.pmfm.QualitativeValueDTO;
import fr.ifremer.reefdb.ui.swing.content.observation.operation.measurement.grouped.OperationMeasurementsGroupedRowModel;
import fr.ifremer.reefdb.ui.swing.util.ReefDbUI;
import fr.ifremer.reefdb.ui.swing.util.table.AbstractReefDbTableUIHandler;
import fr.ifremer.reefdb.ui.swing.util.table.PmfmTableColumn;
import fr.ifremer.reefdb.ui.swing.util.table.ReefDbColumnIdentifier;
import fr.ifremer.reefdb.ui.swing.util.table.ReefDbPmfmColumnIdentifier;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
import java.awt.Dimension;
import java.awt.Insets;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import static org.nuiton.i18n.I18n.t;
/**
* @author peck7 on 08/01/2019.
*/
public abstract class AbstractMeasurementsGroupedTableUIHandler<
R extends AbstractMeasurementsGroupedRowModel<MeasurementDTO, R>,
M extends AbstractMeasurementsGroupedTableUIModel<MeasurementDTO, R, M>,
UI extends ReefDbUI<M, ?>>
extends AbstractReefDbTableUIHandler<R, M, UI> {
private static final Log LOG = LogFactory.getLog(AbstractMeasurementsGroupedTableUIHandler.class);
// editor for taxon group column
protected ExtendedComboBoxCellEditor<TaxonGroupDTO> taxonGroupCellEditor;
// editor for taxon column
protected ExtendedComboBoxCellEditor<TaxonDTO> taxonCellEditor;
// editor for analyst column
protected ExtendedComboBoxCellEditor<DepartmentDTO> departmentCellEditor;
public AbstractMeasurementsGroupedTableUIHandler(String... properties) {
super(properties);
}
@Override
public void beforeInit(UI ui) {
super.beforeInit(ui);
ui.setContextValue(createNewModel());
}
protected abstract M createNewModel();
protected abstract void initTable();
@Override
public void afterInit(UI ui) {
initUI(ui);
createTaxonGroupCellEditor();
createTaxonCellEditor();
createDepartmentCellEditor();
initTable();
initListeners();
}
@Override
public abstract AbstractMeasurementsGroupedTableModel<R> getTableModel();
private void createTaxonCellEditor() {
// taxonCellEditor = newExtendedComboBoxCellEditor(null, SurveyMeasurementsGroupedTableModel.TAXON, false);
taxonCellEditor = newExtendedComboBoxCellEditor(null, TaxonDTO.class, DecoratorService.WITH_CITATION_AND_REFERENT, false);
taxonCellEditor.setAction("unfilter-taxon", "reefdb.common.unfilter.taxon", e -> {
// unfilter taxons
updateTaxonCellEditor(getModel().getSingleSelectedRow(), true);
});
}
private void updateTaxonCellEditor(R row, boolean forceNoFilter) {
// Mantis #0027041 forceNoFilter==true remove link between TaxonGroup and Taxon, but keep context filter
taxonCellEditor.getCombo().setActionEnabled(!forceNoFilter /*&& getContext().getDataContext().isContextFiltered(FilterTypeValues.TAXON)*/);
// List<TaxonDTO> taxons = getContext().getObservationService().getAvailableTaxons(row == null ? null : row.getTaxonGroup(), forceNoFilter);
List<TaxonDTO> taxons = getModel().getObservationUIHandler().getAvailableTaxons(row == null || forceNoFilter ? null : row.getTaxonGroup(), false);
taxonCellEditor.getCombo().setData(taxons);
// Mantis #0028101 : don't affect taxon in row, even there is no corresponding
// if (row != null) {
// if (taxons.isEmpty() && row.getTaxon() != null) {
// row.setTaxon(null);
// } else if (taxons.size() == 1) {
// row.setTaxon(taxons.get(0));
// }
// }
}
private void createTaxonGroupCellEditor() {
taxonGroupCellEditor = newExtendedComboBoxCellEditor(null, TaxonGroupDTO.class, false);
taxonGroupCellEditor.setAction("unfilter-taxon", "reefdb.common.unfilter.taxon", e -> {
// unfilter taxon groups
updateTaxonGroupCellEditor(getModel().getSingleSelectedRow(), true);
});
}
private void updateTaxonGroupCellEditor(R row, boolean forceNoFilter) {
// Mantis #0027041 forceNoFilter==true remove link between TaxonGroup and Taxon, but keep context filter
taxonGroupCellEditor.getCombo().setActionEnabled(!forceNoFilter /*&& getContext().getDataContext().isContextFiltered(FilterTypeValues.TAXON_GROUP)*/);
// List<TaxonGroupDTO> taxonGroups = getContext().getObservationService().getAvailableTaxonGroups(row == null ? null : row.getTaxon(), forceNoFilter);
List<TaxonGroupDTO> taxonGroups = getModel().getObservationUIHandler().getAvailableTaxonGroups(row == null || forceNoFilter ? null : row.getTaxon(), false);
taxonGroupCellEditor.getCombo().setData(taxonGroups);
// Mantis #0028101 : don't affect taxon group in row, even there is no corresponding
// if (row != null) {
// if (taxonGroups.isEmpty() && row.getTaxonGroup() != null) {
// row.setTaxonGroup(null);
// } else if (taxonGroups.size() == 1) {
// row.setTaxonGroup(taxonGroups.get(0));
// }
// }
}
private void createDepartmentCellEditor() {
departmentCellEditor = newExtendedComboBoxCellEditor(null, DepartmentDTO.class, false);
departmentCellEditor.setAction("unfilter", "reefdb.common.unfilter", e -> {
if (!askBefore(t("reefdb.common.unfilter"), t("reefdb.common.unfilter.confirmation"))) {
return;
}
// unfilter location
updateDepartmentCellEditor(true);
});
}
private void updateDepartmentCellEditor(boolean forceNoFilter) {
departmentCellEditor.getCombo().setActionEnabled(!forceNoFilter
&& getContext().getDataContext().isContextFiltered(FilterTypeValues.DEPARTMENT));
departmentCellEditor.getCombo().setData(getContext().getObservationService().getAvailableDepartments(forceNoFilter));
}
protected void resetCellEditors() {
updateTaxonGroupCellEditor(null, false);
updateTaxonCellEditor(null, false);
updateDepartmentCellEditor(false);
}
protected void initListeners() {
getModel().addPropertyChangeListener(evt -> {
switch (evt.getPropertyName()) {
case AbstractMeasurementsGroupedTableUIModel.PROPERTY_SURVEY:
loadMeasurements();
break;
case AbstractMeasurementsGroupedTableUIModel.EVENT_MEASUREMENTS_LOADED:
detectPreconditionedPmfms();
detectGroupedIdentifiers();
break;
case AbstractMeasurementsGroupedTableUIModel.PROPERTY_MEASUREMENT_FILTER:
filterMeasurements();
break;
case AbstractMeasurementsGroupedTableUIModel.PROPERTY_SINGLE_ROW_SELECTED:
if (getModel().getSingleSelectedRow() != null && !getModel().getSingleSelectedRow().isEditable()) {
return;
}
// update taxonCellEditor
updateTaxonCellEditor(getModel().getSingleSelectedRow(), false);
updateTaxonGroupCellEditor(getModel().getSingleSelectedRow(), false);
updateDepartmentCellEditor(false);
updatePmfmCellEditors(getModel().getSingleSelectedRow(), null, false);
break;
}
});
}
protected abstract void filterMeasurements();
/**
* Load measurements
*/
private void loadMeasurements() {
SwingUtilities.invokeLater(() -> {
// Uninstall save state listener
uninstallSaveTableStateListener();
// reset (and prepare combo boxes)
resetCellEditors();
// Build dynamic columns
// TODO manage column position depending on criteria (parametrized list or specific attribute)
// maybe split pmfms list in 2 separated list or move afterward
ReefDbColumnIdentifier<R> insertPosition = getTableModel().getPmfmInsertPosition();
addPmfmColumns(
getModel().getPmfms(),
AbstractMeasurementsGroupedRowModel.PROPERTY_INDIVIDUAL_PMFMS,
DecoratorService.NAME_WITH_UNIT,
insertPosition);
boolean notEmpty = CollectionUtils.isNotEmpty(getModel().getPmfms());
// Build rows
getModel().setRows(buildRows(!getModel().getSurvey().isEditable()));
recomputeRowsValidState();
// Apply measurement filter
filterMeasurements();
// restore table from swing session
getContext().restoreComponentFromSwingSession(getTable());
// hide analyst if no pmfm
// forceColumnVisibleAtLastPosition(AbstractMeasurementsGroupedTableModel.ANALYST, notEmpty);
// Don't force position (Mantis #49537)
forceColumnVisible(AbstractMeasurementsGroupedTableModel.ANALYST, notEmpty);
// set columns with errors visible (Mantis #40752)
ensureColumnsWithErrorAreVisible(getModel().getRows());
// Install save state listener
installSaveTableStateListener();
getModel().fireMeasurementsLoaded();
});
}
protected List<R> buildRows(boolean readOnly) {
List<R> rows = new ArrayList<>();
List<? extends MeasurementAware> measurementAwareBeans = getMeasurementAwareModels();
for (MeasurementAware bean : measurementAwareBeans) {
List<MeasurementDTO> measurements = Lists.newArrayList(bean.getIndividualMeasurements());
// sort by individual id
measurements.sort(Comparator.comparingInt(MeasurementDTO::getIndividualId));
R row = null;
for (final MeasurementDTO measurement : measurements) {
// check previous row
if (row != null) {
// if individual id differs = new row
if (!row.getIndividualId().equals(measurement.getIndividualId())) {
row = null;
}
// if taxon group or taxon differs, should update current row (see Mantis #0026647)
else if (!Objects.equals(row.getTaxonGroup(), measurement.getTaxonGroup())
|| !Objects.equals(row.getTaxon(), measurement.getTaxon())
|| !Objects.equals(row.getInputTaxonId(), measurement.getInputTaxonId())
|| !Objects.equals(row.getInputTaxonName(), measurement.getInputTaxonName())) {
// update taxon group and taxon if previously empty
if (row.getTaxonGroup() == null) {
row.setTaxonGroup(measurement.getTaxonGroup());
} else if (measurement.getTaxonGroup() != null && !row.getTaxonGroup().equals(measurement.getTaxonGroup())) {
// the taxon group in measurement differs
LOG.error(String.format("taxon group in measurement (id=%s) differs with taxon group in previous measurements with same individual id (=%s) !",
measurement.getId(), measurement.getIndividualId()));
}
if (row.getTaxon() == null) {
row.setTaxon(measurement.getTaxon());
} else if (measurement.getTaxon() != null && !row.getTaxon().equals(measurement.getTaxon())) {
// the taxon in measurement differs
LOG.error(String.format("taxon in measurement (id=%s) differs with taxon in previous measurements with same individual id (=%s) !",
measurement.getId(), measurement.getIndividualId()));
}
if (row.getInputTaxonId() == null) {
row.setInputTaxonId(measurement.getInputTaxonId());
} else if (measurement.getInputTaxonId() != null && !row.getInputTaxonId().equals(measurement.getInputTaxonId())) {
// the input taxon Id in measurement differs
LOG.error(String.format("input taxon id in measurement (id=%s) differs with input taxon id in previous measurements with same individual id (=%s) !",
measurement.getId(), measurement.getIndividualId()));
}
if (StringUtils.isBlank(row.getInputTaxonName())) {
row.setInputTaxonName(measurement.getInputTaxonName());
} else if (measurement.getInputTaxonName() != null && !row.getInputTaxonName().equals(measurement.getInputTaxonName())) {
// the input taxon name in measurement differs
LOG.error(String.format("input taxon name in measurement (id=%s) differs with input taxon name in previous measurements with same individual id (=%s) !",
measurement.getId(), measurement.getIndividualId()));
}
}
}
// build a new row
if (row == null) {
row = createNewRow(readOnly, bean);
row.setIndividualPmfms(new ArrayList<>(getModel().getPmfms()));
row.setIndividualId(measurement.getIndividualId());
row.setTaxonGroup(measurement.getTaxonGroup());
row.setTaxon(measurement.getTaxon());
row.setInputTaxonId(measurement.getInputTaxonId());
row.setInputTaxonName(measurement.getInputTaxonName());
row.setAnalyst(measurement.getAnalyst());
// Mantis #26724 or #29130: don't take only one comment but a concatenation of all measurements comments
// row.setComment(measurement.getComment());
row.setValid(true);
rows.add(row);
// add errors on new row if samplingOperations have some
List<ErrorDTO> errors = ReefDbBeans.filterCollection(bean.getErrors(),
input -> input != null
&& ControlElementValues.MEASUREMENT.equals(input.getControlElementCode())
&& Objects.equals(input.getIndividualId(), measurement.getIndividualId())
);
ReefDbBeans.addUniqueErrors(row, errors);
}
// add measurement on current row
row.getIndividualMeasurements().add(measurement);
// add errors on row if measurement have some
ReefDbBeans.addUniqueErrors(row, measurement.getErrors());
}
}
// Mantis #26724 or #29130: don't take only one comment but a concatenation of all measurements comments
for (R row : rows) {
// join and affect
row.setComment(ReefDbBeans.getUnifiedCommentFromIndividualMeasurements(row));
}
return rows;
}
protected abstract List<? extends MeasurementAware> getMeasurementAwareModels();
protected abstract MeasurementAware getMeasurementAwareModelForRow(R row);
protected abstract R createNewRow(boolean readOnly, MeasurementAware parentBean);
@Override
protected void onRowsAdded(List<R> addedRows) {
super.onRowsAdded(addedRows);
if (addedRows.size() == 1) {
R newRow = addedRows.get(0);
// If newRow has no pmfm and no measurement (a real new row)
if (CollectionUtils.isEmpty(newRow.getIndividualPmfms()) && CollectionUtils.isEmpty(newRow.getIndividualMeasurements())) {
// Affect individual pmfms from parent model
newRow.setIndividualPmfms(new ArrayList<>(getModel().getPmfms()));
// Create empty measurements
ReefDbBeans.createEmptyMeasurements(newRow);
// Set default value if filters are set
initAddedRow(newRow);
}
// set default analyst from pmfm strategies (Mantis #42619)
setDefaultAnalyst(ImmutableList.of(newRow));
// now calculate individual id
calculateIndividualIds(newRow);
// reset the cell editors
resetCellEditors();
getModel().setModify(true);
// Ajouter le focus sur la cellule de la ligne cree
setFocusOnCell(newRow);
} else {
// set default analyst from pmfm strategies for all new rows (ie. from grid initialization) (Mantis #49331)
setDefaultAnalyst(addedRows);
}
}
protected void setDefaultAnalyst(Collection<R> rows) {
if (CollectionUtils.isEmpty(rows))
return;
DepartmentDTO analyst = getContext().getProgramStrategyService().getAnalysisDepartmentOfAppliedStrategyBySurvey(
getModel().getSurvey(),
getModel().getPmfms()
);
rows.forEach(row -> row.setAnalyst(analyst));
}
protected void calculateIndividualIds(R rowToUpdate) {
MeasurementAware bean = getMeasurementAwareModelForRow(rowToUpdate);
if (bean == null) return;
AtomicInteger individualId = new AtomicInteger();
// iterate over all ordered rows and set incremental individualId
getModel().getRows().stream().filter(getRowForBeanPredicate(bean)).forEach(row -> {
row.setIndividualId(individualId.incrementAndGet());
// update all individual measurements
if (CollectionUtils.isNotEmpty(row.getIndividualMeasurements())) {
for (MeasurementDTO individualMeasurement : row.getIndividualMeasurements()) {
individualMeasurement.setIndividualId(individualId.get());
}
}
});
setDirty(bean);
}
/**
* return a predicate to tell if the row is associated to the bean (true by default)
*
* @param bean the bean
* @return a predicate
*/
protected Predicate<? super R> getRowForBeanPredicate(MeasurementAware bean) {
return (Predicate<R>) r -> true;
}
protected void initAddedRow(R row) {
if (getModel().getTaxonGroupFilter() != null) {
row.setTaxonGroup(getModel().getTaxonGroupFilter());
}
if (getModel().getTaxonFilter() != null) {
row.setTaxon(getModel().getTaxonFilter());
}
}
@Override
protected void onRowModified(int rowIndex, R row, String propertyName, Integer propertyIndex, Object oldValue, Object newValue) {
// if a value of individual pmfm changes
if (AbstractMeasurementsGroupedRowModel.PROPERTY_INDIVIDUAL_PMFMS.equals(propertyName)) {
// no need to tell the table is modified if no pmfms value change
if (oldValue == newValue) return;
// if a value has been changed, update other editor with preconditions
if (!getModel().isAdjusting())
updatePmfmCellEditors(row, propertyIndex, newValue != null);
}
// update taxon when taxon group is updated
if (AbstractMeasurementsGroupedRowModel.PROPERTY_TAXON_GROUP.equals(propertyName)) {
// filter taxon
updateTaxonCellEditor(row, false);
// reset measurementId if no more taxon group or new taxon group
if ((newValue == null ^ oldValue == null) && row.getTaxon() == null) {
resetIndividualMeasurementIds(row);
}
}
if (AbstractMeasurementsGroupedRowModel.PROPERTY_TAXON.equals(propertyName)) {
// filter taxon
updateTaxonGroupCellEditor(row, false);
// reset measurementId if no more taxon or new taxon
if ((newValue == null ^ oldValue == null) && row.getTaxonGroup() == null) {
resetIndividualMeasurementIds(row);
}
// update input taxon
TaxonDTO taxon = (TaxonDTO) newValue;
row.setInputTaxonId(taxon != null ? taxon.getId() : null);
row.setInputTaxonName(taxon != null ? taxon.getName() : null);
}
// fire modify event
super.onRowModified(rowIndex, row, propertyName, propertyIndex, oldValue, newValue);
// if value has changed, process save to bean
if (oldValue != newValue) {
// save modifications ot the row to bean
saveMeasurementsInModel(row);
// also recompute valid state on all rows
recomputeRowsValidState();
}
}
protected void resetIndividualMeasurementIds(R row) {
if (row != null && CollectionUtils.isNotEmpty(row.getIndividualMeasurements())) {
for (MeasurementDTO individualMeasurement : row.getIndividualMeasurements()) {
individualMeasurement.setId(null);
}
}
}
public void saveMeasurementsInModel(R row) {
Assert.notNull(row);
if (!row.isValid()) return;
MeasurementAware bean = getMeasurementAwareModelForRow(row);
Assert.notNull(bean);
// existing measurements in bean (for this individualId)
List<MeasurementDTO> remainingMeasurements = ReefDbBeans.filterCollection(bean.getIndividualMeasurements(),
input -> input != null && input.getId() != null && Objects.equals(row.getIndividualId(), input.getIndividualId()));
// get stored individual measurement by id
List<Integer> existingIndividualMeasurementIds = ReefDbBeans.collectIds(bean.getIndividualMeasurements());
// get the minimum negative measurement id
int minNegativeId = CollectionUtils.isEmpty(existingIndividualMeasurementIds) ? 0 : Math.min(Collections.min(existingIndividualMeasurementIds), 0);
// iterate individual measurement in row
for (MeasurementDTO individualMeasurement : row.getIndividualMeasurements()) {
if (individualMeasurement.getId() == null) {
// this measurement was not in bean, so add it with next negative id
individualMeasurement.setId(--minNegativeId);
// add to bean
bean.getIndividualMeasurements().add(individualMeasurement);
}
// update this measurement
updateMeasurementFromRow(individualMeasurement, row);
// remove if existing
remainingMeasurements.remove(individualMeasurement);
}
// Remove remaining
bean.getIndividualMeasurements().removeAll(remainingMeasurements);
setDirty(bean);
}
private void setDirty(Object bean) {
if (bean instanceof DirtyAware) ((DirtyAware) bean).setDirty(true);
}
protected void updateMeasurementFromRow(MeasurementDTO measurement, R row) {
measurement.setIndividualId(row.getIndividualId());
measurement.setTaxonGroup(row.getTaxonGroup());
measurement.setTaxon(row.getTaxon());
measurement.setInputTaxonId(row.getInputTaxonId());
measurement.setInputTaxonName(row.getInputTaxonName());
measurement.setComment(row.getComment());
measurement.setAnalyst(row.getAnalyst());
}
public void removeIndividualMeasurements() {
if (getModel().getSelectedRows().isEmpty()) {
LOG.warn("Aucune mesure de selectionnee");
return;
}
// Selected measurement
List<MeasurementDTO> measurementToDelete = Lists.newArrayList();
for (final R row : getModel().getSelectedRows()) {
if (CollectionUtils.isNotEmpty(row.getIndividualMeasurements())) {
measurementToDelete.addAll(row.getIndividualMeasurements());
}
}
if (askBeforeDelete(t("reefdb.action.delete.survey.measurement.titre"), t("reefdb.action.delete.survey.measurement.message"))) {
// Remove from table
getModel().deleteSelectedRows();
// Remove from model
removeIndividualMeasurements(measurementToDelete);
recomputeRowsValidState();
}
}
protected abstract void removeIndividualMeasurements(List<MeasurementDTO> measurementToDelete);
/* validation section */
/**
* Call this method only before save to perform perfect duplicate check
* It can be too long if many rows (see Mantis #50040)
*/
@Override
public void recomputeRowsValidState() {
// recompute individual row valid state
super.recomputeRowsValidState();
// and check perfect duplicates
hasNoTaxonPerfectDuplicates();
}
@Override
protected boolean isRowValid(R row) {
boolean valid = super.isRowValid(row);
if (!valid && !row.isMandatoryValid()) {
// check invalid mandatory errors
new ArrayList<>(row.getInvalidMandatoryIdentifiers()).forEach(invalidIdentifier -> {
if (row.getMultipleValuesOnIdentifier().contains(invalidIdentifier)) {
// if this identifier has multiple value, remove error
row.getErrors().removeIf(error -> error.getPropertyName().size() == 1 && error.getPropertyName().contains(invalidIdentifier.getPropertyName()));
row.getInvalidMandatoryIdentifiers().remove(invalidIdentifier);
}
});
valid = row.isMandatoryValid();
}
boolean noUnicityDuplicates = hasNoUnicityDuplicates(row);
boolean noPreconditionErrors = hasNoPreconditionErrors(row);
boolean noGroupedRuleErrors = hasNoGroupedRuleErrors(row);
boolean hasAnalyst = hasAnalyst(row);
return valid && noUnicityDuplicates && noPreconditionErrors && noGroupedRuleErrors && hasAnalyst;
}
protected boolean hasAnalyst(R row) {
if (!row.getMultipleValuesOnIdentifier().contains(AbstractMeasurementsGroupedTableModel.ANALYST)
&& row.getAnalyst() == null && row.getIndividualMeasurements().stream().anyMatch(this::isMeasurementNotEmpty)) {
ReefDbBeans.addError(row,
t("reefdb.validator.error.analyst.required"),
AbstractMeasurementsGroupedRowModel.PROPERTY_ANALYST);
return false;
}
return true;
}
protected boolean isMeasurementNotEmpty(MeasurementDTO measurement) {
return !ReefDbBeans.isMeasurementEmpty(measurement);
}
/**
* Check and set invalid rows which are perfect duplicates (Mantis #50040)
*/
protected void hasNoTaxonPerfectDuplicates() {
if (getModel().getRowCount() < 2) {
// no need to check duplicates
return;
}
Map<String, List<R>> duplicatedMap = getModel().getRows().stream().collect(Collectors.groupingBy(r -> r.rowWithMeasurementsHashCode()));
duplicatedMap.entrySet().stream().filter(entry -> entry.getValue().size() > 1)
.forEach(entry -> entry.getValue().forEach(duplicatedRow -> {
ReefDbBeans.addWarning(duplicatedRow,
t("reefdb.measurement.grouped.duplicates"), duplicatedRow.getDefaultProperties().toArray(new String[0]));
ReefDbBeans.getPmfmIdsOfNonEmptyIndividualMeasurements(duplicatedRow).forEach(pmfmId -> ReefDbBeans.addWarning(duplicatedRow,
t("reefdb.measurement.grouped.duplicates"),
pmfmId,
AbstractMeasurementsGroupedRowModel.PROPERTY_INDIVIDUAL_PMFMS));
}));
// old algo (too long with many rows)
// for (R row : getModel().getRows()) {
// for (R otherRow : getModel().getRows()) {
// if (otherRow == row /*|| (otherRow.getTaxonGroup() == null && otherRow.getTaxon() == null)*/) continue;
//
// if (row.isSameRow(otherRow)
// // Both measurements must have same non empty measurements (Mantis #42170)
// && ReefDbBeans.haveSameMeasurements(row.getIndividualMeasurements(), otherRow.getIndividualMeasurements())
// ) {
// // if rows equals, check all measurement values
// boolean allValuesEquals = true;
// Set<Integer> notNullMeasurementPmfmIds = Sets.newHashSet();
// for (MeasurementDTO measurement : row.getIndividualMeasurements()) {
//
// // find the measurement with same pmfm in the other row
// MeasurementDTO otherMeasurement = ReefDbBeans.getIndividualMeasurementByPmfmId(otherRow, measurement.getPmfm().getId());
//
// if (otherMeasurement != null) {
// if (measurement.getPmfm().getParameter().isQualitative()) {
// if (!Objects.equals(measurement.getQualitativeValue(), otherMeasurement.getQualitativeValue())) {
// allValuesEquals = false;
// break;
// }
// } else {
// if (!Objects.equals(measurement.getNumericalValue(), otherMeasurement.getNumericalValue())) {
// allValuesEquals = false;
// break;
// }
// }
// }
//
// // collect pmfm ids
// if (!ReefDbBeans.isMeasurementEmpty(measurement)) {
// notNullMeasurementPmfmIds.add(measurement.getPmfm().getId());
// }
// }
// if (allValuesEquals) {
//
// // Mantis #0026890 The check for perfect duplicates is now a warning
// ReefDbBeans.addWarning(row,
// t("reefdb.measurement.grouped.duplicates"), row.getDefaultProperties().toArray(new String[0]));
//
// for (Integer pmfmId : notNullMeasurementPmfmIds) {
// ReefDbBeans.addWarning(row,
// t("reefdb.measurement.grouped.duplicates"),
// pmfmId,
// AbstractMeasurementsGroupedRowModel.PROPERTY_INDIVIDUAL_PMFMS);
// }
// return;
// }
// }
// }
// }
}
protected boolean hasNoUnicityDuplicates(R row) {
// TODO: LP 08/01/2020: try to use same algo as hasNoTaxonPerfectDuplicates but it's a bit more difficult due to valid state to return on each row
// use rowWithMeasurementsHashCode(Collection<PmfmDTO> filterPmfms)
if (getModel().getRowCount() < 2
|| (row.getTaxonGroup() == null && row.getTaxon() == null)
|| CollectionUtils.isEmpty(getModel().getUniquePmfms())) {
// no need to check duplicates
return true;
}
for (R otherRow : getModel().getRows()) {
if (otherRow == row || (otherRow.getTaxonGroup() == null && otherRow.getTaxon() == null)) continue;
if (row.isSameRow(otherRow)
// TODO : ce bug n'a pas été trouvé !
// && row.getIndividualMeasurements().size() == otherRow.getIndividualMeasurements().size()
) {
// if rows are equals, check measurement values with unique pmfms
for (PmfmDTO uniquePmfm : getModel().getUniquePmfms()) {
// find the measurement with this pmfm in the row
MeasurementDTO measurement = ReefDbBeans.getIndividualMeasurementByPmfmId(row, uniquePmfm.getId());
if (measurement == null) {
continue;
}
// find the measurement with this pmfm in the other row
MeasurementDTO otherMeasurement = ReefDbBeans.getIndividualMeasurementByPmfmId(otherRow, uniquePmfm.getId());
if (otherMeasurement == null) {
continue;
}
if ((measurement.getPmfm().getParameter().isQualitative() && Objects.equals(measurement.getQualitativeValue(), otherMeasurement.getQualitativeValue()))
|| (!measurement.getPmfm().getParameter().isQualitative() && Objects.equals(measurement.getNumericalValue(), otherMeasurement.getNumericalValue()))) {
// duplicate value found
List<String> properties = row.getDefaultProperties();
properties.add(AbstractMeasurementsGroupedRowModel.PROPERTY_INDIVIDUAL_PMFMS);
ReefDbBeans.addError(row,
t("reefdb.measurement.grouped.duplicates.taxonUnique", decorate(uniquePmfm, DecoratorService.NAME_WITH_UNIT)),
uniquePmfm.getId(), properties.toArray(new String[0]));
return false;
}
}
}
}
return true;
}
protected boolean hasNoPreconditionErrors(R row) {
if (row.getPreconditionErrors().isEmpty()) return true;
row.getErrors().addAll(row.getPreconditionErrors());
return true;
}
/* grouped rules related methods */
private void detectGroupedIdentifiers() {
if (CollectionUtils.isEmpty(getModel().getSurvey().getGroupedRules())) return;
for (ControlRuleDTO groupedRule : getModel().getSurvey().getGroupedRules()) {
boolean allIdentifiersFound = true;
List<Map.Entry<RuleGroupDTO, ? extends ReefDbColumnIdentifier<R>>> identifierByGroup = new ArrayList<>();
for (RuleGroupDTO group : groupedRule.getGroups()) {
ControlRuleDTO rule = group.getRule();
if (ControlElementValues.MEASUREMENT.equals(rule.getControlElement())) {
if (ControlFeatureMeasurementValues.TAXON_GROUP.equals(rule.getControlFeature())) {
ReefDbColumnIdentifier<R> identifier = findColumnIdentifierByPropertyName(AbstractMeasurementsGroupedRowModel.PROPERTY_TAXON_GROUP);
if (identifier != null) {
identifierByGroup.add(Maps.immutableEntry(group, identifier));
} else {
allIdentifiersFound = false;
break;
}
} else if (ControlFeatureMeasurementValues.TAXON.equals(rule.getControlFeature())) {
ReefDbColumnIdentifier<R> identifier = findColumnIdentifierByPropertyName(AbstractMeasurementsGroupedRowModel.PROPERTY_TAXON);
if (identifier != null) {
identifierByGroup.add(Maps.immutableEntry(group, identifier));
} else {
allIdentifiersFound = false;
break;
}
} else if (ControlFeatureMeasurementValues.PMFM.equals(rule.getControlFeature())) {
for (PmfmDTO pmfm : rule.getRulePmfms().stream().map(RulePmfmDTO::getPmfm).collect(Collectors.toList())) {
PmfmTableColumn column = findPmfmColumnByPmfmId(getModel().getPmfmColumns(), pmfm.getId());
if (column != null) {
identifierByGroup.add(Maps.immutableEntry(group, column.getPmfmIdentifier()));
} else {
allIdentifiersFound = false;
break;
}
}
}
}
}
if (allIdentifiersFound) {
getModel().getIdentifiersByGroupedRuleMap().putAll(groupedRule, identifierByGroup);
}
}
}
protected boolean hasNoGroupedRuleErrors(R row) {
if (getModel().getIdentifiersByGroupedRuleMap().isEmpty()) return true;
boolean result = true;
for (ControlRuleDTO groupedRule : getModel().getIdentifiersByGroupedRuleMap().keySet()) {
boolean groupResult = false;
for (Map.Entry<RuleGroupDTO, ? extends ReefDbColumnIdentifier<R>> identifierByGroup : getModel().getIdentifiersByGroupedRuleMap().get(groupedRule)) {
RuleGroupDTO group = identifierByGroup.getKey();
ReefDbColumnIdentifier<R> identifier = identifierByGroup.getValue();
// For now, assume logical operator is always OR
Assert.isTrue(group.isIsOr());
groupResult |= getContext().getControlRuleService().controlUniqueObject(group.getRule(), identifier.getValue(row));
}
if (!groupResult) {
// build error message
String message;
List<String> columnNames = new ArrayList<>();
Set<String> propertyNames = new HashSet<>();
Set<String> pmfmPropertyNames = new HashSet<>();
Set<Integer> pmfmIds = new HashSet<>();
for (RuleGroupDTO group : groupedRule.getGroups()) {
if (ReefDbBeans.isPmfmMandatory(group.getRule())) {
// iterate pmfms
for (PmfmDTO pmfm : group.getRule().getRulePmfms().stream().map(RulePmfmDTO::getPmfm).collect(Collectors.toList())) {
ReefDbPmfmColumnIdentifier<R> pmfmIdentifier = findPmfmColumnByPmfmId(getModel().getPmfmColumns(), pmfm.getId()).getPmfmIdentifier();
columnNames.add(pmfmIdentifier.getHeaderLabel());
pmfmPropertyNames.add(pmfmIdentifier.getPropertyName());
pmfmIds.add(pmfm.getId());
}
} else {
ReefDbColumnIdentifier<R> identifier = getModel().getIdentifierByGroup(group);
columnNames.add(t(identifier.getHeaderI18nKey()));
propertyNames.add(identifier.getPropertyName());
}
}
if (StringUtils.isNotBlank(groupedRule.getMessage())) {
message = groupedRule.getMessage();
} else {
message = t("reefdb.measurement.grouped.invalidGroupedRule", Joiner.on(',').join(columnNames));
}
// add error on normal column
ReefDbBeans.addError(row, message, propertyNames.toArray(new String[0]));
// add error on pmfm column
if (!pmfmIds.isEmpty())
for (Integer pmfmId : pmfmIds)
ReefDbBeans.addError(row, message, pmfmId, pmfmPropertyNames.toArray(new String[0]));
}
// if a group result is false, the result is false (= has error)
result &= groupResult;
}
return result;
}
/* Preconditions related methods */
private void detectPreconditionedPmfms() {
if (CollectionUtils.isEmpty(getModel().getSurvey().getPreconditionedRules())) return;
for (ControlRuleDTO preconditionedRule : getModel().getSurvey().getPreconditionedRules()) {
for (PreconditionRuleDTO precondition : preconditionedRule.getPreconditions()) {
int basePmfmId = precondition.getBaseRule().getRulePmfms(0).getPmfm().getId();
int usedPmfmId = precondition.getUsedRule().getRulePmfms(0).getPmfm().getId();
getModel().addPreconditionRuleByPmfmId(basePmfmId, precondition);
if (precondition.isBidirectional())
getModel().addPreconditionRuleByPmfmId(usedPmfmId, precondition);
}
}
}
/**
* Update all editors on measurements if they have preconditions
*
* @param row the actual selected row
* @param firstPmfmId the first pmfm Id to treat (can be null)
* @param resetValueAllowed allow or not the target value to be reset
*/
private void updatePmfmCellEditors(R row