package com.constellio.app.services.metadata; import static com.constellio.app.services.metadata.DeletionProhibitionReason.CALCULATED_METADATA_SOURCE; import static com.constellio.app.services.metadata.DeletionProhibitionReason.COPIED_METADATA_REFERENCE; import static com.constellio.app.services.metadata.DeletionProhibitionReason.COPIED_METADATA_SOURCE; import static com.constellio.app.services.metadata.DeletionProhibitionReason.EXTRACTED_METADATA_SOURCE; import static com.constellio.app.services.metadata.DeletionProhibitionReason.FACET_METADATA; import static com.constellio.app.services.metadata.DeletionProhibitionReason.INHERITED_METADATA; import static com.constellio.app.services.metadata.DeletionProhibitionReason.POPULATED_METADATA; import static com.constellio.model.services.search.query.logical.LogicalSearchQueryOperators.*; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import org.apache.commons.lang.StringUtils; import com.constellio.app.entities.schemasDisplay.SchemaTypesDisplayConfig; import com.constellio.app.services.factories.AppLayerFactory; import com.constellio.app.services.metadata.MetadataDeletionException.MetadataDeletionException_CalculatedMetadataSource; import com.constellio.app.services.metadata.MetadataDeletionException.MetadataDeletionException_CopiedMetadataReference; import com.constellio.app.services.metadata.MetadataDeletionException.MetadataDeletionException_CopiedMetadataSource; import com.constellio.app.services.metadata.MetadataDeletionException.MetadataDeletionException_ExtractedMetadataSource; import com.constellio.app.services.metadata.MetadataDeletionException.MetadataDeletionException_FacetMetadata; import com.constellio.app.services.metadata.MetadataDeletionException.MetadataDeletionException_InheritedMetadata; import com.constellio.app.services.metadata.MetadataDeletionException.MetadataDeletionException_PopulatedMetadata; import com.constellio.app.services.metadata.MetadataDeletionException.MetadataDeletionException_SystemMetadata; import com.constellio.app.services.schemasDisplay.SchemasDisplayManager; import com.constellio.model.entities.calculators.dependencies.Dependency; import com.constellio.model.entities.calculators.dependencies.ReferenceDependency; import com.constellio.model.entities.records.wrappers.Facet; import com.constellio.model.entities.schemas.Metadata; import com.constellio.model.entities.schemas.MetadataSchemaType; 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.encrypt.EncryptionServices; import com.constellio.model.services.records.SchemasRecordsServices; import com.constellio.model.services.schemas.MetadataSchemasManager; import com.constellio.model.services.schemas.MetadataSchemasManagerException.OptimisticLocking; import com.constellio.model.services.schemas.SchemaUtils; import com.constellio.model.services.schemas.builders.MetadataBuilder; import com.constellio.model.services.schemas.builders.MetadataSchemaBuilder; import com.constellio.model.services.schemas.builders.MetadataSchemaTypesBuilder; import com.constellio.model.services.search.SearchServices; import com.constellio.model.services.search.query.logical.condition.LogicalSearchCondition; public class MetadataDeletionService { final private MetadataSchemasManager schemasManager; final private String collection; private final SchemasDisplayManager displayManager; private final SearchServices searchServices; private final EncryptionServices encryptionServices; private final SchemasRecordsServices schemas; public MetadataDeletionService(AppLayerFactory appLayerFactory, String collection) { schemasManager = appLayerFactory.getModelLayerFactory().getMetadataSchemasManager(); displayManager = appLayerFactory.getMetadataSchemasDisplayManager(); this.collection = collection; searchServices = appLayerFactory.getModelLayerFactory().newSearchServices(); encryptionServices = appLayerFactory.getModelLayerFactory().newEncryptionServices(); schemas = new SchemasRecordsServices(collection, appLayerFactory.getModelLayerFactory()); } public boolean isMetadataDeletable(String codeOrLocalCode) { if (StringUtils.isBlank(codeOrLocalCode)) { throw new RuntimeException("invalid blank code " + codeOrLocalCode); } String localCode; if (codeOrLocalCode.contains("_")) { localCode = codeOrLocalCode.split("_")[2]; } else { localCode = codeOrLocalCode; } return localCode.startsWith("USR") || localCode.startsWith("MAP"); } DeletionProhibitionReason canDeleteMetadata(String code) { Metadata metadata = getMetadata(code); return canDeleteMetadata(metadata); } private Metadata getMetadata(String code) { return schemasManager.getSchemaTypes(collection).getMetadata(code); } DeletionProhibitionReason canDeleteMetadata(Metadata metadata) { if (isInherited(metadata)) { return INHERITED_METADATA; } if (isPopulated(metadata)) { return POPULATED_METADATA; } if (isFacetMetadata(metadata)) { return FACET_METADATA; } MetadataDependency schemaTypeDependencies = computeDependencies(metadata); if (!schemaTypeDependencies.getCalculationDependencies().isEmpty()) { return CALCULATED_METADATA_SOURCE; } if (!schemaTypeDependencies.getCopyReferenceDependencies().isEmpty()) { return COPIED_METADATA_REFERENCE; } if (!schemaTypeDependencies.getCopySourceDependencies().isEmpty()) { return COPIED_METADATA_SOURCE; } if (!schemaTypeDependencies.getExtractionDependencies().isEmpty()) { return EXTRACTED_METADATA_SOURCE; } return null; } private MetadataDependency computeDependencies(Metadata dependOnMetadata) { List<Metadata> metadatalist = schemasManager.getSchemaTypes(collection) .getAllMetadataIncludingInheritedOnes(); MetadataDependency metadataDependency = new MetadataDependency(dependOnMetadata.getCode()); for (Metadata dependentMetadata : metadatalist) { computeDependencies(dependOnMetadata, dependentMetadata, metadataDependency); } return metadataDependency; } private void computeDependencies(Metadata dependOnMetadata, Metadata dependentMetadata, MetadataDependency metadataDependency) { String dependOnMetadataCode = dependOnMetadata.getCode(); String dependOnMetadataLocalCode = dependOnMetadata.getLocalCode(); if (dependentMetadata.getDataEntry().getType() == DataEntryType.COPIED) { CopiedDataEntry dataEntry = (CopiedDataEntry) dependentMetadata.getDataEntry(); String referenceMetadata = dataEntry.getReferenceMetadata(); if (referenceMetadata != null && (referenceMetadata.equals(dependOnMetadataCode) || referenceMetadata .equals(dependOnMetadataLocalCode)) ) { metadataDependency.addCopyReferenceDependency(dependentMetadata.getCode()); } String sourceMetadata = dataEntry.getCopiedMetadata(); if (sourceMetadata != null && (sourceMetadata.equals(dependOnMetadataCode) || sourceMetadata.equals(dependOnMetadataLocalCode))) { metadataDependency.addCopySourceDependency(dependentMetadata.getCode()); } } else if (dependentMetadata.getDataEntry().getType() == DataEntryType.CALCULATED) { CalculatedDataEntry dataEntry = (CalculatedDataEntry) dependentMetadata.getDataEntry(); for (Dependency dependency : dataEntry.getCalculator().getDependencies()) { String dependencyLocalCode = dependency.getLocalMetadataCode(); if (dependencyLocalCode != null && (dependencyLocalCode.equals(dependOnMetadataLocalCode) || dependencyLocalCode .equals(dependOnMetadataCode))) { metadataDependency.addCalculationDependency(dependentMetadata.getCode()); } if (dependency instanceof ReferenceDependency) { ReferenceDependency referenceDependency = (ReferenceDependency) dependency; String dependentMetadataCode = referenceDependency.getDependentMetadataCode(); if (dependentMetadataCode != null && (dependentMetadataCode.equals(dependOnMetadataLocalCode) || dependentMetadataCode .equals(dependOnMetadataCode))) { metadataDependency.addCalculationDependency(dependentMetadata.getCode()); } } } } List<String> properties = (dependentMetadata.getPopulateConfigs() == null) ? new ArrayList<String>() : dependentMetadata.getPopulateConfigs().getProperties(); if (properties.contains(dependOnMetadataCode) || properties.contains(dependOnMetadataLocalCode)) { metadataDependency.addExtractionDependency(dependentMetadata.getCode()); } } private boolean isFacetMetadata(Metadata metadata) { SchemaTypesDisplayConfig typesConfig = displayManager.getTypes(collection); List<String> facetsCodesOrLocalCodes = typesConfig.getFacetMetadataCodes(); boolean configFacet = facetsCodesOrLocalCodes.contains(metadata.getCode()) || facetsCodesOrLocalCodes.contains(metadata.getLocalCode()); if (configFacet) { return true; } else { return isFieldFacet(metadata); } } private boolean isFieldFacet(Metadata metadata) { LogicalSearchCondition query = from(schemas.facetFieldSchema()).where( schemas.facetFieldSchema().getMetadata(Facet.FIELD_DATA_STORE_CODE)).is(metadata.getCode()); return searchServices.hasResults(query); } private boolean isPopulated(Metadata metadata) { MetadataSchemaType schemaType = schemasManager.getSchemaTypes(collection).getSchemaType( new SchemaUtils().getSchemaTypeCode(metadata)); LogicalSearchCondition query; Object defaultValue = metadata.getDefaultValue(); if (defaultValue != null) { if (metadata.isEncrypted()) { defaultValue = encryptionServices.encrypt(defaultValue.toString()); } query = from(schemaType).where(metadata).isNotEqual(defaultValue).andWhere(metadata) .isNotNull(); } else { query = from(schemaType).where(metadata).isNotNull(); } return searchServices.hasResults(query); } private boolean isInherited(Metadata metadata) { return metadata.getInheritance() != null; } public void deleteMetadata(String code) throws MetadataDeletionException { if (!isMetadataDeletable(code)) { throw new MetadataDeletionException_SystemMetadata(); } Metadata metadata = getMetadata(code); DeletionProhibitionReason reason = canDeleteMetadata(metadata); if (reason != null) { switch (reason) { case POPULATED_METADATA: throw new MetadataDeletionException_PopulatedMetadata(); case INHERITED_METADATA: throw new MetadataDeletionException_InheritedMetadata(); case COPIED_METADATA_SOURCE: throw new MetadataDeletionException_CopiedMetadataSource(); case COPIED_METADATA_REFERENCE: throw new MetadataDeletionException_CopiedMetadataReference(); case CALCULATED_METADATA_SOURCE: throw new MetadataDeletionException_CalculatedMetadataSource(); case EXTRACTED_METADATA_SOURCE: throw new MetadataDeletionException_ExtractedMetadataSource(); case FACET_METADATA: throw new MetadataDeletionException_FacetMetadata(); default: throw new RuntimeException("Unsupported reason " + reason); } } MetadataSchemaTypesBuilder typesBuilder = schemasManager.modify(collection); MetadataSchemaBuilder schemaBuilder = typesBuilder .getSchema(metadata.getSchemaCode()); MetadataBuilder metadataBuilder = schemaBuilder.getMetadata(metadata.getLocalCode()); schemaBuilder.deleteMetadataWithoutValidation(metadataBuilder); //typesBuilder.deleteMetadataFromInheretedSchemas(metadata); try { schemasManager.saveUpdateSchemaTypes(typesBuilder); } catch (OptimisticLocking optimistickLocking) { throw new RuntimeException(optimistickLocking); } } public static class MetadataDependency { final String dependOnMetadataCode; Set<String> copyReferenceDependency = new HashSet<>(); Set<String> copySourceDependency = new HashSet<>(); Set<String> calculationDependencies = new HashSet<>(); Set<String> extractionDependencies = new HashSet<>(); public MetadataDependency(String dependOnMetadataCode) { this.dependOnMetadataCode = dependOnMetadataCode; } public Set<String> getCopyReferenceDependencies() { return Collections.unmodifiableSet(copyReferenceDependency); } public MetadataDependency addCopyReferenceDependency(String copyReferenceDependency) { this.copyReferenceDependency.add(copyReferenceDependency); return this; } public Set<String> getCopySourceDependencies() { return Collections.unmodifiableSet(copySourceDependency); } public MetadataDependency addCopySourceDependency(String copySourceDependency) { this.copySourceDependency.add(copySourceDependency); return this; } public Set<String> getCalculationDependencies() { return Collections.unmodifiableSet(calculationDependencies); } public MetadataDependency addCalculationDependency(String calculationDependency) { this.calculationDependencies.add(calculationDependency); return this; } public Set<String> getExtractionDependencies() { return Collections.unmodifiableSet(extractionDependencies); } public MetadataDependency addExtractionDependency(String extractionDependency) { this.extractionDependencies.add(extractionDependency); return this; } } }