package com.constellio.app.ui.pages.base;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.constellio.app.services.factories.ConstellioFactories;
import com.constellio.app.ui.entities.ContentVersionVO;
import com.constellio.app.ui.entities.ContentVersionVO.InputStreamProvider;
import com.constellio.app.ui.entities.MetadataVO;
import com.constellio.app.ui.entities.MetadataValueVO;
import com.constellio.app.ui.entities.RecordVO;
import com.constellio.app.ui.entities.UserVO;
import com.constellio.data.dao.dto.records.OptimisticLockingResolution;
import com.constellio.data.dao.dto.records.RecordsFlushing;
import com.constellio.data.io.services.facades.IOServices;
import com.constellio.model.entities.batchprocess.BatchProcess;
import com.constellio.model.entities.records.Content;
import com.constellio.model.entities.records.ContentVersion;
import com.constellio.model.entities.records.Record;
import com.constellio.model.entities.records.Transaction;
import com.constellio.model.entities.records.wrappers.User;
import com.constellio.model.entities.schemas.Metadata;
import com.constellio.model.entities.schemas.MetadataSchema;
import com.constellio.model.entities.schemas.MetadataSchemasRuntimeException.NoSuchMetadata;
import com.constellio.model.entities.schemas.Schemas;
import com.constellio.model.entities.schemas.entries.DataEntryType;
import com.constellio.model.extensions.ModelLayerCollectionExtensions;
import com.constellio.model.extensions.events.schemas.PutSchemaRecordsInTrashEvent;
import com.constellio.model.frameworks.validation.ValidationException;
import com.constellio.model.services.contents.ContentManager;
import com.constellio.model.services.contents.ContentVersionDataSummary;
import com.constellio.model.services.extensions.ModelLayerExtensions;
import com.constellio.model.services.factories.ModelLayerFactory;
import com.constellio.model.services.records.RecordServicesException;
import com.constellio.model.services.records.RecordServicesRuntimeException;
import com.constellio.model.services.schemas.SchemaUtils;
import com.constellio.model.services.users.UserServices;
public class SchemaPresenterUtils extends BasePresenterUtils {
private static final String VERSION_INPUT_STREAM_NAME = "SchemaPresenterUtils-VersionInputStream";
private static Logger LOGGER = LoggerFactory.getLogger(SchemaPresenterUtils.class);
protected String schemaCode;
public SchemaPresenterUtils(String schemaCode, ConstellioFactories constellioFactories, SessionContext sessionContext) {
super(constellioFactories, sessionContext);
this.schemaCode = schemaCode;
}
@Deprecated
//Keep the schema code outside of this class
public final String getSchemaCode() {
return schemaCode;
}
@Deprecated
//Keep the schema code outside of this class
public final void setSchemaCode(String schemaCode) {
this.schemaCode = schemaCode;
}
public final Record newRecord() {
return recordServices().newRecordWithSchema(schema(schemaCode));
}
private Record newRecord(String id) {
return recordServices().newRecordWithSchema(schema(schemaCode), id);
}
public final Record getRecord(String id) {
return recordServices().getDocumentById(id);
}
public final Metadata getMetadata(String code) {
return types().getSchema(schemaCode).getMetadata(code);
}
public final List<BatchProcess> addOrUpdate(Record record) {
return addOrUpdate(record, getCurrentUser(), RecordsFlushing.NOW());
}
public final List<BatchProcess> addOrUpdateWithoutUser(Record record) {
return addOrUpdate(record, null);
}
public final List<BatchProcess> addOrUpdate(Record record, User user) {
return addOrUpdate(record, user, RecordsFlushing.NOW());
}
public final List<BatchProcess> addOrUpdate(Record record, User user, RecordsFlushing recordFlushing) {
Transaction createTransaction = new Transaction();
createTransaction.setUser(user);
createTransaction.setOptimisticLockingResolution(OptimisticLockingResolution.EXCEPTION);
createTransaction.addUpdate(record);
if (!modelLayerFactory().getRecordsCaches().isCached(record.getId())
|| !modelLayerFactory().getRecordsCaches().getCache(getCollection()).isCached(record.getId())) {
createTransaction.setRecordFlushing(recordFlushing);
}
try {
return recordServices().executeHandlingImpactsAsync(createTransaction);
} catch (RecordServicesException e) {
Exception nestedException;
if (e instanceof RecordServicesException.ValidationException) {
LOGGER.error(e.getMessage(), e);
nestedException = new ValidationException(((RecordServicesException.ValidationException) e).getErrors());
} else {
nestedException = e;
}
throw new RuntimeException(nestedException);
}
}
public final void delete(Record record, String reason) {
delete(record, reason, true);
}
public final void delete(Record record, String reason, User user) {
delete(record, reason, true, user);
}
public final void delete(Record record, String reason, boolean physically) {
delete(record, reason, physically, getCurrentUser());
}
public final void delete(Record record, String reason, boolean physically, User user) {
boolean putFirstInTrash = putFirstInTrash(record);
if (recordServices().isLogicallyThenPhysicallyDeletable(record, user) || putFirstInTrash) {
recordServices().logicallyDelete(record, user);
modelLayerFactory().newLoggingServices().logDeleteRecordWithJustification(record, user, reason);
if (physically && !putFirstInTrash) {
recordServices().physicallyDelete(record, user);
}
}
}
private boolean putFirstInTrash(Record record) {
ModelLayerExtensions ext = modelLayerFactory().getExtensions();
if (ext == null) {
return false;
}
ModelLayerCollectionExtensions extensions = ext.forCollection(record.getCollection());
PutSchemaRecordsInTrashEvent event = new PutSchemaRecordsInTrashEvent(record.getSchemaCode());
return extensions.isPutInTrashBeforePhysicalDelete(event);
}
@SuppressWarnings("unchecked")
public final Record toRecord(RecordVO recordVO) {
return toRecord(recordVO, false);
}
@SuppressWarnings("unchecked")
public final Record toRecord(RecordVO recordVO, boolean newMinorEmpty) {
Record record;
try {
record = recordServices().getDocumentById(recordVO.getId());
} catch (RecordServicesRuntimeException.NoSuchRecordWithId e) {
record = newRecord(recordVO.getId());
}
String recordSchemaCode = record.getSchemaCode();
MetadataSchema currentSchema = schema(recordSchemaCode);
MetadataSchema targetSchema = schema(schemaCode);
if (!recordSchemaCode.equals(schemaCode)) {
record.changeSchema(currentSchema, targetSchema);
}
for (MetadataValueVO metadataValueVO : recordVO.getMetadataValues()) {
MetadataVO metadataVO = metadataValueVO.getMetadata();
String metadataCode = metadataVO.getCode();
String localMetadataCode = new SchemaUtils().getLocalCodeFromMetadataCode(metadataCode);
Metadata metadata;
try {
metadata = targetSchema.getMetadata(localMetadataCode);
} catch (NoSuchMetadata e) {
continue;
}
boolean systemReserved = metadata.isSystemReserved() && !metadata.hasSameCode(Schemas.LEGACY_ID);
if (!systemReserved && metadata.isEnabled() && metadata.getDataEntry().getType() == DataEntryType.MANUAL) {
Object metadataValue = record.get(metadata);
Object metadataVOValue = metadataValueVO.getValue();
if (metadataVOValue instanceof RecordVO) {
metadataVOValue = ((RecordVO) metadataVOValue).getId();
} else if (metadataVOValue instanceof ContentVersionVO) {
ContentVersionVO contentVersionVO = (ContentVersionVO) metadataVOValue;
Content content = toContent(contentVersionVO, newMinorEmpty);
if (content != null) {
metadataVOValue = content;
} else {
// Same value
metadataVOValue = metadataValue;
}
} else if (metadataVOValue instanceof Collection) {
List<Object> replacementValue = new ArrayList<Object>();
Collection<Object> collectionMetadataValue = (Collection<Object>) metadataVOValue;
boolean contentMetadata = false;
for (Iterator<Object> it = collectionMetadataValue.iterator(); it.hasNext(); ) {
Object element = it.next();
if (element instanceof RecordVO) {
replacementValue.add(((RecordVO) metadataVOValue).getId());
} else if (element instanceof ContentVersionVO) {
contentMetadata = true;
ContentVersionVO contentVersionVO = (ContentVersionVO) element;
Content content = toContent(contentVersionVO, newMinorEmpty);
if (content == null) {
content = getContent(contentVersionVO.getHash(), (List<Content>) metadataValue);
}
replacementValue.add(content);
}
}
if (!replacementValue.isEmpty()) {
metadataVOValue = replacementValue;
} else if (contentMetadata) {
// No changes were made
if (collectionMetadataValue.isEmpty()) {
// Set an empty list, as a content may have been removed from the collection
metadataVOValue = new ArrayList<Content>();
} else {
metadataVOValue = metadataValue;
}
}
}
boolean valueDifferent;
if ((metadataValue != null && metadataVOValue == null) || (metadataValue == null && metadataVOValue != null)) {
valueDifferent = true;
} else if (metadataVOValue == null && metadataValue == null) {
valueDifferent = false;
} else {
valueDifferent = !metadataValueVO.equals(metadataValue);
}
if (valueDifferent) {
record.set(metadata, metadataVOValue);
}
}
}
return record;
}
private Content getContent(String id, List<Content> contentList) {
Content match = null;
if (contentList != null) {
for (Content content : contentList) {
ContentVersion currentVersion = content.getCurrentVersion();
String hash = currentVersion.getHash();
if (id.equals(hash)) {
match = content;
break;
}
}
}
return match;
}
public Content toContent(ContentVersionVO contentVersionVO) {
return toContent(contentVersionVO, false);
}
public Content toContent(ContentVersionVO contentVersionVO, boolean newMinorEmpty) {
Content content;
ConstellioFactories constellioFactories = ConstellioFactories.getInstance();
ModelLayerFactory modelLayerFactory = constellioFactories.getModelLayerFactory();
ContentManager contentManager = modelLayerFactory.getContentManager();
UserServices userServices = modelLayerFactory.newUserServices();
String collection = sessionContext.getCurrentCollection();
UserVO currentUserVO = sessionContext.getCurrentUser();
String username = currentUserVO.getUsername();
User currentUser = userServices.getUserInCollection(username, collection);
String hash = contentVersionVO.getHash();
String fileName = contentVersionVO.getFileName();
Boolean majorVersion = contentVersionVO.isMajorVersion();
InputStreamProvider inputStreamProvider = contentVersionVO.getInputStreamProvider();
boolean newContent = hash == null;
if (newContent) {
InputStream inputStream = null;
ContentVersionDataSummary contentVersionDataSummary;
try {
inputStream = inputStreamProvider.getInputStream(VERSION_INPUT_STREAM_NAME);
contentVersionDataSummary = uploadContent(inputStream, true, true, fileName);
} finally {
IOServices ioServices = modelLayerFactory.getIOServicesFactory().newIOServices();
ioServices.closeQuietly(inputStream);
}
if (majorVersion == null) {
// TODO Use the right kind of exception
throw new RuntimeException("Must specify if the version is minor or major");
} else if (majorVersion) {
content = contentManager.createMajor(currentUser, fileName, contentVersionDataSummary);
} else if (newMinorEmpty) {
content = contentManager.createEmptyMinor(currentUser, fileName, contentVersionDataSummary);
} else {
content = contentManager.createMinor(currentUser, fileName, contentVersionDataSummary);
}
} else {
content = null;
}
if (content != null) {
contentVersionVO.setContentId(content.getId());
}
return content;
}
@Deprecated
//Use schema(code) instead
public final MetadataSchema schema() {
return schema(schemaCode);
}
public final MetadataSchema defaultSchema() {
String schemaTypeCode = new SchemaUtils().getSchemaTypeCode(schemaCode);
return schemaType(schemaTypeCode).getDefaultSchema();
}
}