diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e0af16951..d4a3232cff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [4.0.91] - 2024-04-13 +### Added +- CSV data export: added option to always evaluate calculated attributes (useful for CE surveys); + ## [4.0.90] - 2024-03-11 ### Added - Coordinate attribute: get current location from browser; diff --git a/collect-core/src/main/java/org/openforis/collect/io/data/CSVDataExportJob.java b/collect-core/src/main/java/org/openforis/collect/io/data/CSVDataExportJob.java index 48f6c0499d..7bb59fde92 100644 --- a/collect-core/src/main/java/org/openforis/collect/io/data/CSVDataExportJob.java +++ b/collect-core/src/main/java/org/openforis/collect/io/data/CSVDataExportJob.java @@ -149,7 +149,8 @@ private void exportData(OutputStream outputStream, int entityDefId) throws Inval List summaries = recordManager.loadSummaries(recordFilter); for (CollectRecordSummary s : summaries) { if ( isRunning() ) { - CollectRecord record = recordManager.load(survey, s.getId(), recordFilter.getStepGreaterOrEqual(), false); + CollectRecord record = recordManager.load(survey, s.getId(), recordFilter.getStepGreaterOrEqual(), + false, parameters.isAlwaysEvaluateCalculatedAttributes()); modelWriter.printData(record); incrementProcessedItems(); } else { diff --git a/collect-core/src/main/java/org/openforis/collect/io/data/csv/CSVDataExportParametersBase.java b/collect-core/src/main/java/org/openforis/collect/io/data/csv/CSVDataExportParametersBase.java index 297690aa78..be1cef2e6a 100644 --- a/collect-core/src/main/java/org/openforis/collect/io/data/csv/CSVDataExportParametersBase.java +++ b/collect-core/src/main/java/org/openforis/collect/io/data/csv/CSVDataExportParametersBase.java @@ -1,17 +1,17 @@ package org.openforis.collect.io.data.csv; public abstract class CSVDataExportParametersBase { - + public enum HeadingSource { ATTRIBUTE_NAME, INSTANCE_LABEL, REPORTING_LABEL; } - + public enum OutputFormat { CSV, XLSX } - + private Integer entityId; - + private boolean alwaysGenerateZipFile = false; private String multipleAttributeValueSeparator = ", "; private String fieldHeadingSeparator = "_"; @@ -25,6 +25,7 @@ public enum OutputFormat { private boolean includeCreatedByUserColumn = false; private boolean codeAttributeExpanded = false; private boolean includeImages = false; + private boolean alwaysEvaluateCalculatedAttributes = false; private int maxMultipleAttributeValues = 10; private int maxExpandedCodeAttributeItems = 30; private HeadingSource headingSource = HeadingSource.ATTRIBUTE_NAME; @@ -38,7 +39,7 @@ public Integer getEntityId() { public void setEntityId(Integer entityId) { this.entityId = entityId; } - + public boolean isAlwaysGenerateZipFile() { return alwaysGenerateZipFile; } @@ -46,30 +47,28 @@ public boolean isAlwaysGenerateZipFile() { public void setAlwaysGenerateZipFile(boolean alwaysGenerateZipFile) { this.alwaysGenerateZipFile = alwaysGenerateZipFile; } - + public String getMultipleAttributeValueSeparator() { return multipleAttributeValueSeparator; } - - public void setMultipleAttributeValueSeparator( - String multipleAttributeValueSeparator) { + + public void setMultipleAttributeValueSeparator(String multipleAttributeValueSeparator) { this.multipleAttributeValueSeparator = multipleAttributeValueSeparator; } - + public String getFieldHeadingSeparator() { return fieldHeadingSeparator; } - + public void setFieldHeadingSeparator(String fieldHeadingSeparator) { this.fieldHeadingSeparator = fieldHeadingSeparator; } - + public boolean isIncludeCodeItemPositionColumn() { return includeCodeItemPositionColumn; } - public void setIncludeCodeItemPositionColumn( - boolean includeCodeItemPositionColumn) { + public void setIncludeCodeItemPositionColumn(boolean includeCodeItemPositionColumn) { this.includeCodeItemPositionColumn = includeCodeItemPositionColumn; } @@ -77,8 +76,7 @@ public boolean isIncludeKMLColumnForCoordinates() { return includeKMLColumnForCoordinates; } - public void setIncludeKMLColumnForCoordinates( - boolean includeKMLColumnForCoordinates) { + public void setIncludeKMLColumnForCoordinates(boolean includeKMLColumnForCoordinates) { this.includeKMLColumnForCoordinates = includeKMLColumnForCoordinates; } @@ -97,93 +95,99 @@ public boolean isIncludeAllAncestorAttributes() { public void setIncludeAllAncestorAttributes(boolean includeAllAncestorAttributes) { this.includeAllAncestorAttributes = includeAllAncestorAttributes; } - + public boolean isIncludeCodeItemLabelColumn() { return includeCodeItemLabelColumn; } - + public void setIncludeCodeItemLabelColumn(boolean includeCodeItemLabelColumn) { this.includeCodeItemLabelColumn = includeCodeItemLabelColumn; } - + + public boolean isAlwaysEvaluateCalculatedAttributes() { + return alwaysEvaluateCalculatedAttributes; + } + + public void setAlwaysEvaluateCalculatedAttributes(boolean alwaysEvaluateCalculatedAttributes) { + this.alwaysEvaluateCalculatedAttributes = alwaysEvaluateCalculatedAttributes; + } + public int getMaxMultipleAttributeValues() { return maxMultipleAttributeValues; } - + public void setMaxMultipleAttributeValues(int maxMultipleAttributeValues) { this.maxMultipleAttributeValues = maxMultipleAttributeValues; } - + public boolean isCodeAttributeExpanded() { return codeAttributeExpanded; } - + public void setCodeAttributeExpanded(boolean codeAttributeExpanded) { this.codeAttributeExpanded = codeAttributeExpanded; } - + public boolean isIncludeCompositeAttributeMergedColumn() { return includeCompositeAttributeMergedColumn; } - - public void setIncludeCompositeAttributeMergedColumn( - boolean includeCompositeAttributeMergedColumn) { + + public void setIncludeCompositeAttributeMergedColumn(boolean includeCompositeAttributeMergedColumn) { this.includeCompositeAttributeMergedColumn = includeCompositeAttributeMergedColumn; } - + public int getMaxExpandedCodeAttributeItems() { return maxExpandedCodeAttributeItems; } - - public void setMaxExpandedCodeAttributeItems( - int maxExpandedCodeAttributeItems) { + + public void setMaxExpandedCodeAttributeItems(int maxExpandedCodeAttributeItems) { this.maxExpandedCodeAttributeItems = maxExpandedCodeAttributeItems; } - + public HeadingSource getHeadingSource() { return headingSource; } - + public void setHeadingSource(HeadingSource headingSource) { this.headingSource = headingSource; } - + public String getLanguageCode() { return languageCode; } - + public void setLanguageCode(String languageCode) { this.languageCode = languageCode; } - + public boolean isIncludeGroupingLabels() { return includeGroupingLabels; } - + public void setIncludeGroupingLabels(boolean includeGroupingLabels) { this.includeGroupingLabels = includeGroupingLabels; } - + public boolean isIncludeCreatedByUserColumn() { return includeCreatedByUserColumn; } - + public void setIncludeCreatedByUserColumn(boolean includeCreatedByUserColumn) { this.includeCreatedByUserColumn = includeCreatedByUserColumn; } - + public boolean isIncludeImages() { return this.includeImages; } - + public void setIncludeImages(boolean includeImages) { this.includeImages = includeImages; } - + public OutputFormat getOutputFormat() { return outputFormat; } - + public void setOutputFormat(OutputFormat outputFormat) { this.outputFormat = outputFormat; } diff --git a/collect-core/src/main/java/org/openforis/collect/manager/RecordManager.java b/collect-core/src/main/java/org/openforis/collect/manager/RecordManager.java index a732e828be..01435a317c 100644 --- a/collect-core/src/main/java/org/openforis/collect/manager/RecordManager.java +++ b/collect-core/src/main/java/org/openforis/collect/manager/RecordManager.java @@ -351,8 +351,12 @@ public CollectRecord load(CollectSurvey survey, int recordId, boolean validate) public CollectRecord load(CollectSurvey survey, int recordId, Step step) { return load(survey, recordId, step, true); } - + public CollectRecord load(CollectSurvey survey, int recordId, Step step, boolean validate) { + return load(survey, recordId, step, validate, false); + } + + public CollectRecord load(CollectSurvey survey, int recordId, Step step, boolean validate, boolean alwaysEvaluateCalculatedAttributes) { if (survey == null) { int surveyId = recordDao.loadSurveyId(recordId); survey = surveyManager.getOrLoadSurveyById(surveyId); @@ -365,6 +369,7 @@ public CollectRecord load(CollectSurvey survey, int recordId, Step step, boolean recordConverter.convertToLatestVersion(r); RecordUpdater updater = new RecordUpdater(); updater.setValidateAfterUpdate(validate); + updater.setAlwaysEvaluateCalculatedAttributes(alwaysEvaluateCalculatedAttributes); updater.initializeRecord(r); return r; } diff --git a/collect-core/src/main/java/org/openforis/collect/model/RecordUpdater.java b/collect-core/src/main/java/org/openforis/collect/model/RecordUpdater.java index 13ab7b62c0..d6fc23241b 100644 --- a/collect-core/src/main/java/org/openforis/collect/model/RecordUpdater.java +++ b/collect-core/src/main/java/org/openforis/collect/model/RecordUpdater.java @@ -1,6 +1,5 @@ package org.openforis.collect.model; - import static org.openforis.collect.model.CollectRecord.APPROVED_MISSING_POSITION; import static org.openforis.collect.model.CollectRecord.CONFIRMED_ERROR_POSITION; import static org.openforis.idm.model.NodePointers.nodesToPointers; @@ -60,50 +59,58 @@ * */ public class RecordUpdater { - + public static class RecordUpdateConfiguration { private boolean validateAfterUpdate = true; private boolean clearNotRelevantAttributes = false; private boolean clearDependentCodeAttributes = false; private boolean addEmptyMultipleEntitiesWhenAddingNewEntities = true; - + private boolean alwaysEvaluateCalculatedAttributes = false; + public boolean isValidateAfterUpdate() { return validateAfterUpdate; } - + public void setValidateAfterUpdate(boolean validateAfterUpdate) { this.validateAfterUpdate = validateAfterUpdate; } - + public boolean isClearDependentCodeAttributes() { return clearDependentCodeAttributes; } - + public void setClearDependentCodeAttributes(boolean clearDependentCodeAttributes) { this.clearDependentCodeAttributes = clearDependentCodeAttributes; } - + public boolean isClearNotRelevantAttributes() { return clearNotRelevantAttributes; } - + public void setClearNotRelevantAttributes(boolean clearNotRelevantAttributes) { this.clearNotRelevantAttributes = clearNotRelevantAttributes; } - + public boolean isAddEmptyMultipleEntitiesWhenAddingNewEntities() { return addEmptyMultipleEntitiesWhenAddingNewEntities; } - + public void setAddEmptyMultipleEntitiesWhenAddingNewEntities( boolean addEmptyMultipleEntitiesWhenAddingNewEntities) { this.addEmptyMultipleEntitiesWhenAddingNewEntities = addEmptyMultipleEntitiesWhenAddingNewEntities; } + + public boolean isAlwaysEvaluateCalculatedAttributes() { + return alwaysEvaluateCalculatedAttributes; + } + + public void setAlwaysEvaluateCalculatedAttributes(boolean alwaysEvaluateCalculatedAttributes) { + this.alwaysEvaluateCalculatedAttributes = alwaysEvaluateCalculatedAttributes; + } } - + private RecordUpdateConfiguration configuration = new RecordUpdateConfiguration(); - - + /** * Updates an attribute with a new value */ @@ -113,84 +120,86 @@ public NodeChangeSet updateAttribute(Attribute attribute attribute.updateSummaryInfo(); return afterAttributeUpdate(attribute); } - + /** * Updates an attribute and sets the specified FieldSymbol on every field */ - public NodeChangeSet updateAttribute(Attribute attribute, FieldSymbol symbol) { + public NodeChangeSet updateAttribute(Attribute attribute, FieldSymbol symbol) { beforeAttributeUpdate(attribute); attribute.clearValue(); setSymbolOnFields(attribute, symbol); attribute.updateSummaryInfo(); return afterAttributeUpdate(attribute); } - + public NodeChangeSet updateMultipleAttribute(Entity parentEntity, AttributeDefinition attrDef, List values) { CollectRecord record = (CollectRecord) parentEntity.getRecord(); NodePointer selfNodePointer = new NodePointer(parentEntity, attrDef); - + NodeChangeMap changeMap = new NodeChangeMap(); - + List> children = parentEntity.getChildren(attrDef); Set> oldAttributes = new HashSet>(children); - + Set dependentPointers = record.determineDependenciesThatRequireUpdates(oldAttributes); - - //delete old values + + // delete old values for (Node child : oldAttributes) { List ancestorIds = child.getAncestorIds(); performNodeDeletion(child); changeMap.addNodeDeleteChange(record.getId(), record.getStep(), ancestorIds, parentEntity.getPath(), child); } - //add new values - Collection> newAttrs = new ArrayList>(values.size()); + // add new values + Collection> newAttrs = new ArrayList>(values.size()); for (Value value : values) { Attribute a = performAttributeAdd(parentEntity, attrDef, value, null, null); newAttrs.add(a); changeMap.addAttributeAddChange(a); } dependentPointers.addAll(NodePointers.nodesToPointers(newAttrs)); - + // re-calculate values and relevance - RecordDependentsUpdateResult dependentsUpdateResult = new RecordDependentsUpdater(configuration).updateDependents(record, dependentPointers); + RecordDependentsUpdateResult dependentsUpdateResult = new RecordDependentsUpdater(configuration) + .updateDependents(record, dependentPointers); List> dependentUpdatedAttributes = dependentsUpdateResult.getUpdatedAttributes(); changeMap.addValueChanges(dependentUpdatedAttributes); - + Set updatedRelevancePointers = dependentsUpdateResult.getUpdatedRelevancePointers(); changeMap.addRelevanceChanges(updatedRelevancePointers); if (configuration.validateAfterUpdate) { - List> updatedAttributes = new ArrayList>(); + List> updatedAttributes = new ArrayList>(); updatedAttributes.addAll(newAttrs); updatedAttributes.addAll(dependentUpdatedAttributes); - + List ancestorsAndSelfPointers = getAncestorsAndSelfPointers(selfNodePointer); - + performValidationAfterUpdate(selfNodePointer, ancestorsAndSelfPointers, updatedAttributes, - updatedRelevancePointers, Collections.emptySet(), - Collections.emptySet(), Collections.>emptySet(), changeMap); + updatedRelevancePointers, Collections.emptySet(), Collections.emptySet(), + Collections.>emptySet(), changeMap); } return changeMap; } - + /** - * Updates a field with a new value. - * The value will be parsed according to field data type. + * Updates a field with a new value. The value will be parsed according to field + * data type. */ public NodeChangeSet updateField(Field field, V value) { Attribute attribute = field.getAttribute(); beforeAttributeUpdate(attribute); - + field.setValue(value); attribute.updateSummaryInfo(); - + return afterAttributeUpdate(attribute); } /** * Updates a field with a new symbol. - * @param clearChildCodeAttributes + * + * @param clearChildCodeAttributes */ public NodeChangeSet updateField(Field field, FieldSymbol symbol) { Attribute attribute = field.getAttribute(); @@ -203,14 +212,14 @@ public NodeChangeSet updateField(Field field, FieldSymbol symbol) { return afterAttributeUpdate(attribute); } - + public NodeChangeSet addNode(Entity parentEntity, String nodeName) { NodeDefinition nodeDef = parentEntity.getDefinition().getChildDefinition(nodeName); return addNode(parentEntity, nodeDef); } public NodeChangeSet addNode(Entity parentEntity, NodeDefinition nodeDef) { - if ( nodeDef instanceof EntityDefinition ) { + if (nodeDef instanceof EntityDefinition) { return addEntity(parentEntity, (EntityDefinition) nodeDef); } else { return addAttribute(parentEntity, (AttributeDefinition) nodeDef); @@ -222,25 +231,26 @@ public NodeChangeSet addNode(Entity parentEntity, NodeDefinition nodeDef) { * * @param parentEntity * @param entityName - * @return Changes applied to the record + * @return Changes applied to the record */ public NodeChangeSet addEntity(Entity parentEntity, String entityName) { - EntityDefinition entityDef = parentEntity.getDefinition().getChildDefinition(entityName, EntityDefinition.class); + EntityDefinition entityDef = parentEntity.getDefinition().getChildDefinition(entityName, + EntityDefinition.class); return addEntity(parentEntity, entityDef); } - + public NodeChangeSet addEntity(Entity parentEntity, EntityDefinition entityDef) { Entity entity = performEntityAdd(parentEntity, entityDef, null); - + setMissingValueApproved(parentEntity, entityDef.getName(), false); NodeChangeMap changeMap = initializeEntity(entity, true); return changeMap; } - + public NodeChangeSet addEntity(Entity parentEntity, Entity entity) { performEntityAdd(parentEntity, entity); - + setMissingValueApproved(parentEntity, entity.getName(), false); NodeChangeMap changeMap = initializeEntity(entity, false); @@ -250,32 +260,29 @@ public NodeChangeSet addEntity(Entity parentEntity, Entity entity) { public NodeChangeSet addAttribute(Entity parentEntity, String attributeName) { return addAttribute(parentEntity, attributeName, null, null, null); } - + public NodeChangeSet addAttribute(Entity parentEntity, String attributeName, Value value) { return addAttribute(parentEntity, attributeName, value, null, null); } - + public NodeChangeSet addAttribute(Entity parentEntity, AttributeDefinition attributeDef) { return addAttribute(parentEntity, attributeDef, null, null, null); } - + /** - * Adds a new attribute to a record. - * This attribute can be immediately populated with a value or with a FieldSymbol, and remarks. - * You cannot specify both value and symbol. + * Adds a new attribute to a record. This attribute can be immediately populated + * with a value or with a FieldSymbol, and remarks. You cannot specify both + * value and symbol. * - * @param parentEntity Parent entity of the attribute + * @param parentEntity Parent entity of the attribute * @param attributeName Name of the attribute definition - * @param value Value to set on the attribute - * @param symbol FieldSymbol to set on each field of the attribute - * @param remarks Remarks to set on each field of the attribute - * @return Changes applied to the record + * @param value Value to set on the attribute + * @param symbol FieldSymbol to set on each field of the attribute + * @param remarks Remarks to set on each field of the attribute + * @return Changes applied to the record */ - public NodeChangeSet addAttribute(Entity parentEntity, - String attributeName, - Value value, - FieldSymbol symbol, - String remarks) { + public NodeChangeSet addAttribute(Entity parentEntity, String attributeName, Value value, FieldSymbol symbol, + String remarks) { EntityDefinition parentEntityDefn = parentEntity.getDefinition(); AttributeDefinition attributeDef = (AttributeDefinition) parentEntityDefn.getChildDefinition(attributeName); return addAttribute(parentEntity, attributeDef, value, symbol, remarks); @@ -284,16 +291,16 @@ public NodeChangeSet addAttribute(Entity parentEntity, public NodeChangeSet addAttribute(Entity parentEntity, AttributeDefinition attributeDef, Value value, FieldSymbol symbol, String remarks) { Attribute attribute = performAttributeAdd(parentEntity, attributeDef, value, symbol, remarks); - + setMissingValueApproved(parentEntity, attribute.getName(), false); - + if (value == null) { applyInitialValue(attribute); } - + NodeChangeMap changeMap = new NodeChangeMap(); changeMap.addAttributeAddChange(attribute); - + return afterAttributeInsertOrUpdate(changeMap, attribute); } @@ -322,24 +329,24 @@ public NodeChangeSet approveMissingValue(Entity parentEntity, String nodeName) { } public NodeChangeSet confirmError(Attribute attribute) { - Set> checkDependencies = new HashSet>(); + Set> checkDependencies = new HashSet>(); setErrorConfirmed(attribute, true); checkDependencies.add(attribute); - + NodeChangeMap changeMap = new NodeChangeMap(); changeMap.prepareAttributeChange(attribute); - - Set> attributesToRevalidate = new HashSet>(); + + Set> attributesToRevalidate = new HashSet>(); attributesToRevalidate.add(attribute); - + validateAttributes(attribute.getRecord(), attributesToRevalidate, changeMap); return changeMap; } - + /** - * Applies the default value to an attribute, if any. - * The applied default value will be the first one having verified the "condition". - * + * Applies the default value to an attribute, if any. The applied default value + * will be the first one having verified the "condition". + * * @param attribute * @return */ @@ -360,40 +367,42 @@ private NodeChangeSet afterAttributeUpdate(Attribute attribute) { changeMap.addValueChange(attribute); return afterAttributeInsertOrUpdate(changeMap, attribute); } - + private NodeChangeSet afterAttributeInsertOrUpdate(final NodeChangeMap changeMap, Attribute attribute) { CollectRecord record = (CollectRecord) attribute.getRecord(); CollectSurvey survey = (CollectSurvey) record.getSurvey(); NodePointer selfPointer = new NodePointer(attribute); - + RecordDependentsUpdater recordDependentsUpdater = new RecordDependentsUpdater(configuration); - RecordDependentsUpdateResult recordDependentsUpdateResult = recordDependentsUpdater.updateDependents(record, selfPointer, true); - - List> updatedAttributes = new ArrayList>(); + RecordDependentsUpdateResult recordDependentsUpdateResult = recordDependentsUpdater.updateDependents(record, + selfPointer, true); + + List> updatedAttributes = new ArrayList>(); updatedAttributes.add(attribute); updatedAttributes.addAll(recordDependentsUpdateResult.getUpdatedAttributes()); changeMap.addValueChanges(updatedAttributes); - + if (survey.isCollectEarth()) { - // include dependent code attributes in change map (allows the re-rendering of code list items in Collect Earth) + // include dependent code attributes in change map (allows the re-rendering of + // code list items in Collect Earth) Collection updatedCodeAttributes = Nodes.filterCodeAttributes(updatedAttributes); for (CodeAttribute codeAttribute : updatedCodeAttributes) { Set dependentCodeAttributes = record.determineDependentCodeAttributes(codeAttribute); changeMap.addValueChanges(dependentCodeAttributes); } } - + Set updatedRelevancePointers = recordDependentsUpdateResult.getUpdatedRelevancePointers(); changeMap.addRelevanceChanges(updatedRelevancePointers); - + List ancestorsAndSelfPointers = getAncestorsAndSelfPointers(selfPointer); if (configuration.validateAfterUpdate) { Set minCountDependenciesToSelf = new HashSet(); Set maxCountDependenciesToSelf = new HashSet(); - Set> validationDependenciesToSelf = new HashSet>(); - + Set> validationDependenciesToSelf = new HashSet>(); + performValidationAfterUpdate(selfPointer, ancestorsAndSelfPointers, updatedAttributes, updatedRelevancePointers, minCountDependenciesToSelf, maxCountDependenciesToSelf, validationDependenciesToSelf, changeMap); @@ -403,9 +412,10 @@ private NodeChangeSet afterAttributeInsertOrUpdate(final NodeChangeMap changeMap private void validateAttributes(Record record, Set> attributes, NodeChangeMap changeMap) { Validator validator = record.getSurveyContext().getValidator(); - + for (Attribute a : attributes) { - ValidationResults validationResultsNew = a.isRelevantInsideAncestors() ? validator.validate(a) : new ValidationResults(); + ValidationResults validationResultsNew = a.isRelevantInsideAncestors() ? validator.validate(a) + : new ValidationResults(); ValidationResults validationResultsOld = a.getValidationResults(); if (validationResultsOld == null && !validationResultsNew.isEmpty() || validationResultsOld != null && !validationResultsNew.equals(validationResultsOld)) { @@ -423,7 +433,7 @@ private Collection updateMinCount(Collection nodePoint Integer oldCount = entity.getMinCount(childDef); int newCount = calculateMinCount(nodePointer); entity.setMinCount(childDef, newCount); - if ( oldCount == null || oldCount.intValue() != newCount ) { + if (oldCount == null || oldCount.intValue() != newCount) { updatedPointers.add(nodePointer); } } @@ -439,34 +449,35 @@ private Collection updateMaxCount(Collection nodePoint Integer oldCount = entity.getMaxCount(childDef); int newCount = calculateMaxCount(nodePointer); entity.setMaxCount(childDef, newCount); - if ( ! ObjectUtils.equals(oldCount, newCount) ) { + if (!ObjectUtils.equals(oldCount, newCount)) { updatedPointers.add(nodePointer); } } return updatedPointers; } - private Set validateCardinality(Record record, Collection pointers, NodeChangeMap changeMap) { + private Set validateCardinality(Record record, Collection pointers, + NodeChangeMap changeMap) { Set updatedPointers = new HashSet(); Validator validator = record.getSurveyContext().getValidator(); for (NodePointer nodePointer : pointers) { Entity entity = nodePointer.getEntity(); NodeDefinition childDef = nodePointer.getChildDefinition(); - + ValidationResultFlag minCountResult, maxCountResult; - - if ( entity.isRelevant() && entity.isRelevant(childDef) ) { + + if (entity.isRelevant() && entity.isRelevant(childDef)) { minCountResult = validator.validateMinCount(entity, childDef); maxCountResult = validator.validateMaxCount(entity, childDef); } else { minCountResult = maxCountResult = ValidationResultFlag.OK; } - if ( entity.getMinCountValidationResult(childDef) != minCountResult ) { + if (entity.getMinCountValidationResult(childDef) != minCountResult) { entity.setMinCountValidationResult(childDef, minCountResult); changeMap.addMinCountValidationResultChange(nodePointer, minCountResult); updatedPointers.add(nodePointer); } - if ( entity.getMaxCountValidationResult(childDef) != maxCountResult ) { + if (entity.getMaxCountValidationResult(childDef) != maxCountResult) { entity.setMaxCountValidationResult(childDef, maxCountResult); changeMap.addMaxCountValidationResultChange(nodePointer, maxCountResult); updatedPointers.add(nodePointer); @@ -474,10 +485,10 @@ private Set validateCardinality(Record record, Collection 0) { + while (entity.getCount(childDef) > 0) { changeMap.addMergeChanges(deleteNode(entity.getLastChild(childDef))); } return changeMap; @@ -491,59 +502,63 @@ public NodeChangeSet deleteChildren(Entity entity, NodeDefinition childDef) { */ public NodeChangeSet deleteNode(Node node) { CollectRecord record = (CollectRecord) node.getRecord(); - + NodeChangeMap changeMap = new NodeChangeMap(); - + final Set> nodesToBeDeleted = new HashSet>(); nodesToBeDeleted.add(node); if (node instanceof Entity) { nodesToBeDeleted.addAll(((Entity) node).getDescendants()); } - + Set pointersToBeDeleted = nodesToPointers(nodesToBeDeleted); NodePointer nodePointer = new NodePointer(node); - + List ancestorsAndSelfPointers = getAncestorsAndSelfPointers(nodePointer); // calculated attributes and default values - + Collection dependentPointers = record.determineDependenciesThatRequireUpdates(nodesToBeDeleted); CollectionUtils.filter(dependentPointers, new Predicate() { public boolean evaluate(NodePointer nodePointer) { return !nodesToBeDeleted.contains(nodePointer.getEntity()); } }); - + Set minCountDependenciesToSelf = new HashSet(); Set maxCountDependenciesToSelf = new HashSet(); - Set> validationDependenciesToSelf = new HashSet>(); - + Set> validationDependenciesToSelf = new HashSet>(); + if (configuration.validateAfterUpdate) { // min/max - Collection preDeletionMinMaxDependenciesToCheck = new HashSet(pointersToBeDeleted); + Collection preDeletionMinMaxDependenciesToCheck = new HashSet( + pointersToBeDeleted); preDeletionMinMaxDependenciesToCheck.addAll(getAncestorsAndSelfPointers(node)); - minCountDependenciesToSelf.addAll(record.determineMinCountDependentNodes(preDeletionMinMaxDependenciesToCheck)); - maxCountDependenciesToSelf.addAll(record.determineMaxCountDependentNodes(preDeletionMinMaxDependenciesToCheck)); - + minCountDependenciesToSelf + .addAll(record.determineMinCountDependentNodes(preDeletionMinMaxDependenciesToCheck)); + maxCountDependenciesToSelf + .addAll(record.determineMaxCountDependentNodes(preDeletionMinMaxDependenciesToCheck)); + // validation validationDependenciesToSelf = record.determineValidationDependentNodes(nodesToBeDeleted); validationDependenciesToSelf.removeAll(nodesToBeDeleted); } - - //perform node deletion + + // perform node deletion List ancestorIds = node.getAncestorIds(); Entity parentEntity = node.getParent(); performNodeDeletion(node); changeMap.addNodeDeleteChange(record.getId(), record.getStep(), ancestorIds, parentEntity.getPath(), node); // update dependents - RecordDependentsUpdateResult dependentsUpdateResult = new RecordDependentsUpdater(configuration).updateDependents(record, dependentPointers); + RecordDependentsUpdateResult dependentsUpdateResult = new RecordDependentsUpdater(configuration) + .updateDependents(record, dependentPointers); List> updatedAttributes = dependentsUpdateResult.getUpdatedAttributes(); changeMap.addValueChanges(updatedAttributes); Set updatedRelevancePointers = dependentsUpdateResult.getUpdatedRelevancePointers(); changeMap.addRelevanceChanges(updatedRelevancePointers); - + if (configuration.validateAfterUpdate) { performValidationAfterUpdate(nodePointer, ancestorsAndSelfPointers, updatedAttributes, updatedRelevancePointers, minCountDependenciesToSelf, maxCountDependenciesToSelf, @@ -557,31 +572,33 @@ private void performValidationAfterUpdate(NodePointer nodePointer, List minCountDependenciesToSelf, Set maxCountDependenciesToSelf, Set> validationDependenciesToSelf, NodeChangeMap changeMap) { Record record = nodePointer.getRecord(); - + Set updatedAttributePointers = nodesToPointers(updatedAttributes); - + // min count Collection pointersToCheckMinCountFor = new HashSet(); pointersToCheckMinCountFor.addAll(ancestorsAndSelfPointers); pointersToCheckMinCountFor.addAll(updatedRelevancePointers); pointersToCheckMinCountFor.addAll(minCountDependenciesToSelf); pointersToCheckMinCountFor.addAll(updatedAttributePointers); - - Collection minCountPointersToUpdate = record.determineMinCountDependentNodes(pointersToCheckMinCountFor); + + Collection minCountPointersToUpdate = record + .determineMinCountDependentNodes(pointersToCheckMinCountFor); Collection updatedMinCountPointers = updateMinCount(minCountPointersToUpdate); changeMap.addMinCountChanges(updatedMinCountPointers); - + // max count Collection pointersToCheckMaxCountFor = new HashSet(); pointersToCheckMaxCountFor.addAll(ancestorsAndSelfPointers); pointersToCheckMaxCountFor.addAll(updatedRelevancePointers); pointersToCheckMaxCountFor.addAll(maxCountDependenciesToSelf); pointersToCheckMaxCountFor.addAll(updatedAttributePointers); - - Collection maxCountPointersToUpdate = record.determineMaxCountDependentNodes(pointersToCheckMaxCountFor); + + Collection maxCountPointersToUpdate = record + .determineMaxCountDependentNodes(pointersToCheckMaxCountFor); Collection updatedMaxCountPointers = updateMaxCount(maxCountPointersToUpdate); changeMap.addMaxCountChanges(updatedMaxCountPointers); - + Set updatedCardinalityPointers = new HashSet(updatedMinCountPointers); updatedCardinalityPointers.addAll(updatedMaxCountPointers); @@ -589,9 +606,11 @@ private void performValidationAfterUpdate(NodePointer nodePointer, List updatedAttributePointersAndSelf = new HashSet(); updatedAttributePointersAndSelf.addAll(updatedAttributePointers); updatedAttributePointersAndSelf.add(nodePointer); - //determine dependent attributes (hierarchical code attributes with parent/child relation) - Set dependentCodeAttributesPointers = determineDependentCodeAttributes(updatedAttributePointersAndSelf); - + // determine dependent attributes (hierarchical code attributes with + // parent/child relation) + Set dependentCodeAttributesPointers = determineDependentCodeAttributes( + updatedAttributePointersAndSelf); + Set updatedRelevanceDescendantPointers = pointersToDescendantPointers(updatedRelevancePointers); Set pointersToValidateCardinalityFor = new HashSet(); @@ -601,11 +620,12 @@ private void performValidationAfterUpdate(NodePointer nodePointer, List> nodesToCheckValidationFor = new HashSet>(); if (nodePointer.getChildDefinition() instanceof AttributeDefinition) { @@ -616,7 +636,7 @@ private void performValidationAfterUpdate(NodePointer nodePointer, List> attributesToRevalidate = filterAttributes(nodesToCheckValidationFor); attributesToRevalidate.addAll(record.determineValidationDependentNodes(nodesToCheckValidationFor)); @@ -630,9 +650,9 @@ public void moveNode(CollectRecord record, int nodeId, int index) { int oldIndex = siblings.indexOf(node); parent.move(node.getDefinition(), oldIndex, index); } - + private Node performNodeDeletion(Node node) { - if(node.isDetached()) { + if (node.isDetached()) { throw new IllegalArgumentException("Unable to delete a node already detached"); } Entity parentEntity = node.getParent(); @@ -641,51 +661,51 @@ private Node performNodeDeletion(Node node) { return deletedNode; } - private void setFieldSymbol(Field field, FieldSymbol symbol){ + private void setFieldSymbol(Field field, FieldSymbol symbol) { Character symbolChar = null; if (symbol != null) { symbolChar = symbol.getCode(); } field.setSymbol(symbolChar); } - + private void setSymbolOnFields(Attribute attribute, FieldSymbol symbol) { for (Field field : attribute.getFields()) { setFieldSymbol(field, symbol); } } - + private void setRemarksOnFirstField(Attribute attribute, String remarks) { Field field = attribute.getField(0); field.setRemarks(remarks); } - - private void setErrorConfirmed(Attribute attribute, boolean confirmed){ + + private void setErrorConfirmed(Attribute attribute, boolean confirmed) { for (Field field : attribute.getFields()) { field.getState().set(CONFIRMED_ERROR_POSITION, confirmed); } } - + private void setMissingValueApproved(Entity parentEntity, String childName, boolean approved) { org.openforis.idm.model.State childState = parentEntity.getChildState(childName); childState.set(APPROVED_MISSING_POSITION, approved); } - + /** * Applies the first default value (if any) that is applicable to the attribute. * The condition of the corresponding DefaultValue will be verified. - * + * * @param attribute - * @return + * @return */ private V performDefaultValueApply(Attribute attribute) { AttributeDefinition attributeDefn = (AttributeDefinition) attribute.getDefinition(); List defaults = attributeDefn.getAttributeDefaults(); for (AttributeDefault attributeDefault : defaults) { try { - if ( attributeDefault.evaluateCondition(attribute) ) { + if (attributeDefault.evaluateCondition(attribute)) { V value = attributeDefault.evaluate(attribute); - if ( value != null ) { + if (value != null) { attribute.setValue(value); attribute.setDefaultValueApplied(true); attribute.updateSummaryInfo(); @@ -698,12 +718,12 @@ private V performDefaultValueApply(Attribute attribute) } return null; } - + /** - * Validate the entire record validating the value of each attribute and - * the min/max count of each child node of each entity + * Validate the entire record validating the value of each attribute and the + * min/max count of each child node of each entity * - * @return + * @return */ public void validate(final CollectRecord record) { record.resetValidationInfo(); @@ -712,28 +732,28 @@ public void validate(final CollectRecord record) { private Attribute performAttributeAdd(Entity parentEntity, AttributeDefinition attributeDef, Value value, FieldSymbol symbol, String remarks) { - if ( value != null && symbol != null ) { + if (value != null && symbol != null) { throw new IllegalArgumentException("Cannot specify both value and symbol"); } @SuppressWarnings("unchecked") Attribute attribute = (Attribute) attributeDef.createNode(); parentEntity.add(attribute); - if ( value != null ) { + if (value != null) { attribute.setValue(value); - } else if ( symbol != null ) { + } else if (symbol != null) { setSymbolOnFields(attribute, symbol); } - if ( remarks != null ) { + if (remarks != null) { setRemarksOnFirstField(attribute, remarks); } attribute.updateSummaryInfo(); return attribute; } - + private Entity performEntityAdd(Entity parentEntity, EntityDefinition defn) { return performEntityAdd(parentEntity, defn, null); } - + private Entity performEntityAdd(Entity parentEntity, EntityDefinition defn, Integer idx) { return performEntityAdd(parentEntity, (Entity) defn.createNode(), idx); } @@ -741,9 +761,9 @@ private Entity performEntityAdd(Entity parentEntity, EntityDefinition defn, Inte private Entity performEntityAdd(Entity parentEntity, Entity entity) { return performEntityAdd(parentEntity, entity, null); } - + private Entity performEntityAdd(Entity parentEntity, Entity entity, Integer idx) { - if ( idx == null ) { + if (idx == null) { parentEntity.add(entity); } else { parentEntity.add(entity, idx); @@ -761,36 +781,37 @@ public NodeChangeSet initializeRecord(Record record) { result.addMergeChanges(new VirtualEntityPopuplator(this).populateVirtualEntitites(record)); return result; } - + public NodeChangeSet initializeNewRecord(Record record) { return initializeEntity(record.getRootEntity(), true); } - + protected NodeChangeMap initializeEntity(Entity entity) { return initializeEntity(entity, false); } - + protected NodeChangeMap initializeEntity(Entity entity, boolean newEntity) { CollectRecord record = (CollectRecord) entity.getRecord(); - + NodeChangeMap changeMap = new NodeChangeMap(); changeMap.addEntityAddChange(entity); List entityDescendantAndSelfPointers = getDescendantAndSelfNodePointers(entity); - + updateMinCount(entityDescendantAndSelfPointers); updateMaxCount(entityDescendantAndSelfPointers); addEmptyNodes(entity); - + // descendant pointers could have been changed entityDescendantAndSelfPointers = getDescendantAndSelfNodePointers(entity); - + RecordDependentsUpdater recordDependentsUpdater = new RecordDependentsUpdater(configuration); - //recalculate attributes - //TODO exclude this when exporting for backup (not for Calc) - RecordDependentsUpdateResult recordDependentsUpdateResult = recordDependentsUpdater.updateDependentsAndSelf(record, entityDescendantAndSelfPointers); - + // recalculate attributes + // TODO exclude this when exporting for backup (not for Calc) + RecordDependentsUpdateResult recordDependentsUpdateResult = recordDependentsUpdater + .updateDependentsAndSelf(record, entityDescendantAndSelfPointers); + // calculated attributes and default values List> updatedAttributes = recordDependentsUpdateResult.getUpdatedAttributes(); changeMap.addValueChanges(updatedAttributes); @@ -800,32 +821,35 @@ protected NodeChangeMap initializeEntity(Entity entity, boolean newEntity) { changeMap.addRelevanceChanges(updatedRelevancePointers); if (configuration.validateAfterUpdate) { - //recalculate descendant pointers after empty nodes have been added + // recalculate descendant pointers after empty nodes have been added entityDescendantAndSelfPointers = getDescendantAndSelfNodePointers(entity); - //min/max count - //for root entity there is no node pointer so we iterate over its descendants - Collection minCountDependentNodes = record.determineMinCountDependentNodes(entityDescendantAndSelfPointers); + // min/max count + // for root entity there is no node pointer so we iterate over its descendants + Collection minCountDependentNodes = record + .determineMinCountDependentNodes(entityDescendantAndSelfPointers); Collection updatedMinCountPointers = updateMinCount(minCountDependentNodes); changeMap.addMinCountChanges(updatedMinCountPointers); - - Collection maxCountDependentNodes = record.determineMaxCountDependentNodes(entityDescendantAndSelfPointers); + + Collection maxCountDependentNodes = record + .determineMaxCountDependentNodes(entityDescendantAndSelfPointers); Collection updatedMaxCountPointers = updateMaxCount(maxCountDependentNodes); changeMap.addMaxCountChanges(updatedMaxCountPointers); - + Set updatedCardinalityPointers = new HashSet(); updatedCardinalityPointers.addAll(updatedMinCountPointers); updatedCardinalityPointers.addAll(updatedMaxCountPointers); - - //cardinality - Collection nodePointersToCheckCardinalityFor = new HashSet(entityDescendantAndSelfPointers); + + // cardinality + Collection nodePointersToCheckCardinalityFor = new HashSet( + entityDescendantAndSelfPointers); nodePointersToCheckCardinalityFor.addAll(updatedCardinalityPointers); validateCardinality(record, nodePointersToCheckCardinalityFor, changeMap); - - //validate attributes + + // validate attributes Set> nodesToCheckValidationFor = new HashSet>(); nodesToCheckValidationFor.addAll(pointersToNodes(entityDescendantAndSelfPointers)); nodesToCheckValidationFor.addAll(pointersToNodes(updatedCardinalityPointers)); - + Set> attributesToValidate = filterAttributes(nodesToCheckValidationFor); attributesToValidate.addAll(record.determineValidationDependentNodes(nodesToCheckValidationFor)); validateAttributes(record, attributesToValidate, changeMap); @@ -840,12 +864,12 @@ private void addEmptyNodes(Entity entity) { EntityDefinition entityDefn = entity.getDefinition(); List childDefinitions = entityDefn.getChildDefinitionsInVersion(version); for (NodeDefinition childDefn : childDefinitions) { - if(entity.getCount(childDefn) == 0) { + if (entity.getCount(childDefn) == 0) { boolean isMultipleEntity = childDefn instanceof EntityDefinition && childDefn.isMultiple(); if (configuration.addEmptyMultipleEntitiesWhenAddingNewEntities || !isMultipleEntity) { int toBeInserted = entity.getMinCount(childDefn); - if ( toBeInserted <= 0 && childDefn instanceof AttributeDefinition || ! childDefn.isMultiple() ) { - //insert at least one node + if (toBeInserted <= 0 && childDefn instanceof AttributeDefinition || !childDefn.isMultiple()) { + // insert at least one node toBeInserted = 1; } addEmptyChildren(entity, childDefn, toBeInserted); @@ -853,7 +877,7 @@ private void addEmptyNodes(Entity entity) { } else { entity.visitChildren(childDefn, new NodeVisitor() { public void visit(Node child, int idx) { - if(child instanceof Entity) { + if (child instanceof Entity) { addEmptyNodes((Entity) child); } } @@ -866,16 +890,16 @@ private int addEmptyChildren(Entity entity, NodeDefinition childDefn, int toBeIn CollectSurvey survey = (CollectSurvey) entity.getSurvey(); CollectAnnotations annotations = survey.getAnnotations(); int count = 0; - if (! childDefn.isMultiple() || annotations.isAutoGenerateMinItems(childDefn) || survey.isCollectEarth()) { - while(count < toBeInserted) { - if(childDefn instanceof AttributeDefinition) { + if (!childDefn.isMultiple() || annotations.isAutoGenerateMinItems(childDefn) || survey.isCollectEarth()) { + while (count < toBeInserted) { + if (childDefn instanceof AttributeDefinition) { Node createdNode = childDefn.createNode(); entity.add(createdNode); - } else if(childDefn instanceof EntityDefinition ) { + } else if (childDefn instanceof EntityDefinition) { Entity childEntity = performEntityAdd(entity, (EntityDefinition) childDefn); addEmptyNodes(childEntity); } - count ++; + count++; } } return count; @@ -886,8 +910,8 @@ private Value applyInitialValue(Attribute attr) { } private Value applyInitialValue(Attribute attr, boolean onlyIfEmpty) { - if (!attr.getDefinition().isCalculated() && attr.isRelevant() && - (attr.isEmpty() || (!onlyIfEmpty && attr.isDefaultValueApplied()))) { + if (!attr.getDefinition().isCalculated() && attr.isRelevant() + && (attr.isEmpty() || (!onlyIfEmpty && attr.isDefaultValueApplied()))) { if (canApplyDefaultValueInCurrentPhase(attr)) { // check if default value can be applied Value value = performDefaultValueApply(attr); @@ -895,7 +919,8 @@ private Value applyInitialValue(Attribute attr, boolean onlyIfEmpty) { return value; } } - if (attr instanceof BooleanAttribute && ((BooleanAttributeDefinition) attr.getDefinition()).isAffirmativeOnly()) { + if (attr instanceof BooleanAttribute + && ((BooleanAttributeDefinition) attr.getDefinition()).isAffirmativeOnly()) { // apply value "false" to boolean attributes with "checkbox" layout Value value = new BooleanValue(false); BooleanAttribute boolAttr = (BooleanAttribute) attr; @@ -909,7 +934,7 @@ private Value applyInitialValue(Attribute attr, boolean onlyIfEmpty) { private boolean canApplyDefaultValueInCurrentPhase(Attribute attr) { Survey survey = attr.getSurvey(); - if(survey instanceof CollectSurvey) { + if (survey instanceof CollectSurvey) { CollectAnnotations annotations = ((CollectSurvey) survey).getAnnotations(); Step recordStep = ((CollectRecord) attr.getRecord()).getStep(); AttributeDefinition def = attr.getDefinition(); @@ -926,9 +951,9 @@ private void addEmptyEnumeratedEntities(Entity parentEntity) { EntityDefinition parentEntityDefn = parentEntity.getDefinition(); List childDefinitions = parentEntityDefn.getChildDefinitionsInVersion(version); for (NodeDefinition childDefn : childDefinitions) { - if ( childDefn instanceof EntityDefinition ) { + if (childDefn instanceof EntityDefinition) { EntityDefinition childEntityDefn = (EntityDefinition) childDefn; - if(childEntityDefn.isMultiple() && childEntityDefn.isEnumerable() && childEntityDefn.isEnumerate()) { + if (childEntityDefn.isMultiple() && childEntityDefn.isEnumerable() && childEntityDefn.isEnumerate()) { addEmptyEnumeratedEntities(parentEntity, childEntityDefn); } } @@ -939,20 +964,21 @@ private void addEmptyEnumeratedEntities(Entity parentEntity, EntityDefinition en Record record = parentEntity.getRecord(); ModelVersion version = record.getVersion(); CodeAttributeDefinition enumeratingCodeDefn = enumerableEntityDefn.getEnumeratingKeyCodeAttribute(version); - if(enumeratingCodeDefn != null) { + if (enumeratingCodeDefn != null) { CodeList list = enumeratingCodeDefn.getList(); Survey survey = record.getSurvey(); CodeListService codeListService = survey.getContext().getCodeListService(); List items = codeListService.loadRootItems(list); int i = 0; for (CodeListItem item : items) { - if(version == null || version.isApplicable(item)) { + if (version == null || version.isApplicable(item)) { String code = item.getCode(); - Entity enumeratedEntity = parentEntity.getEnumeratedEntity(enumerableEntityDefn, enumeratingCodeDefn, code); - if( enumeratedEntity == null ) { + Entity enumeratedEntity = parentEntity.getEnumeratedEntity(enumerableEntityDefn, + enumeratingCodeDefn, code); + if (enumeratedEntity == null) { Entity addedEntity = performEntityAdd(parentEntity, enumerableEntityDefn, i); addEmptyNodes(addedEntity); - //set the value of the key CodeAttribute + // set the value of the key CodeAttribute CodeAttribute addedCode = (CodeAttribute) addedEntity.getChild(enumeratingCodeDefn, 0); addedCode.setValue(new Code(code)); addedCode.updateSummaryInfo(); @@ -964,14 +990,14 @@ private void addEmptyEnumeratedEntities(Entity parentEntity, EntityDefinition en } } } - + private int calculateMinCount(NodePointer nodePointer) { return calculateMinCount(nodePointer.getEntity(), nodePointer.getChildDefinition()); } private int calculateMinCount(Entity entity, NodeDefinition defn) { String expression = defn.getMinCountExpression(); - if ( ! entity.isRelevant(defn) || StringUtils.isBlank(expression) ) { + if (!entity.isRelevant(defn) || StringUtils.isBlank(expression)) { return 0; } if (defn.getFixedMinCount() != null) { @@ -981,7 +1007,7 @@ private int calculateMinCount(Entity entity, NodeDefinition defn) { SurveyContext surveyContext = defn.getSurvey().getContext(); ExpressionEvaluator expressionEvaluator = surveyContext.getExpressionEvaluator(); Number value = expressionEvaluator.evaluateNumericValue(entity, null, expression); - return value == null ? 0: value.intValue(); + return value == null ? 0 : value.intValue(); } catch (InvalidExpressionException e) { throw new IdmInterpretationError("Error evaluating required expression", e); } @@ -993,8 +1019,8 @@ private int calculateMaxCount(NodePointer nodePointer) { private int calculateMaxCount(Entity entity, NodeDefinition defn) { String expression = defn.getMaxCountExpression(); - if ( ! entity.isRelevant(defn) || StringUtils.isBlank(expression) ) { - return defn.isMultiple() ? Integer.MAX_VALUE: 1; + if (!entity.isRelevant(defn) || StringUtils.isBlank(expression)) { + return defn.isMultiple() ? Integer.MAX_VALUE : 1; } if (defn.getFixedMaxCount() != null) { return defn.getFixedMaxCount(); @@ -1003,19 +1029,19 @@ private int calculateMaxCount(Entity entity, NodeDefinition defn) { SurveyContext surveyContext = defn.getSurvey().getContext(); ExpressionEvaluator expressionEvaluator = surveyContext.getExpressionEvaluator(); Number value = expressionEvaluator.evaluateNumericValue(entity, null, expression); - return value == null ? Integer.MAX_VALUE: value.intValue(); + return value == null ? Integer.MAX_VALUE : value.intValue(); } catch (InvalidExpressionException e) { throw new IdmInterpretationError("Error evaluating required expression", e); } } - + private List getDescendantNodePointers(Entity entity) { ModelVersion version = entity.getRecord().getVersion(); List pointers = new ArrayList(); EntityDefinition definition = entity.getDefinition(); for (NodeDefinition childDef : definition.getChildDefinitionsInVersion(version)) { pointers.add(new NodePointer(entity, childDef)); - if ( childDef instanceof EntityDefinition ) { + if (childDef instanceof EntityDefinition) { for (Node childEntity : entity.getChildren(childDef)) { pointers.addAll(getDescendantNodePointers((Entity) childEntity)); } @@ -1023,10 +1049,10 @@ private List getDescendantNodePointers(Entity entity) { } return pointers; } - + private List getDescendantAndSelfNodePointers(Entity entity) { List descendantNodePointers = getDescendantNodePointers(entity); - + List pointers = new ArrayList(descendantNodePointers); if (!entity.isRoot()) { @@ -1035,7 +1061,7 @@ private List getDescendantAndSelfNodePointers(Entity entity) { } return pointers; } - + private List getAncestorPointers(Node node) { if (node.getParent() == null) { return new ArrayList(); @@ -1043,7 +1069,7 @@ private List getAncestorPointers(Node node) { return getAncestorPointers(new NodePointer(node)); } } - + private List getAncestorPointers(NodePointer nodePointer) { List pointers = new ArrayList(); Entity currentEntity = nodePointer.getEntity(); @@ -1053,32 +1079,33 @@ private List getAncestorPointers(NodePointer nodePointer) { } return pointers; } - + private List getAncestorsAndSelfPointers(Node node) { List pointers = getAncestorPointers(node); pointers.add(new NodePointer(node)); return pointers; } - + private List getAncestorsAndSelfPointers(NodePointer nodePointer) { List pointers = getAncestorPointers(nodePointer); pointers.add(nodePointer); return pointers; } - + private Set determineDependentCodeAttributes(Collection attributePointers) { Set dependentAttributes = new HashSet(); for (NodePointer updatedAttributePointer : attributePointers) { if (updatedAttributePointer.getChildDefinition() instanceof CodeAttributeDefinition) { - dependentAttributes.addAll(updatedAttributePointer.getRecord().determineDependentCodeAttributes(updatedAttributePointer)); + dependentAttributes.addAll( + updatedAttributePointer.getRecord().determineDependentCodeAttributes(updatedAttributePointer)); } } Set dependentAttributesPointers = nodesToPointers(dependentAttributes); return dependentAttributesPointers; } - + private Set> filterAttributes(Collection> nodes) { - Set> attributes = new HashSet>(); + Set> attributes = new HashSet>(); for (Node node : nodes) { if (node instanceof Attribute) { attributes.add((Attribute) node); @@ -1086,36 +1113,40 @@ private Set determineDependentCodeAttributes(Collection node, int idx) { }); return result; } - + /** - * Populates a virtual entity cloning entities according to it's generatorExpression + * Populates a virtual entity cloning entities according to it's + * generatorExpression */ public NodeChangeSet populateVirtualEntity(Entity parentEntity, EntityDefinition entityDef) { NodeChangeMap result = new NodeChangeMap(); @@ -1162,26 +1194,33 @@ public NodeChangeSet populateVirtualEntity(Entity parentEntity, EntityDefinition } return result; } - - public NodeChangeSet duplicateEntity(Entity parentDestEntity, EntityDefinition destEntityDef, Entity sourceEntity) { + + public NodeChangeSet duplicateEntity(Entity parentDestEntity, EntityDefinition destEntityDef, + Entity sourceEntity) { NodeChangeMap result = new NodeChangeMap(); result.addMergeChanges(recordUpdater.addEntity(parentDestEntity, destEntityDef.getName())); Entity newEntity = parentDestEntity.getLastChild(destEntityDef.getName()); List> children = ((Entity) sourceEntity).getChildren(); for (Node child : children) { if (destEntityDef.containsChildDefinition(child.getName()) - && destEntityDef.getChildDefinition(child.getName()).getClass().isAssignableFrom(child.getDefinition().getClass())) { + && destEntityDef.getChildDefinition(child.getName()).getClass() + .isAssignableFrom(child.getDefinition().getClass())) { if (child instanceof Entity) { - result.addMergeChanges(duplicateEntity(newEntity, (EntityDefinition) destEntityDef.getChildDefinition(child.getName()), (Entity) child)); + result.addMergeChanges(duplicateEntity(newEntity, + (EntityDefinition) destEntityDef.getChildDefinition(child.getName()), (Entity) child)); } else { - //duplicate attribute + // duplicate attribute if (child.getDefinition().isMultiple()) { result.addMergeChanges(recordUpdater.addAttribute(newEntity, child.getName())); Attribute newAttr = newEntity.getLastChild(child.getName()); - result.addMergeChanges(recordUpdater.updateAttribute(newAttr, ((Attribute) child).getValue())); + result.addMergeChanges( + recordUpdater.updateAttribute(newAttr, ((Attribute) child).getValue())); } else { - Attribute newAttr = newEntity.getChild(child.getName()); //node already inserted during entity creation - result.addMergeChanges(recordUpdater.updateAttribute(newAttr, ((Attribute) child).getValue())); + Attribute newAttr = newEntity.getChild(child.getName()); // node already inserted + // during entity + // creation + result.addMergeChanges( + recordUpdater.updateAttribute(newAttr, ((Attribute) child).getValue())); } } } diff --git a/collect-core/src/main/java/org/openforis/collect/model/recordUpdater/RecordDependentsUpdater.java b/collect-core/src/main/java/org/openforis/collect/model/recordUpdater/RecordDependentsUpdater.java index 5d80171c61..d312344366 100644 --- a/collect-core/src/main/java/org/openforis/collect/model/recordUpdater/RecordDependentsUpdater.java +++ b/collect-core/src/main/java/org/openforis/collect/model/recordUpdater/RecordDependentsUpdater.java @@ -206,7 +206,7 @@ private boolean calculateRelevance(NodePointer nodePointer) { } Value previousValue = attr.getValue(); Value newValue; - if (relevant) { + if (relevant || configuration.isAlwaysEvaluateCalculatedAttributes()) { if (def.isCalculated() && !annotations.isCalculatedOnlyOneTime(def) || attr.isEmpty()) { // calculate value or default value newValue = recalculateValue(attr); diff --git a/collect-webapp/frontend/public/assets/locales/labels_en.json b/collect-webapp/frontend/public/assets/locales/labels_en.json index 0747a89253..51c02d0683 100644 --- a/collect-webapp/frontend/public/assets/locales/labels_en.json +++ b/collect-webapp/frontend/public/assets/locales/labels_en.json @@ -205,7 +205,8 @@ "includeCreatedByUserColumn": "Include 'created by user' column*", "includeEnumeratedEntities": "Include enumerated multiple entities as additional columns", "includeGroupingLabels": "Prepend grouping (single entity) name to column name", - "includeImages": "Include images" + "includeImages": "Include images", + "alwaysEvaluateCalculatedAttributes": "Always evaluate calculated attributes, even when they are not relevant (e.g. Collect Earth surveys)" }, "exportDialog": { "title": "Exporting data", diff --git a/collect-webapp/frontend/src/datamanagement/pages/CsvDataExportPage.js b/collect-webapp/frontend/src/datamanagement/pages/CsvDataExportPage.js index 312ff47de2..8fb0bebfd2 100644 --- a/collect-webapp/frontend/src/datamanagement/pages/CsvDataExportPage.js +++ b/collect-webapp/frontend/src/datamanagement/pages/CsvDataExportPage.js @@ -31,6 +31,7 @@ const additionalOptions = [ 'includeCreatedByUserColumn', 'includeGroupingLabels', 'includeImages', + 'alwaysEvaluateCalculatedAttributes', ] const onlyExcelAdditionalOptions = ['includeImages'] @@ -60,6 +61,7 @@ const defaultState = { languageCode: '', includeGroupingLabels: true, includeImages: false, + alwaysEvaluateCalculatedAttributes: false, } class CsvDataExportPage extends Component {