package com.constellio.model.services.records; import static java.util.Arrays.asList; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.commons.lang3.StringUtils; import com.constellio.data.dao.dto.records.RecordDTO; import com.constellio.data.utils.KeyListMap; import com.constellio.model.entities.records.Record; import com.constellio.model.entities.records.wrappers.RecordWrapper; 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.MetadataValueType; import com.constellio.model.entities.schemas.Schemas; import com.constellio.model.entities.schemas.entries.DataEntryType; import com.constellio.model.services.schemas.SchemaUtils; import com.constellio.model.utils.DependencyUtils; import com.constellio.model.utils.DependencyUtilsParams; public class RecordUtils { private SchemaUtils schemaUtils; public RecordUtils() { schemaUtils = newSchemaUtils(); } public Set<String> toIdSet(List<Record> records) { Set<String> idList = new HashSet<>(); for (Record record : records) { idList.add(record.getId()); } return idList; } public List<String> toIdList(List<Record> records) { List<String> idList = new ArrayList<>(); for (Record record : records) { idList.add(record.getId()); } return idList; } public List<String> toWrappedRecordIdsList(List<? extends RecordWrapper> records) { List<String> idList = new ArrayList<>(); for (RecordWrapper record : records) { idList.add(record.getId()); } return idList; } public Map<String, List<Record>> splitRecordsBySchemaTypes(List<Record> records) { KeyListMap<String, Record> recordsSplittedByTypes = new KeyListMap<>(); SchemaUtils schemaUtils = new SchemaUtils(); for (Record record : records) { String schemaType = schemaUtils.getSchemaTypeCode(record.getSchemaCode()); recordsSplittedByTypes.add(schemaType, record); } return recordsSplittedByTypes.getNestedMap(); } public Map<String, Record> toIdRecordMap(List<Record> records) { Map<String, Record> idRecordMap = new HashMap<>(); for (Record record : records) { idRecordMap.put(record.getId(), record); } return idRecordMap; } public List<Record> sortRecordsOnDependencies(List<Record> unsortedRecords, MetadataSchemaTypes schemaTypes) { schemaUtils = new SchemaUtils(); List<Record> recordsSortedOnDependencies = new ArrayList<>(); KeyListMap<String, Record> keyListMap = new KeyListMap<>(); for (Record record : unsortedRecords) { keyListMap.add(schemaUtils.getSchemaTypeCode(record.getSchemaCode()), record); } List<String> typesSortedByDependency = schemaTypes.getSchemaTypesSortedByDependency(); for (String schemaTypeCode : typesSortedByDependency) { MetadataSchemaType type = schemaTypes.getSchemaType(schemaTypeCode); List<Record> records = keyListMap.get(schemaTypeCode); if (records != null) { recordsSortedOnDependencies.addAll(sortRecordsOfType(type, records)); } } return recordsSortedOnDependencies; } private List<Record> sortRecordsOfType(MetadataSchemaType schemaType, List<Record> unsortedRecords) { if (hasRecordDependingOnAnother(schemaType, unsortedRecords)) { List<Record> sortedRecords = new ArrayList<>(); List<Metadata> referenceMetadatas = schemaType.getAllParentReferences(); Map<String, Set<String>> dependencyMap = new HashMap<>(); for (Record record : unsortedRecords) { String parentDependencyId = record.getNonNullValueIn(referenceMetadatas); dependencyMap.put(record.getId(), Collections.singleton(parentDependencyId)); } List<String> sortedIds = new DependencyUtils<String>().sortByDependency(dependencyMap); Map<String, Record> idRecordMap = toIdRecordMap(unsortedRecords); for (String recordId : sortedIds) { sortedRecords.add(idRecordMap.get(recordId)); } return sortedRecords; } else { Set<String> ids = new HashSet<>(); List<Record> sortedRecordsById = new ArrayList<>(); for (int i = unsortedRecords.size() - 1; i >= 0; i--) { //for (int i = 0; i < unsortedRecords.size(); i++) { if (!ids.contains(unsortedRecords.get(i).getId())) { ids.add(unsortedRecords.get(i).getId()); sortedRecordsById.add(unsortedRecords.get(i)); } else { System.out.println("Same record added twice in a collection"); } } // Collections.sort(sortedRecordsById, new Comparator<Record>() { @Override public int compare(Record o1, Record o2) { return o1.getId().compareTo(o2.getId()); } }); // // Set<String> ids = new HashSet<>(); // Iterator<Record> recordIterator = sortedRecordsById.iterator(); // while(recordIterator.hasNext()) { // Record record = recordIterator.next(); // if (ids.contains(record.getId())) { // recordIterator.remove(); // } else { // // } // } // // List<Record> sortedRecords = new ArrayList<>(); // // List<Metadata> referenceMetadatas = schemaType.getAllParentReferences(); // // Map<String, Set<String>> dependencyMap = new HashMap<>(); // for (Record record : unsortedRecords) { // String parentDependencyId = record.getNonNullValueIn(referenceMetadatas); // dependencyMap.put(record.getId(), Collections.singleton(parentDependencyId)); // } // List<String> sortedIds = new DependencyUtils<String>().sortByDependency(dependencyMap); // Map<String, Record> idRecordMap = toIdRecordMap(unsortedRecords); // for (String recordId : sortedIds) { // sortedRecords.add(idRecordMap.get(recordId)); // } // // List<String> idsOfSortedByIds = toIdList(sortedRecordsById); // List<String> idsOfSorted = toIdList(sortedRecords); // // if (!idsOfSortedByIds.equals(idsOfSorted)) { // System.out.println("bobo!"); // } return sortedRecordsById; } } public boolean hasRecordDependingOnAnother(MetadataSchemaType schemaType, List<Record> unsortedRecords) { if (unsortedRecords.isEmpty()) { return false; } List<Metadata> metadatas = new ArrayList<>(); for (MetadataSchema schema : schemaType.getAllSchemas()) { for (Metadata metadata : schema.getMetadatas()) { if (metadata.getType() == MetadataValueType.REFERENCE && metadata.getAllowedReferences().isAllowed(schemaType) && metadata.getInheritance() == null) { metadatas.add(metadata); } } } if (metadatas.isEmpty()) { return false; } List<String> ids = toIdList(unsortedRecords); for (Record unsortedRecord : unsortedRecords) { for (Metadata metadata : metadatas) { if (metadata.isMultivalue()) { for (String anId : unsortedRecord.<String>getList(metadata)) { if (ids.contains(anId)) { return true; } } } else { if (ids.contains(unsortedRecord.get(metadata))) { return true; } } } } return false; } SchemaUtils newSchemaUtils() { return new SchemaUtils(); } public List<RecordDTO> toRecordDTOList(List<Record> records) { List<RecordDTO> recordDTOs = new ArrayList<>(); for (Record record : records) { recordDTOs.add(((RecordImpl) record).getRecordDTO()); } return recordDTOs; } public List<Record> newListWithoutDuplicates(List<Record> records) { List<Record> listWithoutDuplicates = new ArrayList<>(); Set<String> ids = new HashSet<>(); for (Record record : records) { if (!ids.contains(record.getId())) { ids.add(record.getId()); listWithoutDuplicates.add(record); } } return listWithoutDuplicates; } public List<RecordWrapper> newWrappersListWithoutDuplicates(List<RecordWrapper> recordWrappers) { List<RecordWrapper> listWithoutDuplicates = new ArrayList<>(); Set<String> ids = new HashSet<>(); for (RecordWrapper recordWrapper : recordWrappers) { if (!ids.contains(recordWrapper.getId())) { ids.add(recordWrapper.getId()); listWithoutDuplicates.add(recordWrapper); } } return listWithoutDuplicates; } public String getRecordsCollection(List<Record> records) { String collection = null; for (Record record : records) { if (collection == null) { collection = record.getCollection(); } else if (collection.equals(record.getCollection())) { throw new RuntimeException("Records are in different collections"); } } return collection; } public Record findRecordWithId(List<Record> records, String id) { for (Record record : records) { if (record.getId().equals(id)) { return record; } } return null; } public static List<Record> unwrap(List<? extends RecordWrapper> recordWrappers) { List<Record> records = new ArrayList<>(); for (RecordWrapper recordWrapper : recordWrappers) { records.add(recordWrapper.getWrappedRecord()); } return records; } public static void copyMetadatas(RecordWrapper source, RecordWrapper destination) { copyMetadatas(source.getWrappedRecord(), destination.getWrappedRecord(), source.getMetadataSchemaTypes()); } private static List<String> excludedMetadatas = asList(Schemas.IDENTIFIER.getLocalCode(), Schemas.LEGACY_ID.getLocalCode()); public static void copyMetadatas(Record source, Record destination, MetadataSchemaTypes types) { MetadataSchema sourceRecordSchema = types.getSchema(source.getSchemaCode()); MetadataSchema destinationRecordSchema = types.getSchema(destination.getSchemaCode()); for (Metadata sourceMetadata : sourceRecordSchema.getMetadatas()) { String sourceMetadataLocalCode = SchemaUtils.getMetadataLocalCodeWithoutPrefix(sourceMetadata); for (Metadata destinationMetadata : destinationRecordSchema.getMetadatas()) { String destMetadataLocalCode = SchemaUtils.getMetadataLocalCodeWithoutPrefix(destinationMetadata); if (sourceMetadataLocalCode.equals(destMetadataLocalCode)) { Object value = source.get(sourceMetadata); if (destinationMetadata.getDataEntry().getType() == DataEntryType.MANUAL && destinationMetadata.getType() == sourceMetadata.getType() && destinationMetadata.isMultivalue() == sourceMetadata.isMultivalue() && !destinationMetadata.isSystemReserved() && value != null && !excludedMetadatas.contains(destinationMetadata.getLocalCode())) { destination.set(destinationMetadata, value); } } } } } public static void changeSchemaTypeAccordingToTypeLinkedSchema(Record record, MetadataSchemaTypes schemaTypes, RecordProvider recordProvider) { MetadataSchema recordSchema = schemaTypes.getSchema(record.getSchemaCode()); for (Metadata metadata : recordSchema.getMetadatas()) { if (schemaTypes.isRecordTypeMetadata(metadata)) { changeSchemaTypeAccordingToTypeLinkedSchema(record, schemaTypes, recordProvider, metadata); } } } public static void changeSchemaTypeAccordingToTypeLinkedSchema(Record record, MetadataSchemaTypes schemaTypes, RecordProvider recordProvider, Metadata typeMetadata) { MetadataSchema recordSchema = schemaTypes.getSchema(record.getSchemaCode()); String newSchemaCode = getSchemaAccordingToTypeLinkedSchema(record, schemaTypes, recordProvider, typeMetadata); if (!record.getSchemaCode().equals(newSchemaCode)) { MetadataSchema newSchema = schemaTypes.getSchema(newSchemaCode); record.changeSchema(recordSchema, newSchema); } } public static String getSchemaAccordingToTypeLinkedSchema(Record record, MetadataSchemaTypes schemaTypes, RecordProvider recordProvider, Metadata typeMetadata) { MetadataSchema recordSchema = schemaTypes.getSchema(record.getSchemaCode()); MetadataSchema referencedSchema = schemaTypes.getDefaultSchema(typeMetadata.getReferencedSchemaType()); String schemaTypeCode = new SchemaUtils().getSchemaTypeCode(record.getSchemaCode()); String typeId = record.get(typeMetadata); String customSchema = null; if (typeId != null) { Record typeRecord = recordProvider.getRecord(typeId); customSchema = typeRecord.get(referencedSchema.get("linkedSchema")); } if (customSchema != null && customSchema.contains("_")) { return customSchema; } return schemaTypeCode + "_" + (customSchema == null ? "default" : customSchema); } public static String removeZerosInId(String id) { int lastZero = -1; for (int i = 0; i < id.length(); i++) { if (id.charAt(i) == '0') { lastZero = i; } else { break; } } if (lastZero == -1 || lastZero == id.length() - 1) { return id; } else { return id.substring(lastZero + 1); } } public static List<Record> sortRecordByDependency(MetadataSchemaTypes types, List<Record> records) { Set<String> ids = new HashSet<>(new RecordUtils().toIdList(records)); Map<String, Set<String>> dependencies = new HashMap<>(); Map<String, Record> recordMap = new HashMap<>(); boolean hasInterdependency = false; for (Record record : records) { MetadataSchema schema = types.getSchema(record.getSchemaCode()); Set<String> dependentIds = new HashSet<>(); recordMap.put(record.getId(), record); for (Metadata metadata : schema.getMetadatas()) { if (metadata.getType() == MetadataValueType.REFERENCE) { if (metadata.isMultivalue()) { List<String> metadataIds = record.getList(metadata); dependentIds.addAll(metadataIds); } else { String metadataId = record.get(metadata); if (metadataId != null) { dependentIds.add(metadataId); } } } } for (String dependency : dependentIds) { hasInterdependency |= ids.contains(dependency); } dependencies.put(record.getId(), dependentIds); } if (hasInterdependency) { List<Record> sorted = new ArrayList<>(); DependencyUtilsParams params = new DependencyUtilsParams().withToleratedCyclicDepencies() .sortUsingDefaultComparator(); for (String recordId : new DependencyUtils<String>().sortByDependency(dependencies, params)) { sorted.add(recordMap.get(recordId)); } return sorted; } else { return records; } } public static List<String> parentPaths(Record record) { List<String> paths = record.<String>getList(Schemas.PATH); List<String> parentPaths = new ArrayList<>(); for (String path : paths) { String parentPath = StringUtils.substringBeforeLast(path, "/"); if (!parentPath.isEmpty()) { parentPaths.add(parentPath); } } return parentPaths; } }