package com.constellio.model.entities.records; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; 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 com.constellio.data.dao.dto.records.OptimisticLockingResolution; import com.constellio.data.dao.dto.records.RecordsFlushing; import com.constellio.data.dao.services.idGenerator.UUIDV1Generator; import com.constellio.model.entities.records.TransactionRuntimeException.RecordIdCollision; import com.constellio.model.entities.records.TransactionRuntimeException.RecordsWithoutIds; import com.constellio.model.entities.records.wrappers.RecordWrapper; import com.constellio.model.entities.records.wrappers.User; import com.constellio.model.entities.schemas.MetadataSchemaTypes; import com.constellio.model.services.records.RecordUtils; public class Transaction { private String id = UUIDV1Generator.newRandomId(); Map<String, Record> updatedRecordsMap = new HashMap<>(); List<Record> records = new ArrayList<>(); RecordUpdateOptions recordUpdateOptions = new RecordUpdateOptions(); Map<String, Record> referencedRecords = new HashMap<>(); Set<String> idsToReindex = new HashSet<>(); String title; private User user; private String collection; private Map<String, ParsedContent> parsedContentCache = new HashMap<>(); public Transaction() { } public Transaction(String id) { this.id = id; } public Transaction(RecordWrapper... records) { this(); addAll(records); } public Transaction(Record... records) { this(Arrays.asList(records)); } public Transaction(List<Record> records) { this.records = records; if (!records.isEmpty()) { collection = records.get(0).getCollection(); validateCollections(); } } public Transaction(Transaction transaction) { this.records = new ArrayList<>(); for (Record record : transaction.getRecords()) { addUpdate(record); } this.user = transaction.user; this.title = transaction.title; this.recordUpdateOptions = transaction.recordUpdateOptions; this.idsToReindex.addAll(transaction.getIdsToReindex()); this.recordUpdateOptions = new RecordUpdateOptions(transaction.recordUpdateOptions); } public boolean isContainingUpdatedRecord(Record record) { return updatedRecordsMap.containsKey(record.getId()); } public Transaction addRecordToReindex(String id) { idsToReindex.add(id); return this; } public Transaction addAllRecordsToReindex(Collection<String> ids) { idsToReindex.addAll(ids); return this; } public Transaction addRecordToReindex(Record record) { idsToReindex.add(record.getId()); return this; } public Transaction addRecordToReindex(RecordWrapper record) { idsToReindex.add(record.getId()); return this; } public Transaction addUpdate(Record addUpdateRecord) { if (!addUpdateRecord.isSaved()) { add(addUpdateRecord); } else { update(addUpdateRecord); } return this; } public Transaction addUpdate(Record... addUpdateRecords) { for (Record addUpdateRecord : addUpdateRecords) { addUpdate(addUpdateRecord); } return this; } public Transaction addUpdate(List<Record> addUpdateRecords) { for (Record addUpdateRecord : addUpdateRecords) { addUpdate(addUpdateRecord); } return this; } public <T extends RecordWrapper> T add(T recordWrapper) { this.add(recordWrapper.getWrappedRecord()); return recordWrapper; } public Transaction addAll(RecordWrapper... recordWrappers) { for (RecordWrapper recordWrapper : recordWrappers) { this.add(recordWrapper.getWrappedRecord()); } return this; } public Transaction addAll(List<?> records) { for (Object record : records) { if (record != null) { if (record instanceof RecordWrapper) { this.add(((RecordWrapper) record).getWrappedRecord()); } else if (record instanceof Record) { this.add((Record) record); } else { throw new IllegalArgumentException("Unsupported object of type " + record.getClass().getName()); } } } return this; } public Transaction addAll(Record... records) { for (Record record : records) { this.add(record); } return this; } public Record add(Record addUpdateRecord) { records.add(addUpdateRecord); validateCollection(addUpdateRecord.getCollection()); collection = addUpdateRecord.getCollection(); return addUpdateRecord; } public Transaction update(Record addUpdateRecord) { if (updatedRecordsMap.containsKey(addUpdateRecord.getId())) { if (updatedRecordsMap.get(addUpdateRecord.getId()) != addUpdateRecord) { throw new RecordIdCollision(); } } else { updatedRecordsMap.put(addUpdateRecord.getId(), addUpdateRecord); records.add(addUpdateRecord); } validateCollection(addUpdateRecord.getCollection()); collection = addUpdateRecord.getCollection(); return this; } public Transaction update(Record... addUpdateRecords) { for (Record addUpdateRecord : addUpdateRecords) { update(addUpdateRecord); } return this; } public Transaction update(List<Record> addUpdateRecords) { for (Record addUpdateRecord : addUpdateRecords) { update(addUpdateRecord); } return this; } public RecordUpdateOptions onOptimisticLocking(OptimisticLockingResolution resolution) { return recordUpdateOptions.onOptimisticLocking(resolution); } public Transaction setRecordFlushing(RecordsFlushing recordsFlushing) { recordUpdateOptions.setRecordsFlushing(recordsFlushing); return this; } public void setOptions(RecordUpdateOptions recordUpdateOptions) { this.recordUpdateOptions = recordUpdateOptions; } public List<Record> getSavedRecordWithModification() { List<Record> recordsWithModification = new ArrayList<>(); for (Record record : records) { if (record.isSaved() && record.isDirty()) { recordsWithModification.add(record); } } return recordsWithModification; } public List<Record> getRecords() { return Collections.unmodifiableList(records); } public RecordUpdateOptions getRecordUpdateOptions() { return recordUpdateOptions; } public void sortRecords(MetadataSchemaTypes schemaTypes) { records = new RecordUtils().sortRecordsOnDependencies(records, schemaTypes); } public Transaction setOptimisticLockingResolution(OptimisticLockingResolution resolution) { this.recordUpdateOptions.setOptimisticLockingResolution(resolution); return this; } public List<String> getRecordIds() { List<String> recordIds = new ArrayList<>(); for (Record record : records) { if (record.getId() == null) { throw new RecordsWithoutIds(); } recordIds.add(record.getId()); } return Collections.unmodifiableList(recordIds); } public List<Record> getModifiedRecords() { if (recordUpdateOptions.isFullRewrite()) { return Collections.unmodifiableList(records); } else { List<Record> modifiedRecords = new ArrayList<>(); for (Record record : records) { if (!record.isSaved() || record.isDirty()) { modifiedRecords.add(record); } } return Collections.unmodifiableList(modifiedRecords); } } public String getCollection() { return collection; } public User getUser() { return user; } public Transaction setUser(User user) { this.user = user; return this; } public boolean isSkippingRequiredValuesValidation() { return recordUpdateOptions.isSkippingRequiredValuesValidation(); } public boolean isSkippingReferenceToLogicallyDeletedValidation() { return recordUpdateOptions.isSkippingReferenceToLogicallyDeletedValidation(); } public Transaction setSkippingRequiredValuesValidation(boolean skippingRequiredValuesValidation) { recordUpdateOptions.setSkippingRequiredValuesValidation(skippingRequiredValuesValidation); return this; } public Transaction setSkippingReferenceToLogicallyDeletedValidation(boolean skippingReferenceToLogicallyDeletedValidation) { recordUpdateOptions.setSkippingReferenceToLogicallyDeletedValidation(skippingReferenceToLogicallyDeletedValidation); return this; } public String getId() { return id; } void validateCollection(String collection) { if (this.collection != null && !this.collection.equals(collection)) { throw new TransactionRuntimeException.DifferentCollectionsInRecords(this.collection, collection); } } void validateCollections() { for (Record record : records) { validateCollection(record.getCollection()); } } public void addReferencedRecord(Record record) { referencedRecords.put(record.getId(), record); } public Map<String, Record> getReferencedRecords() { return referencedRecords; } public Record getReferencedRecord(String id) { return referencedRecords.get(id); } public static Transaction wrappers(List<? extends RecordWrapper> recordWrappers) { List<Record> records = new ArrayList<>(); for (RecordWrapper recordWrapper : recordWrappers) { records.add(recordWrapper.getWrappedRecord()); } return new Transaction(records); } public boolean isSkipReferenceValidation() { return getRecordUpdateOptions().isSkipReferenceValidation(); } public void remove(Record record) { for (Iterator<Record> iterator = records.iterator(); iterator.hasNext(); ) { Record aRecord = iterator.next(); if (aRecord.getId().equals(record.getId())) { iterator.remove(); } } } public void remove(RecordWrapper recordWrapper) { for (Iterator<Record> iterator = records.iterator(); iterator.hasNext(); ) { Record record = iterator.next(); if (record.getId().equals(recordWrapper.getId())) { iterator.remove(); } } } public Record getRecord(String id) { for (Record record : records) { if (id.equals(record.getId())) { return record; } } return null; } public String getTitle() { return title; } public Transaction setTitle(String title) { this.title = title; return this; } public Set<String> getIdsToReindex() { return Collections.unmodifiableSet(idsToReindex); } public Map<String, ParsedContent> getParsedContentCache() { return parsedContentCache; } public void removeUnchangedRecords() { Iterator<Record> recordIterator = records.iterator(); while (recordIterator.hasNext()) { Record record = recordIterator.next(); if (!record.isDirty() && record.isSaved()) { recordIterator.remove(); updatedRecordsMap.remove(record.getId()); } } } }