package com.constellio.model.services.records;
import static com.constellio.model.services.search.query.logical.LogicalSearchQueryOperators.from;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.constellio.data.utils.ImpossibleRuntimeException;
import com.constellio.model.entities.Taxonomy;
import com.constellio.model.entities.calculators.CalculatorParameters;
import com.constellio.model.entities.calculators.DynamicDependencyValues;
import com.constellio.model.entities.calculators.MetadataValueCalculator;
import com.constellio.model.entities.calculators.dependencies.ConfigDependency;
import com.constellio.model.entities.calculators.dependencies.Dependency;
import com.constellio.model.entities.calculators.dependencies.DynamicLocalDependency;
import com.constellio.model.entities.calculators.dependencies.HierarchyDependencyValue;
import com.constellio.model.entities.calculators.dependencies.LocalDependency;
import com.constellio.model.entities.calculators.dependencies.ReferenceDependency;
import com.constellio.model.entities.calculators.dependencies.SpecialDependencies;
import com.constellio.model.entities.calculators.dependencies.SpecialDependency;
import com.constellio.model.entities.records.Record;
import com.constellio.model.entities.records.RecordUpdateOptions;
import com.constellio.model.entities.records.TransactionRecordsReindexation;
import com.constellio.model.entities.schemas.Metadata;
import com.constellio.model.entities.schemas.MetadataSchema;
import com.constellio.model.entities.schemas.MetadataSchemaType;
import com.constellio.model.entities.schemas.MetadataSchemaTypes;
import com.constellio.model.entities.schemas.MetadataTransiency;
import com.constellio.model.entities.schemas.Schemas;
import com.constellio.model.entities.schemas.entries.AggregatedDataEntry;
import com.constellio.model.entities.schemas.entries.AggregationType;
import com.constellio.model.entities.schemas.entries.CalculatedDataEntry;
import com.constellio.model.entities.schemas.entries.CopiedDataEntry;
import com.constellio.model.entities.schemas.entries.DataEntryType;
import com.constellio.model.services.configs.SystemConfigurationsManager;
import com.constellio.model.services.factories.ModelLayerLogger;
import com.constellio.model.services.schemas.MetadataList;
import com.constellio.model.services.schemas.MetadataSchemasManager;
import com.constellio.model.services.schemas.SchemaUtils;
import com.constellio.model.services.search.SPEQueryResponse;
import com.constellio.model.services.search.SearchServices;
import com.constellio.model.services.search.query.logical.LogicalSearchQuery;
import com.constellio.model.services.taxonomies.TaxonomiesManager;
public class RecordAutomaticMetadataServices {
private static final Logger LOGGER = LoggerFactory.getLogger(RecordAutomaticMetadataServices.class);
private final ModelLayerLogger modelLayerLogger;
private final MetadataSchemasManager schemasManager;
private final TaxonomiesManager taxonomiesManager;
private final SystemConfigurationsManager systemConfigurationsManager;
private final SearchServices searchServices;
public RecordAutomaticMetadataServices(MetadataSchemasManager schemasManager, TaxonomiesManager taxonomiesManager,
SystemConfigurationsManager systemConfigurationsManager, ModelLayerLogger modelLayerLogger,
SearchServices searchServices) {
super();
this.modelLayerLogger = modelLayerLogger;
this.schemasManager = schemasManager;
this.taxonomiesManager = taxonomiesManager;
this.systemConfigurationsManager = systemConfigurationsManager;
this.searchServices = searchServices;
}
public void updateAutomaticMetadatas(RecordImpl record, RecordProvider recordProvider,
TransactionRecordsReindexation reindexation, RecordUpdateOptions options) {
MetadataSchemaTypes types = schemasManager.getSchemaTypes(record.getCollection());
MetadataSchema schema = types.getSchema(record.getSchemaCode());
for (Metadata automaticMetadata : schema.getAutomaticMetadatas()) {
updateAutomaticMetadata(record, recordProvider, automaticMetadata, reindexation, types, options);
}
}
public void loadTransientEagerMetadatas(RecordImpl record, RecordProvider recordProvider, RecordUpdateOptions options) {
TransactionRecordsReindexation reindexation = TransactionRecordsReindexation.ALL();
MetadataSchemaTypes types = schemasManager.getSchemaTypes(record.getCollection());
MetadataSchema schema = types.getSchema(record.getSchemaCode());
for (Metadata automaticMetadata : schema.getEagerTransientMetadatas()) {
updateAutomaticMetadata(record, recordProvider, automaticMetadata, reindexation, types, options);
}
}
public void loadTransientLazyMetadatas(RecordImpl record, RecordProvider recordProvider, RecordUpdateOptions options) {
TransactionRecordsReindexation reindexation = TransactionRecordsReindexation.ALL();
MetadataSchemaTypes types = schemasManager.getSchemaTypes(record.getCollection());
MetadataSchema schema = types.getSchema(record.getSchemaCode());
for (Metadata automaticMetadata : schema.getLazyTransientMetadatas()) {
updateAutomaticMetadata(record, recordProvider, automaticMetadata, reindexation, types, options);
}
}
void updateAutomaticMetadata(RecordImpl record, RecordProvider recordProvider, Metadata metadata,
TransactionRecordsReindexation reindexation, MetadataSchemaTypes types, RecordUpdateOptions options) {
if (metadata.isMarkedForDeletion()) {
record.updateAutomaticValue(metadata, null);
} else if (metadata.getDataEntry().getType() == DataEntryType.COPIED) {
setCopiedValuesInRecords(record, metadata, recordProvider, reindexation, options);
} else if (metadata.getDataEntry().getType() == DataEntryType.CALCULATED) {
setCalculatedValuesInRecords(record, metadata, recordProvider, reindexation, types, options);
} else if (metadata.getDataEntry().getType() == DataEntryType.AGGREGATED) {
//We don't want to calculate this metadata during record imports
if (record.get(Schemas.LEGACY_ID) == null || record.isSaved()) {
setAggregatedValuesInRecords(record, metadata, recordProvider, reindexation, types);
}
}
}
private void setAggregatedValuesInRecords(RecordImpl record, Metadata metadata, RecordProvider recordProvider,
TransactionRecordsReindexation reindexation, MetadataSchemaTypes types) {
AggregatedDataEntry aggregatedDataEntry = (AggregatedDataEntry) metadata.getDataEntry();
Metadata referenceMetadata = types.getMetadata(aggregatedDataEntry.getReferenceMetadata());
MetadataSchemaType schemaType = types.getSchemaType(new SchemaUtils().getSchemaTypeCode(referenceMetadata));
LogicalSearchQuery query = new LogicalSearchQuery();
query.setCondition(from(schemaType).where(referenceMetadata).isEqualTo(record));
if (aggregatedDataEntry.getAgregationType() == AggregationType.SUM) {
Metadata inputMetadata = types.getMetadata(aggregatedDataEntry.getInputMetadata());
query.computeStatsOnField(inputMetadata);
query.setNumberOfRows(1000);
SPEQueryResponse response = searchServices.query(query);
if (aggregatedDataEntry.getAgregationType() == AggregationType.SUM) {
Map<String, Object> statsValues = response.getStatValues(inputMetadata);
Double sum = statsValues == null ? 0.0 : (Double) response.getStatValues(inputMetadata).get("sum");
((RecordImpl) record).updateAutomaticValue(metadata, sum);
} else {
throw new ImpossibleRuntimeException("Unsupported aggregation type : " + aggregatedDataEntry.getAgregationType());
}
} else if (aggregatedDataEntry.getAgregationType() == AggregationType.REFERENCE_COUNT) {
Double childrenCount = new Double(searchServices.getResultsCount(query));
((RecordImpl) record).updateAutomaticValue(metadata, childrenCount);
}
}
void setCopiedValuesInRecords(RecordImpl record, Metadata metadataWithCopyDataEntry, RecordProvider recordProvider,
TransactionRecordsReindexation reindexation, RecordUpdateOptions options) {
CopiedDataEntry copiedDataEntry = (CopiedDataEntry) metadataWithCopyDataEntry.getDataEntry();
Metadata referenceMetadata = schemasManager.getSchemaTypes(record.getCollection())
.getMetadata(copiedDataEntry.getReferenceMetadata());
Object referenceValue = record.get(referenceMetadata);
Map<String, Object> modifiedValues = record.getModifiedValues();
boolean isReferenceModified = modifiedValues.containsKey(referenceMetadata.getDataStoreCode());
boolean forcedReindexation = reindexation.isReindexed(metadataWithCopyDataEntry);
boolean inTransaction = recordProvider.hasRecordInMemoryList(referenceValue);
if (isReferenceModified || forcedReindexation || inTransaction) {
Metadata copiedMetadata = schemasManager.getSchemaTypes(record.getCollection())
.getMetadata(copiedDataEntry.getCopiedMetadata());
copyValueInRecord(record, metadataWithCopyDataEntry, recordProvider, referenceMetadata, copiedMetadata, options);
}
}
boolean calculatorDependencyModified(RecordImpl record, MetadataValueCalculator<?> calculator, MetadataSchemaTypes types,
Metadata calculatedMetadata) {
boolean calculatorDependencyModified = !record.isSaved();
for (Dependency dependency : calculator.getDependencies()) {
if (dependency == SpecialDependencies.HIERARCHY) {
calculatorDependencyModified = true;
} else if (dependency == SpecialDependencies.IDENTIFIER) {
calculatorDependencyModified = true;
} else if (dependency == SpecialDependencies.PRINCIPAL_TAXONOMY_CODE) {
calculatorDependencyModified = true;
} else if (dependency instanceof DynamicLocalDependency) {
DynamicLocalDependency dynamicLocalDependency = (DynamicLocalDependency) dependency;
for (Metadata metadata : record.getModifiedMetadatas(types)) {
if (new SchemaUtils().isDependentMetadata(calculatedMetadata, metadata, dynamicLocalDependency)) {
calculatorDependencyModified = true;
break;
}
}
} else if (!(dependency instanceof ConfigDependency)) {
Metadata localMetadata = getMetadataFromDependency(record, dependency);
if (record.isModified(localMetadata)) {
calculatorDependencyModified = true;
}
}
}
return calculatorDependencyModified;
}
void calculateValueInRecord(RecordImpl record, Metadata metadataWithCalculatedDataEntry, RecordProvider recordProvider,
MetadataSchemaTypes types, RecordUpdateOptions options) {
MetadataValueCalculator<?> calculator = getCalculatorFrom(metadataWithCalculatedDataEntry);
Map<Dependency, Object> values = new HashMap<>();
boolean requiredValuesDefined = addValuesFromDependencies(record, metadataWithCalculatedDataEntry, recordProvider,
calculator, values, types, options);
Object calculatedValue;
if (requiredValuesDefined) {
modelLayerLogger.logCalculatedValue(record, calculator, values);
calculatedValue = calculator.calculate(
new CalculatorParameters(values, record.getId(), record.<String>get(Schemas.LEGACY_ID),
types.getSchemaType(record.getTypeCode()), record.getCollection()));
} else {
calculatedValue = calculator.getDefaultValue();
}
record.updateAutomaticValue(metadataWithCalculatedDataEntry, calculatedValue);
}
MetadataValueCalculator<?> getCalculatorFrom(Metadata metadataWithCalculatedDataEntry) {
CalculatedDataEntry calculatedDataEntry = (CalculatedDataEntry) metadataWithCalculatedDataEntry.getDataEntry();
return calculatedDataEntry.getCalculator();
}
boolean addValuesFromDependencies(RecordImpl record, Metadata metadata, RecordProvider recordProvider,
MetadataValueCalculator<?> calculator,
Map<Dependency, Object> values, MetadataSchemaTypes types, RecordUpdateOptions options) {
for (Dependency dependency : calculator.getDependencies()) {
if (dependency instanceof LocalDependency<?>) {
if (!addValueForLocalDependency(record, values, dependency)) {
return false;
}
} else if (dependency instanceof ReferenceDependency<?>) {
if (!addValueForReferenceDependency(record, recordProvider, values, dependency, options)) {
return false;
}
} else if (dependency instanceof DynamicLocalDependency) {
addValueForDynamicLocalDependency(record, metadata, values, (DynamicLocalDependency) dependency, types,
recordProvider, options);
} else if (dependency instanceof ConfigDependency<?>) {
ConfigDependency<?> configDependency = (ConfigDependency<?>) dependency;
Object configValue = systemConfigurationsManager.getValue(configDependency.getConfiguration());
values.put(dependency, configValue);
} else if (dependency instanceof SpecialDependency<?>) {
addValuesFromSpecialDependencies(record, recordProvider, values, dependency);
}
}
return true;
}
private void addValueForDynamicLocalDependency(RecordImpl record, Metadata calculatedMetadata,
Map<Dependency, Object> values, DynamicLocalDependency dependency, MetadataSchemaTypes types,
RecordProvider recordProvider, RecordUpdateOptions options) {
Map<String, Object> dynamicDependencyValues = new HashMap<>();
MetadataList availableMetadatas = new MetadataList();
MetadataList availableMetadatasWithValue = new MetadataList();
for (Metadata metadata : types.getSchema(record.getSchemaCode()).getMetadatas()) {
if (metadata.getTransiency() == MetadataTransiency.TRANSIENT_LAZY
&& record.getLazyTransientValues().isEmpty()) {
loadTransientLazyMetadatas(record, recordProvider, options);
}
if (new SchemaUtils().isDependentMetadata(calculatedMetadata, metadata, dependency)) {
availableMetadatas.add(metadata);
if (metadata.isMultivalue()) {
List<?> metadataValues = record.getList(metadata);
dynamicDependencyValues.put(metadata.getLocalCode(), metadataValues);
if (!metadataValues.isEmpty()) {
availableMetadatasWithValue.add(metadata);
}
} else {
Object metadataValue = record.get(metadata);
dynamicDependencyValues.put(metadata.getLocalCode(), metadataValue);
if (metadataValue != null) {
availableMetadatasWithValue.add(metadata);
}
}
}
}
MetadataValueCalculator<?> calculator = ((CalculatedDataEntry) calculatedMetadata.getDataEntry()).getCalculator();
values.put(dependency, new DynamicDependencyValues(calculator, dynamicDependencyValues, availableMetadatas.unModifiable(),
availableMetadatasWithValue.unModifiable()));
}
void addValuesFromSpecialDependencies(RecordImpl record, RecordProvider recordProvider,
Map<Dependency, Object> values, Dependency dependency) {
if (dependency == SpecialDependencies.HIERARCHY) {
addValueForTaxonomyDependency(record, recordProvider, values, dependency);
} else if (dependency == SpecialDependencies.IDENTIFIER) {
values.put(dependency, record.getId());
} else if (dependency == SpecialDependencies.PRINCIPAL_TAXONOMY_CODE) {
Taxonomy principalTaxonomy = taxonomiesManager.getPrincipalTaxonomy(record.getCollection());
if (principalTaxonomy != null) {
values.put(dependency, principalTaxonomy.getCode());
}
}
}
boolean addValueForReferenceDependency(RecordImpl record, RecordProvider recordProvider, Map<Dependency, Object> values,
Dependency dependency, RecordUpdateOptions options) {
ReferenceDependency<?> referenceDependency = (ReferenceDependency<?>) dependency;
Metadata referenceMetadata = getMetadataFromDependency(record, referenceDependency);
if (!referenceMetadata.isMultivalue()) {
return addSingleValueReference(record, recordProvider, values, referenceDependency, referenceMetadata, options);
} else {
return addMultivalueReference(record, recordProvider, values, referenceDependency, referenceMetadata, options);
}
}
boolean addValueForTaxonomyDependency(RecordImpl record, RecordProvider recordProvider, Map<Dependency, Object> values,
Dependency dependency) {
String schemaTypeCode = new SchemaUtils().getSchemaTypeCode(record.getSchemaCode());
Taxonomy taxonomy = taxonomiesManager.getTaxonomyFor(record.getCollection(), schemaTypeCode);
List<String> paths = new ArrayList<>();
List<String> removedAuthorizations = new ArrayList<>();
List<String> attachedAncestors = new ArrayList<>();
MetadataSchema recordSchema = schemasManager.getSchemaTypes(record.getCollection()).getSchema(record.getSchemaCode());
List<Metadata> parentReferences = recordSchema.getParentReferences();
for (Metadata metadata : parentReferences) {
String referenceValue = record.get(metadata);
if (referenceValue != null) {
Record referencedRecord = recordProvider.getRecord(referenceValue);
List<String> parentPaths = referencedRecord.getList(Schemas.PATH);
paths.addAll(parentPaths);
removedAuthorizations.addAll(referencedRecord.<String>getList(Schemas.ALL_REMOVED_AUTHS));
attachedAncestors.addAll(referencedRecord.<String>getList(Schemas.ATTACHED_ANCESTORS));
}
}
for (Taxonomy aTaxonomy : taxonomiesManager.getEnabledTaxonomies(record.getCollection())) {
for (Metadata metadata : recordSchema.getTaxonomyRelationshipReferences(aTaxonomy)) {
List<String> referencesValues = new ArrayList<>();
if (metadata.isMultivalue()) {
referencesValues.addAll(record.<String>getList(metadata));
} else {
String referenceValue = record.get(metadata);
if (referenceValue != null) {
referencesValues.add(referenceValue);
}
}
for (String referenceValue : referencesValues) {
if (referenceValue != null) {
try {
Record referencedRecord = recordProvider.getRecord(referenceValue);
List<String> parentPaths = referencedRecord.getList(Schemas.PATH);
paths.addAll(parentPaths);
removedAuthorizations.addAll(referencedRecord.<String>getList(Schemas.ALL_REMOVED_AUTHS));
if (aTaxonomy.hasSameCode(taxonomiesManager.getPrincipalTaxonomy(record.getCollection()))) {
attachedAncestors.addAll(referencedRecord.<String>getList(Schemas.ATTACHED_ANCESTORS));
}
} catch (RecordServicesRuntimeException.NoSuchRecordWithId e) {
e.printStackTrace();
}
}
}
}
}
HierarchyDependencyValue value = new HierarchyDependencyValue(taxonomy, paths, removedAuthorizations,
attachedAncestors);
values.put(dependency, value);
return true;
}
@SuppressWarnings("unchecked")
private boolean addMultivalueReference(RecordImpl record, RecordProvider recordProvider, Map<Dependency, Object> values,
ReferenceDependency<?> referenceDependency, Metadata referenceMetadata, RecordUpdateOptions options) {
List<String> referencesValues = record.<String>getList(referenceMetadata);
List<Record> referencedRecords = new ArrayList<>();
for (String referenceValue : referencesValues) {
if (referenceValue != null) {
try {
referencedRecords.add(recordProvider.getRecord(referenceValue));
} catch (RecordServicesRuntimeException.NoSuchRecordWithId e) {
RuntimeException brokenReferenceException = new RecordServicesRuntimeException.BrokenReference(
record.getId(), referenceValue, referenceMetadata, e);
if (options.isCatchBrokenReferenceErrors()) {
LOGGER.warn("Broken reference while calculating automatic metadata", brokenReferenceException);
} else {
throw brokenReferenceException;
}
}
}
}
List<Object> referencedValues = new ArrayList<>();
SortedMap<String, Object> referencedValuesMap = new TreeMap<>();
for (Record referencedRecord : referencedRecords) {
Metadata dependentMetadata = getDependentMetadataFromDependency(referenceDependency, referencedRecord);
Object dependencyValue = referencedRecord.get(dependentMetadata);
if (referenceDependency.isRequired() && dependencyValue == null) {
return false;
} else if (referenceDependency.isGroupedByReference()) {
referencedValuesMap.put(referencedRecord.getId(), dependencyValue);
} else if (dependencyValue instanceof List) {
referencedValues.addAll((List) dependencyValue);
} else {
referencedValues.add(dependencyValue);
}
}
if (referenceDependency.isGroupedByReference()) {
values.put(referenceDependency, referencedValuesMap);
} else {
values.put(referenceDependency, referencedValues);
}
return true;
}
private boolean addSingleValueReference(RecordImpl record, RecordProvider recordProvider, Map<Dependency, Object> values,
ReferenceDependency<?> dependency, Metadata referenceMetadata, RecordUpdateOptions options) {
String referenceValue = (String) record.get(referenceMetadata);
Record referencedRecord;
if (dependency.isRequired() && referenceValue == null) {
return false;
} else {
try {
referencedRecord = referenceValue == null ? null : recordProvider.getRecord(referenceValue);
} catch (RecordServicesRuntimeException.NoSuchRecordWithId e) {
RuntimeException brokenReferenceException = new RecordServicesRuntimeException.BrokenReference(
record.getId(), referenceValue, referenceMetadata, e);
if (options.isCatchBrokenReferenceErrors()) {
LOGGER.warn("Broken reference while calculating automatic metadata", brokenReferenceException);
referencedRecord = null;
} else {
throw brokenReferenceException;
}
}
}
Object dependencyValue;
if (referencedRecord != null) {
Metadata dependentMetadata = getDependentMetadataFromDependency(dependency, referencedRecord);
dependencyValue = referencedRecord.get(dependentMetadata);
} else if (dependency.isMultivalue()) {
dependencyValue = new ArrayList<>();
} else {
dependencyValue = null;
}
if (dependency.isRequired() && dependencyValue == null) {
return false;
} else {
values.put(dependency, dependencyValue);
}
return true;
}
Metadata getDependentMetadataFromDependency(ReferenceDependency<?> referenceDependency, Record referencedRecord) {
MetadataSchema schema = schemasManager.getSchemaTypes(referencedRecord.getCollection())
.getSchema(referencedRecord.getSchemaCode());
return schema.get(referenceDependency.getDependentMetadataCode());
}
boolean addValueForLocalDependency(RecordImpl record, Map<Dependency, Object> values, Dependency dependency) {
Metadata metadata = getMetadataFromDependency(record, dependency);
Object dependencyValue = record.get(metadata);
if (dependency.isRequired() && dependencyValue == null) {
return false;
} else {
values.put(dependency, dependencyValue);
}
return true;
}
Metadata getMetadataFromDependency(RecordImpl record, Dependency dependency) {
MetadataSchema schema = schemasManager.getSchemaTypes(record.getCollection()).getSchema(record.getSchemaCode());
return schema.get(dependency.getLocalMetadataCode());
}
void copyValueInRecord(RecordImpl record, Metadata metadataWithCopyDataEntry, RecordProvider recordProvider,
Metadata referenceMetadata, Metadata copiedMetadata, RecordUpdateOptions options) {
if (referenceMetadata.isMultivalue()) {
List<String> referencedRecordIds = record.getList(referenceMetadata);
if (referencedRecordIds == null || referencedRecordIds.isEmpty()) {
record.updateAutomaticValue(metadataWithCopyDataEntry, Collections.emptyList());
} else {
copyReferenceValueInRecord(record, metadataWithCopyDataEntry, recordProvider, copiedMetadata,
referencedRecordIds, referenceMetadata, options);
}
} else {
String referencedRecordId = record.get(referenceMetadata);
if (referencedRecordId == null) {
record.updateAutomaticValue(metadataWithCopyDataEntry, null);
} else {
copyReferenceValueInRecord(record, metadataWithCopyDataEntry, recordProvider, copiedMetadata, referencedRecordId,
referenceMetadata, options);
}
}
}
void copyReferenceValueInRecord(RecordImpl record, Metadata metadataWithCopyDataEntry, RecordProvider recordProvider,
Metadata copiedMetadata, String referencedRecordId, Metadata referenceMetadata, RecordUpdateOptions options) {
Object copiedValue;
try {
Record referencedRecord = recordProvider.getRecord(referencedRecordId);
copiedValue = referencedRecord.get(copiedMetadata);
} catch (RecordServicesRuntimeException.NoSuchRecordWithId e) {
RuntimeException brokenReferenceException = new RecordServicesRuntimeException.BrokenReference(
record.getId(), referencedRecordId, referenceMetadata, e);
if (options.isCatchBrokenReferenceErrors()) {
LOGGER.warn("Broken reference while calculating automatic metadata", brokenReferenceException);
copiedValue = null;
} else {
throw brokenReferenceException;
}
}
record.updateAutomaticValue(metadataWithCopyDataEntry, copiedValue);
}
void copyReferenceValueInRecord(RecordImpl record, Metadata metadataWithCopyDataEntry, RecordProvider recordProvider,
Metadata copiedMetadata, List<String> referencedRecordIds, Metadata referenceMetadata, RecordUpdateOptions options) {
List<Object> values = new ArrayList<>();
for (String referencedRecordId : referencedRecordIds) {
if (referencedRecordId != null) {
try {
RecordImpl referencedRecord = (RecordImpl) recordProvider.getRecord(referencedRecordId);
if (copiedMetadata.isMultivalue()) {
values.addAll(referencedRecord.getList(copiedMetadata));
} else {
Object value = referencedRecord.get(copiedMetadata);
if (value != null) {
values.add(value);
}
}
} catch (RecordServicesRuntimeException.NoSuchRecordWithId e) {
RuntimeException brokenReferenceException = new RecordServicesRuntimeException.BrokenReference(
record.getId(), referencedRecordId, referenceMetadata, e);
if (options.isCatchBrokenReferenceErrors()) {
LOGGER.warn("Broken reference while calculating automatic metadata", brokenReferenceException);
} else {
throw brokenReferenceException;
}
}
}
}
record.updateAutomaticValue(metadataWithCopyDataEntry, values);
}
public List<Metadata> sortMetadatasUsingLocalDependencies(Map<Metadata, Set<String>> metadatasWithLocalCodeDependencies) {
List<Metadata> sortedMetadatas = new ArrayList<>();
Map<Metadata, Set<String>> metadatas = copyInModifiableMap(metadatasWithLocalCodeDependencies);
while (!metadatas.isEmpty()) {
Metadata nextMetadata = getAMetadataWithoutDependencies(metadatas);
if (nextMetadata == null) {
throw new ImpossibleRuntimeException("Cyclic dependency");
}
metadatas.remove(nextMetadata);
for (Map.Entry<Metadata, Set<String>> otherMetadataEntry : metadatas.entrySet()) {
otherMetadataEntry.getValue().remove(nextMetadata.getLocalCode());
}
sortedMetadatas.add(nextMetadata);
}
return sortedMetadatas;
}
private Map<Metadata, Set<String>> copyInModifiableMap(Map<Metadata, Set<String>> metadatasWithLocalCodeDependencies) {
Map<Metadata, Set<String>> metadatas = new HashMap<>();
for (Map.Entry<Metadata, Set<String>> entry : metadatasWithLocalCodeDependencies.entrySet()) {
metadatas.put(entry.getKey(), new HashSet<String>(entry.getValue()));
}
return metadatas;
}
private Metadata getAMetadataWithoutDependencies(Map<Metadata, Set<String>> metadatas) {
Metadata nextMetadata = null;
for (Map.Entry<Metadata, Set<String>> entry : metadatas.entrySet()) {
if (entry.getValue().isEmpty()) {
nextMetadata = entry.getKey();
break;
}
}
return nextMetadata;
}
void setCalculatedValuesInRecords(RecordImpl record, Metadata metadataWithCalculatedDataEntry, RecordProvider recordProvider,
TransactionRecordsReindexation reindexation, MetadataSchemaTypes types, RecordUpdateOptions options) {
MetadataValueCalculator<?> calculator = getCalculatorFrom(metadataWithCalculatedDataEntry);
boolean lazyTransientMetadataToLoad = metadataWithCalculatedDataEntry.getTransiency() == MetadataTransiency.TRANSIENT_LAZY
&& !record.getLazyTransientValues().containsKey(metadataWithCalculatedDataEntry.getDataStoreCode());
if (calculatorDependencyModified(record, calculator, types, metadataWithCalculatedDataEntry)
|| reindexation.isReindexed(metadataWithCalculatedDataEntry)
|| lazyTransientMetadataToLoad) {
calculateValueInRecord(record, metadataWithCalculatedDataEntry, recordProvider, types, options);
}
}
}