package com.constellio.model.services.records.extractions; import com.constellio.model.entities.enums.MetadataPopulatePriority; import com.constellio.model.entities.enums.TitleMetadataPopulatePriority; import com.constellio.model.entities.records.Content; import com.constellio.model.entities.records.ParsedContent; import com.constellio.model.entities.records.Record; import com.constellio.model.entities.schemas.*; import com.constellio.model.entities.schemas.RegexConfig.RegexConfigType; import com.constellio.model.extensions.events.records.RecordSetCategoryEvent; import com.constellio.model.services.configs.SystemConfigurationsManager; import com.constellio.model.services.contents.ContentManager; import com.constellio.model.services.contents.ContentManagerRuntimeException.ContentManagerRuntimeException_NoSuchContent; import com.constellio.model.services.contents.ParsedContentProvider; import com.constellio.model.services.extensions.ModelLayerExtensions; import com.constellio.model.services.migrations.ConstellioEIMConfigs; import com.constellio.model.services.schemas.MetadataSchemasManager; import org.apache.commons.io.FilenameUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.regex.Matcher; public class RecordPopulateServices { public static boolean LOG_CONTENT_MISSING = true; private static final Logger LOGGER = LoggerFactory.getLogger(RecordPopulateServices.class); ContentManager contentManager; MetadataSchemasManager schemasManager; SystemConfigurationsManager systemConfigurationsManager; ModelLayerExtensions modelLayerExtensions; ConstellioEIMConfigs eimConfigs; public RecordPopulateServices(MetadataSchemasManager schemasManager, ContentManager contentManager, SystemConfigurationsManager systemConfigurationsManager, ModelLayerExtensions modelLayerExtensions) { this.schemasManager = schemasManager; this.contentManager = contentManager; this.systemConfigurationsManager = systemConfigurationsManager; this.modelLayerExtensions = modelLayerExtensions; this.eimConfigs = new ConstellioEIMConfigs(systemConfigurationsManager); } public void populate(Record record) { ParsedContentProvider parsedContentProvider = new ParsedContentProvider(contentManager); populate(record, parsedContentProvider); } public void populate(Record record, ParsedContentProvider parsedContentProvider) { MetadataSchema schema = schemasManager.getSchemaTypes(record.getCollection()).getSchema(record.getSchemaCode()); List<Metadata> contentMetadatas = schema.getContentMetadatasForPopulate(); if (!contentMetadatas.isEmpty()) { if (!record.isSaved()) { String category = getCategory(parsedContentProvider, contentMetadatas, record); setCategoryToRecord(record, category); } schema = schemasManager.getSchemaTypes(record.getCollection()).getSchema(record.getSchemaCode()); Record originalRecord = record.isSaved() ? record.getCopyOfOriginalRecord() : null; MetadataPopulatePriority priority = eimConfigs.getMetadataPopulatePriority(); TitleMetadataPopulatePriority titlePriority = eimConfigs.getTitleMetadataPopulatePriority(); for (Metadata metadata : schema.getMetadatas()) { if (!metadata.getPopulateConfigs().isEmpty() || Schemas.TITLE_CODE.equals(metadata.getLocalCode())) { RecordMetadataPopulator populator = new RecordMetadataPopulator(parsedContentProvider, metadata, priority, titlePriority, schema); if (isRepopulatable(record, originalRecord, metadata, contentMetadatas, populator)) { Object currentPopulatedValue = populator.populate(record, contentMetadatas); if (currentPopulatedValue != null || !Schemas.TITLE_CODE.equals(metadata.getLocalCode())) { record.set(metadata, currentPopulatedValue); } } } } } } private String getCategory(ParsedContentProvider parsedContentProvider, List<Metadata> contentMetadatas, Record record) { for (Content content : getContents(record, contentMetadatas)) { ParsedContent parsedContent = parsedContentProvider.getParsedContentParsingIfNotYetDone( content.getCurrentVersion().getHash()); String category = (String) parsedContent.getNormalizedProperty("category"); if (category != null) { return category; } } return null; } private void setCategoryToRecord(Record record, String category) { RecordSetCategoryEvent event = new RecordSetCategoryEvent(record, category); modelLayerExtensions.forCollectionOf(record).callSetRecordCategory(event); } private boolean isRepopulatable(Record record, Record originalRecord, Metadata metadata, List<Metadata> contentMetadatas, RecordMetadataPopulator recordMetadataPopulator) { if (originalRecord == null) { if (metadata.isMultivalue()) { List<String> values = record.getList(metadata); return values.isEmpty() || isValueWritenBySystem(record, metadata, values, contentMetadatas); } else { if (Schemas.TITLE.equals(metadata.getLocalCode()) && metadata.isSchemaAutocomplete()) { return false; } String value = record.get(metadata); return record.get(metadata) == null || isValueWritenBySystem(record, metadata, value, contentMetadatas); } } Object previousValue = originalRecord.get(metadata); Object previousPopulatedValue = recordMetadataPopulator.populate(originalRecord, contentMetadatas); Object currentValue = record.get(metadata); previousValue = emptyListToNull(previousValue); previousPopulatedValue = emptyListToNull(previousPopulatedValue); currentValue = emptyListToNull(currentValue); if (previousValue == null) { //If there was no value, it's only populated if a value has not been set return currentValue == null; } if (previousValue.equals(previousPopulatedValue)) { return previousValue.equals(currentValue); } return false; } private boolean isValueWritenBySystem(Record record, Metadata metadata, String value, List<Metadata> contentMetadatas) { if (metadata.isSameLocalCode(Schemas.TITLE)) { for (Content content : getContents(record, contentMetadatas)) { if (content.getCurrentVersion().getFilename().equals(value)) { return true; } } } return value.equals(metadata.getDefaultValue()); } private boolean isValueWritenBySystem(Record record, Metadata metadata, List<String> value, List<Metadata> contentMetadatas) { return value.equals(metadata.getDefaultValue()); } private Object emptyListToNull(Object value) { if (value instanceof List) { List<?> list = (List<?>) value; return list.isEmpty() ? null : list; } return value; } private class RecordMetadataPopulator { MetadataSchema schema; TitleMetadataPopulatePriority titlePriority; MetadataPopulatePriority priority; ParsedContentProvider parsedContentProvider; Metadata metadata; private RecordMetadataPopulator(ParsedContentProvider parsedContentProvider, Metadata metadata, MetadataPopulatePriority priority, TitleMetadataPopulatePriority titlePriority, MetadataSchema schema) { this.parsedContentProvider = parsedContentProvider; this.metadata = metadata; this.priority = priority; this.titlePriority = titlePriority; this.schema = schema; } private Object populate(Record record, List<Metadata> contentMetadatas) { if (metadata.isSameLocalCode(Schemas.TITLE)) { return populateTitleUsingPropertiesAndStyles(record, contentMetadatas); } else { Object value = null; for (String source : priority.getPrioritizedSources()) { if (value == null || value.equals(new ArrayList<>())) { switch (source) { case "styles": value = populateUsingStyles(record, contentMetadatas); break; case "properties": value = populateUsingProperties(record, contentMetadatas); break; case "regex": value = populateUsingRegex(record); break; case "plugin": value = populateUsingMetatdataPopulator(record); break; } } } return value; } } private Object populateUsingMetatdataPopulator(Record record) { for (MetadataPopulator metadataPopulator : metadata.getPopulateConfigs().getMetadataPopulators()) { metadataPopulator.init(contentManager, schema, metadata.isMultivalue()); Object value = metadataPopulator.getPopulationValue(record); if (value != null) return value; } return null; } private Object populateUsingRegex(Record record) { Object value = null; for (RegexConfig regexConfig : metadata.getPopulateConfigs().getRegexes()) { if (value == null) { value = populateUsingRegex(regexConfig, record); } } return value; } private Object populateUsingRegex(RegexConfig regexConfig, Record record) { Metadata inputMetadata = schema.getMetadata(regexConfig.getInputMetadata()); List<String> populatedValues = new ArrayList<>(); if (inputMetadata.isMultivalue()) { List<String> values = record.getList(inputMetadata); for (Object value : values) { String populatedValue = populateUsingRegex(regexConfig, value, inputMetadata); if (populatedValue != null) { populatedValues.add(populatedValue); } } } else { Object value = record.get(inputMetadata); String populatedValue = populateUsingRegex(regexConfig, value, inputMetadata); if (populatedValue != null) { populatedValues.add(populatedValue); } } return convert(populatedValues); } private String populateUsingRegex(RegexConfig regexConfig, String value) { Matcher matcher = regexConfig.getRegex().matcher(value); if (matcher.find()) { if (regexConfig.getRegexConfigType() == RegexConfigType.TRANSFORMATION) { String match = matcher.group(); return regexConfig.getRegex().matcher(match).replaceAll(regexConfig.getValue()); } else { return regexConfig.getValue(); } } return null; } private String populateUsingRegex(RegexConfig regexConfig, Object value, Metadata inputMetadata) { if (value == null) { return null; } if (inputMetadata.getType().equals(MetadataValueType.CONTENT)) { Content content = (Content) value; try { ParsedContent parsedContent = parsedContentProvider.getParsedContentParsingIfNotYetDone( content.getCurrentVersion().getHash()); return populateUsingRegex(regexConfig, parsedContent.getParsedContent()); } catch (ContentManagerRuntimeException_NoSuchContent e) { if (LOG_CONTENT_MISSING) { LOGGER.error("No content " + content.getCurrentVersion().getHash()); } } } else if (inputMetadata.getType().isStringOrText()) { return populateUsingRegex(regexConfig, (String) value); } return null; } private Object populateUsingStyles(Record record, List<Metadata> contentMetadatas) { for (Content content : getContents(record, contentMetadatas)) { List<String> contentPopulatedValue; try { ParsedContent parsedContent = parsedContentProvider.getParsedContentParsingIfNotYetDone( content.getCurrentVersion().getHash()); contentPopulatedValue = populateUsingStyles(parsedContent); if (contentPopulatedValue != null) { return convert(contentPopulatedValue); } } catch (ContentManagerRuntimeException_NoSuchContent e) { if (LOG_CONTENT_MISSING) { LOGGER.error("No content " + content.getCurrentVersion().getHash()); } } } return null; } private Object populateUsingProperties(Record record, List<Metadata> contentMetadatas) { for (Content content : getContents(record, contentMetadatas)) { List<String> contentPopulatedValue; try { ParsedContent parsedContent = parsedContentProvider.getParsedContentParsingIfNotYetDone( content.getCurrentVersion().getHash()); contentPopulatedValue = populateUsingProperties(parsedContent); if (contentPopulatedValue != null) { return convert(contentPopulatedValue); } } catch (ContentManagerRuntimeException_NoSuchContent e) { if (LOG_CONTENT_MISSING) { LOGGER.error("No content " + content.getCurrentVersion().getHash()); } } } return null; } private Object populateTitleUsingPropertiesAndStyles(Record record, List<Metadata> contentMetadatas) { for (Content content : getContents(record, contentMetadatas)) { List<String> contentPopulatedValue; try { contentPopulatedValue = populateTitleUsingPropertiesAndStyles(content); if (contentPopulatedValue != null) { return convert(contentPopulatedValue); } } catch (ContentManagerRuntimeException_NoSuchContent e) { if (LOG_CONTENT_MISSING) { LOGGER.error("No content " + content.getCurrentVersion().getHash()); } } } return null; } private Object convert(List<String> contentPopulatedValues) { if (contentPopulatedValues == null || contentPopulatedValues.equals(new ArrayList<String>())) { return null; } else if (metadata.isMultivalue()) { List<String> convertedValues = new ArrayList<>(); for (String contentPopulatedValue : contentPopulatedValues) { convertedValues.addAll(asStringList(contentPopulatedValue)); } return convertedValues; } else if (contentPopulatedValues.isEmpty()) { return null; } else { return contentPopulatedValues.get(0).trim(); } } private List<String> populateTitleUsingPropertiesAndStyles(Content content) { ParsedContent parsedContent = parsedContentProvider.getParsedContentParsingIfNotYetDone( content.getCurrentVersion().getHash()); List<String> propertiesValues = populateUsingProperties(parsedContent); List<String> stylesValues = populateUsingStyles(parsedContent); for (String source : titlePriority.getPrioritizedSouces()) { if (source.equals("styles")) { if (!stylesValues.isEmpty()) { return stylesValues; } } else if (source.equals("properties")) { if (!propertiesValues.isEmpty()) { return propertiesValues; } } else { break; } } String fileName = content.getCurrentVersion().getFilename(); if (eimConfigs.isRemoveExtensionFromRecordTitle()) { fileName = FilenameUtils.removeExtension(fileName); } return fileName == null ? new ArrayList<String>() : Collections.singletonList(fileName); // Collections.singletonList(content.getCurrentVersion().getFilename()); } private List<String> populateUsingStyles(ParsedContent parsedContent) { List<String> values = new ArrayList<>(); for (String style : metadata.getPopulateConfigs().getStyles()) { List<String> styleValues = parsedContent.getStyles().get(style); if (styleValues != null) { values.addAll(styleValues); } } return values; } @SuppressWarnings("unchecked") private List<String> populateUsingProperties(ParsedContent parsedContent) { List<String> values = new ArrayList<>(); for (String property : metadata.getPopulateConfigs().getProperties()) { Object value = parsedContent.getNormalizedProperty(property); if (value instanceof String) { values.add((String) value); } else if (value instanceof List) { values.addAll((List<String>) value); } } return values; } } private static List<String> asStringList(Object value) { if (value == null) { return new ArrayList<>(); } else { List<String> values = new ArrayList<>(); for (String aValue : value.toString().split(";")) { values.add(aValue.replace("'", "").trim()); } return values; } } private static List<Content> getContents(Record record, List<Metadata> contentMetadatas) { List<Content> contents = new ArrayList<>(); for (Metadata contentMetadata : contentMetadatas) { if (contentMetadata.isMultivalue()) { List<Content> metadataContents = record.getList(contentMetadata); contents.addAll(metadataContents); } else { Content content = record.get(contentMetadata); if (content != null) { contents.add(content); } } } return contents; } }