package com.constellio.model.services.records;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.constellio.model.entities.records.Record;
import com.constellio.model.entities.records.Transaction;
import com.constellio.model.entities.schemas.ConfigProvider;
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.entries.DataEntryType;
import com.constellio.model.entities.schemas.validation.RecordMetadataValidator;
import com.constellio.model.entities.schemas.validation.RecordValidator;
import com.constellio.model.frameworks.validation.ValidationErrors;
import com.constellio.model.services.records.RecordServicesException.ValidationException;
import com.constellio.model.services.schemas.MetadataSchemasManager;
import com.constellio.model.services.schemas.SchemaUtils;
import com.constellio.model.services.schemas.validators.AllowedReferencesValidator;
import com.constellio.model.services.schemas.validators.CyclicHierarchyValidator;
import com.constellio.model.services.schemas.validators.MaskedMetadataValidator;
import com.constellio.model.services.schemas.validators.MetadataChildOfValidator;
import com.constellio.model.services.schemas.validators.MetadataUniqueValidator;
import com.constellio.model.services.schemas.validators.MetadataUnmodifiableValidator;
import com.constellio.model.services.schemas.validators.MetadataValueTypeValidator;
import com.constellio.model.services.schemas.validators.RecordPermissionValidator;
import com.constellio.model.services.schemas.validators.ValueRequirementValidator;
import com.constellio.model.services.search.SearchServices;
import com.constellio.model.services.security.AuthorizationsServices;
public class RecordValidationServices {
private final MetadataSchemasManager schemasManager;
private final AuthorizationsServices authorizationServices;
private final ConfigProvider configProvider;
private final RecordProvider recordProvider;
private SearchServices searchService;
public RecordValidationServices(ConfigProvider configProvider, RecordProvider recordProvider,
MetadataSchemasManager schemasManager,
SearchServices searchService) {
this(configProvider, recordProvider, schemasManager, searchService, null);
}
public RecordValidationServices(ConfigProvider configProvider, RecordProvider recordProvider,
MetadataSchemasManager schemasManager,
SearchServices searchService,
AuthorizationsServices authorizationsServices) {
this.configProvider = configProvider;
this.recordProvider = recordProvider;
this.schemasManager = schemasManager;
this.searchService = searchService;
this.authorizationServices = authorizationsServices;
}
public void validateManualMetadatas(Record record, RecordProvider recordProvider, Transaction transaction)
throws RecordServicesException.ValidationException {
ValidationErrors validationErrors = validateManualMetadatasReturningErrors(record, recordProvider, transaction);
if (!validationErrors.getValidationErrors().isEmpty()) {
throw new RecordServicesException.ValidationException(record, validationErrors);
}
}
public void validateMetadatas(Record record, RecordProvider recordProvider, Transaction transaction, List<Metadata> metadatas)
throws RecordServicesException.ValidationException {
ValidationErrors validationErrors = validateMetadatasReturningErrors(record, recordProvider, transaction, metadatas);
if (!validationErrors.getValidationErrors().isEmpty()) {
throw new RecordServicesException.ValidationException(record, validationErrors);
}
}
public void validateCyclicReferences(Record record, RecordProvider recordProvider)
throws RecordServicesException.ValidationException {
MetadataSchemaTypes schemaTypes = schemasManager.getSchemaTypes(record.getCollection());
MetadataSchema schema = schemaTypes.getSchema(record.getSchemaCode());
List<Metadata> metadatas = getManualMetadatas(schema);
validateCyclicReferences(record, recordProvider, schemaTypes, metadatas);
}
public void validateCyclicReferences(Record record, RecordProvider recordProvider, MetadataSchemaTypes schemaTypes,
List<Metadata> metadatas)
throws RecordServicesException.ValidationException {
ValidationErrors validationErrors = new ValidationErrors();
new CyclicHierarchyValidator(schemaTypes, metadatas, recordProvider).validate(record, validationErrors);
if (!validationErrors.getValidationErrors().isEmpty()) {
throw new RecordServicesException.ValidationException(record, validationErrors);
}
}
public void validateAutomaticMetadatas(Record record, RecordProvider recordProvider, Transaction transaction)
throws RecordServicesException.ValidationException {
ValidationErrors validationErrors = validateAutomaticMetadatasReturningErrors(record, recordProvider, transaction);
if (!validationErrors.getValidationErrors().isEmpty()) {
throw new RecordServicesException.ValidationException(record, validationErrors);
}
}
public void validateSchemaUsingCustomSchemaValidator(Record record, RecordProvider recordProvider, Transaction transaction)
throws RecordServicesException.ValidationException {
this.validateUsingCustomSchemaValidators(record, recordProvider);
}
public void validateUsingCustomSchemaValidators(Record record, RecordProvider recordProvider)
throws RecordServicesException.ValidationException {
ValidationErrors validationErrors = validateUsingCustomSchemaValidatorsReturningErrors(record);
if (!validationErrors.getValidationErrors().isEmpty()) {
throw new RecordServicesException.ValidationException(record, validationErrors);
}
}
boolean hasSecurityOnSchema(Record record) {
String schemaTypeCode = new SchemaUtils().getSchemaTypeCode(record.getSchemaCode());
MetadataSchemaType schemaType = schemasManager.getSchemaTypes(record.getCollection()).getSchemaType(schemaTypeCode);
return schemaType.hasSecurity();
}
List<Metadata> getManualMetadatas(MetadataSchema schema) {
List<Metadata> manualMetadatas = new ArrayList<>();
for (Metadata metadata : schema.getMetadatas()) {
if (metadata.getDataEntry().getType() == DataEntryType.MANUAL && !metadata.isSystemReserved()) {
manualMetadatas.add(metadata);
}
}
return manualMetadatas;
}
ValidationErrors validateMetadatasReturningErrors(Record record, RecordProvider recordProvider,
Transaction transaction, List<Metadata> metadatas) {
MetadataSchemaTypes schemaTypes = schemasManager.getSchemaTypes(record.getCollection());
MetadataSchema schema = schemaTypes.getSchema(record.getSchemaCode());
return validateMetadatasReturningErrors(record, recordProvider, schemaTypes, metadatas, transaction);
}
ValidationErrors validateManualMetadatasReturningErrors(Record record, RecordProvider recordProvider,
Transaction transaction) {
MetadataSchemaTypes schemaTypes = schemasManager.getSchemaTypes(record.getCollection());
MetadataSchema schema = schemaTypes.getSchema(record.getSchemaCode());
List<Metadata> metadatas = getManualMetadatas(schema);
return validateMetadatasReturningErrors(record, recordProvider, schemaTypes, metadatas, transaction);
}
ValidationErrors validateAutomaticMetadatasReturningErrors(Record record, RecordProvider recordProvider,
Transaction transaction) {
MetadataSchemaTypes schemaTypes = schemasManager.getSchemaTypes(record.getCollection());
MetadataSchema schema = schemaTypes.getSchema(record.getSchemaCode());
List<Metadata> metadatas = schema.getAutomaticMetadatas();
return validateMetadatasReturningErrors(record, recordProvider, schemaTypes, metadatas, transaction);
}
ValidationErrors validateMetadatasReturningErrors(Record record, RecordProvider recordProvider,
MetadataSchemaTypes schemaTypes, List<Metadata> metadatas, Transaction transaction) {
ValidationErrors validationErrors = new ValidationErrors();
if (!transaction.isSkipReferenceValidation()) {
new AllowedReferencesValidator(schemaTypes, metadatas, recordProvider).validate(record, validationErrors);
}
new MetadataValueTypeValidator(metadatas).validate(record, validationErrors);
if (!transaction.isSkippingRequiredValuesValidation()) {
boolean skipUSRMetadatas = transaction.getRecordUpdateOptions().isSkipUSRMetadatasRequirementValidations();
new ValueRequirementValidator(metadatas, skipUSRMetadatas).validate(record, validationErrors);
}
new MetadataUnmodifiableValidator(metadatas).validate(record, validationErrors);
if (transaction.getRecordUpdateOptions() == null || transaction.getRecordUpdateOptions().isUnicityValidationsEnabled()) {
new MetadataUniqueValidator(metadatas, schemaTypes, searchService).validate(record, validationErrors);
}
new MetadataChildOfValidator(metadatas, schemaTypes).validate(record, validationErrors);
if (transaction.getRecordUpdateOptions() == null || !transaction.getRecordUpdateOptions()
.isSkipMaskedMetadataValidations()) {
newMaskedMetadataValidator(metadatas).validate(record, validationErrors);
}
return validationErrors;
}
public MaskedMetadataValidator newMaskedMetadataValidator(List<Metadata> metadatas) {
return new MaskedMetadataValidator(metadatas);
}
@SuppressWarnings({ "unchecked", "rawtypes" })
ValidationErrors validateUsingCustomSchemaValidatorsReturningErrors(Record record) {
final ValidationErrors validationErrors = new ValidationErrors();
String schemaCode = record.getSchemaCode();
MetadataSchemaTypes types = schemasManager.getSchemaTypes(record.getCollection());
MetadataSchema schema = types.getSchema(schemaCode);
for (RecordValidator validator : schema.getValidators()) {
callSchemaValidator(record, types, schema, validator, validationErrors);
}
for (final Metadata metadata : schema.getMetadatas()) {
Set<RecordMetadataValidator<Object>> validators = (Set) metadata.getValidators();
for (RecordMetadataValidator<Object> validator : validators) {
callMetadataValidator(record, metadata, validator, validationErrors);
}
}
return validationErrors;
}
@SuppressWarnings({ "unchecked", "rawtypes" })
ValidationErrors validateUsingSecurityValidatorsReturningErrors(Record record, Transaction transaction) {
final ValidationErrors validationErrors = new ValidationErrors();
new RecordPermissionValidator(transaction, authorizationServices).validate(record, validationErrors);
return validationErrors;
}
private void callMetadataValidator(Record record, final Metadata metadata,
RecordMetadataValidator<Object> validator, final ValidationErrors validationErrors) {
final Object value = record.get(metadata);
callMetadataValidatorForValue(metadata, validator, validationErrors, value, record.getId());
}
private void callMetadataValidatorForValue(final Metadata metadata, RecordMetadataValidator<Object> validator,
final ValidationErrors validationErrors, final Object value, final String recordId) {
ValidationErrors validationErrorsWithFailedMetadataParameters = new ValidationErrors() {
@Override
public void add(Class<?> validatorClass, String code, Map<String, Object> parameters) {
parameters.put("metadataCode", metadata.getCode());
parameters.put("metadataValue", value.toString());
parameters.put("record", recordId);
validationErrors.add(validatorClass, code, parameters);
}
};
validator.validate(metadata, value, configProvider, validationErrorsWithFailedMetadataParameters);
}
private void callSchemaValidator(Record record, MetadataSchemaTypes types, final MetadataSchema schema,
RecordValidator validator, final ValidationErrors validationErrors) {
ValidationErrors validationErrorsWithExtraParams = new ValidationErrors() {
@Override
public void add(Class<?> validatorClass, String code, Map<String, Object> parameters) {
parameters.put("schemaCode", schema.getCode());
validationErrors.add(validatorClass, code, parameters);
}
};
RecordValidatorParams params = new RecordValidatorParams(record, types, schema, validator,
validationErrorsWithExtraParams,
configProvider, recordProvider);
validator.validate(params);
}
public void validateAccess(Record record, Transaction transaction)
throws ValidationException {
if (hasSecurityOnSchema(record)) {
ValidationErrors validationErrors = validateUsingSecurityValidatorsReturningErrors(record, transaction);
if (!validationErrors.getValidationErrors().isEmpty()) {
throw new RecordServicesException.ValidationException(record, validationErrors);
}
}
}
}