package com.constellio.model.services.contents; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.StringTokenizer; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.joda.time.LocalDateTime; import com.constellio.data.utils.LazyIterator; import com.constellio.model.entities.records.ContentVersion; import com.constellio.model.entities.records.wrappers.User; import com.constellio.model.entities.schemas.ModifiableStructure; import com.constellio.model.entities.schemas.StructureFactory; import com.constellio.model.services.search.query.logical.criteria.IsContainingTextCriterion; import com.constellio.model.utils.Lazy; public class ContentFactory implements StructureFactory { public static final String COLON_REPLACER = "$#$"; public static final String INFO_SEPARATOR = ":"; private static final String NULL_STRING = "null"; public static IsContainingTextCriterion isCheckedOutBy(User user) { return isCheckedOutBy(user.getId()); } public static IsContainingTextCriterion isCheckedOutBy(String userId) { return new IsContainingTextCriterion(INFO_SEPARATOR + userId + INFO_SEPARATOR); } public static IsContainingTextCriterion isFilename(String filename) { return new IsContainingTextCriterion(INFO_SEPARATOR + "f=" + filename + INFO_SEPARATOR); } public static IsContainingTextCriterion isCurrentFilename(String filename) { return new IsContainingTextCriterion(INFO_SEPARATOR + "cf=" + filename + INFO_SEPARATOR); } public static IsContainingTextCriterion isHash(String hash) { return new IsContainingTextCriterion(INFO_SEPARATOR + "h=" + hash + INFO_SEPARATOR); } public static IsContainingTextCriterion isMimetype(String mimetype) { return new IsContainingTextCriterion(INFO_SEPARATOR + "m=" + mimetype + INFO_SEPARATOR); } public static IsContainingTextCriterion checkedOut() { return new IsContainingTextCriterion(INFO_SEPARATOR + "co=true" + INFO_SEPARATOR); } private static Iterator<String> newPartsIterator(final String string) { return new LazyIterator<String>() { int currentIndex = -2; @Override protected String getNextOrNull() { int nextSeparator = string.indexOf("::", currentIndex + 1); if (nextSeparator == -1) { return null; } String content = string.substring(currentIndex + 2, nextSeparator); currentIndex = nextSeparator; return content; } }; } @Override public ModifiableStructure build(String string) { int version = getFactoryVersion(string); Iterator<String> iterator; if (version == 1) { iterator = newPartsIterator(string); } else { iterator = newPartsIterator(string.substring(3)); } return build(iterator, version); } private ModifiableStructure build(Iterator<String> iterator, int version) { String id = iterator.next(); skipCurrentFileName(iterator); skipIsCheckedOut(iterator); ContentVersion current = toContentVersion(iterator.next(), version); ContentVersion currentCheckedOut = toContentVersion(iterator.next(), version); String nextLine = iterator.next(); boolean emptyVersion = false; if ("_EMPTY_VERSION_".equals(nextLine)) { emptyVersion = true; nextLine = iterator.next(); } String checkedOutBy = toNullableString(nextLine); LocalDateTime checkedOutDateTime = toDateTime(iterator.next()); String lastKnownFilename = current != null ? current.getFilename() : null; String lastKnownModifiedBy = current != null ? current.getModifiedBy() : null; String lastKnownMimetype = current != null ? current.getMimetype() : null; Lazy<List<ContentVersion>> lazyLoadedHistory = newLazyLoadedHistory(iterator, version, lastKnownFilename, lastKnownModifiedBy, lastKnownMimetype); return new ContentImpl(id, current, lazyLoadedHistory, currentCheckedOut, checkedOutDateTime, checkedOutBy, emptyVersion); } private int getFactoryVersion(String string) { if (string.startsWith("v2:")) { return 2; } else { return 1; } } private void skipCurrentFileName(Iterator<String> iterator) { iterator.next(); } private void skipIsCheckedOut(Iterator<String> iterator) { iterator.next(); } private Lazy<List<ContentVersion>> newLazyLoadedHistory(final Iterator<String> iterator, final int version, final String lastKnownFilename, final String lastKnownModifiedBy, final String lastKnownMimetype) { return new Lazy<List<ContentVersion>>() { @Override protected List<ContentVersion> load() { List<ContentVersion> versions = new ArrayList<>(); String currentLastKnownFilename = lastKnownFilename; String currentLastKnownModifiedBy = lastKnownModifiedBy; String currentLastKnownMimetype = lastKnownMimetype; while (iterator.hasNext()) { ContentVersion contentVersion = toContentVersion(iterator.next(), version, currentLastKnownFilename, currentLastKnownModifiedBy, currentLastKnownMimetype); currentLastKnownFilename = contentVersion.getFilename(); currentLastKnownModifiedBy = contentVersion.getModifiedBy(); currentLastKnownMimetype = contentVersion.getMimetype(); versions.add(contentVersion); } return versions; } }; } private String toNullableString(String string) { return NULL_STRING.equals(string) ? null : string; } private String afterEqualSign(String string) { int index = string.indexOf("="); return string.substring(index + 1); } @Override public String toString(ModifiableStructure value) { //Version 2 StringBuilder stringBuilder = new StringBuilder("v2:"); ContentImpl contentInfo = (ContentImpl) value; ContentVersion current = contentInfo.getCurrentVersion(); String lastKnownFilename = current.getFilename(); String lastKnownModifiedBy = current.getModifiedBy(); String lastKnownMimetype = current.getMimetype(); stringBuilder.append(contentInfo.getId()); stringBuilder.append("::cf="); stringBuilder.append(current.getFilename()); stringBuilder.append("::co="); stringBuilder.append(contentInfo.getCheckoutUserId() == null ? "false" : "true"); stringBuilder.append(":"); stringBuilder.append(toString(current, null, null, null)); stringBuilder.append(toString(contentInfo.getCurrentCheckedOutVersion(), null, null, null)); stringBuilder.append(":"); if (contentInfo.isEmptyVersion()) { stringBuilder.append("_EMPTY_VERSION_::"); } stringBuilder.append(contentInfo.getCheckoutUserId()); stringBuilder.append("::"); stringBuilder.append(toString(contentInfo.getCheckoutDateTime())); stringBuilder.append(":"); for (ContentVersion historyVersion : contentInfo.getHistoryVersions()) { stringBuilder.append(toString(historyVersion, lastKnownFilename, lastKnownModifiedBy, lastKnownMimetype)); lastKnownFilename = historyVersion.getFilename(); lastKnownModifiedBy = historyVersion.getModifiedBy(); lastKnownMimetype = historyVersion.getMimetype(); } stringBuilder.append(":"); return stringBuilder.toString(); } private String toString(ContentVersion contentVersion, String lastKnownFilename, String lastKnownModifiedBy, String lastKnownMimetype) { if (contentVersion == null) { return ":" + NULL_STRING + ":"; } String filename = contentVersion.getFilename(); String modifiedBy = contentVersion.getModifiedBy(); String mimetype = contentVersion.getMimetype(); if (filename.equals(lastKnownFilename)) { filename = ""; } if (modifiedBy != null && modifiedBy.equals(lastKnownModifiedBy)) { modifiedBy = ""; } if (mimetype != null && mimetype.equals(lastKnownMimetype)) { mimetype = ""; } StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append(":f="); stringBuilder.append(filename); stringBuilder.append(":h="); stringBuilder.append(contentVersion.getHash()); stringBuilder.append(":l="); stringBuilder.append(contentVersion.getLength()); stringBuilder.append(":m="); stringBuilder.append(mimetype); stringBuilder.append(":u="); stringBuilder.append(modifiedBy); stringBuilder.append(":t="); stringBuilder.append(toString(contentVersion.getLastModificationDateTime())); stringBuilder.append(":v="); stringBuilder.append(contentVersion.getVersion()); stringBuilder.append(":"); stringBuilder.append(writeComment(contentVersion.getComment())); stringBuilder.append(":"); return stringBuilder.toString(); } private ContentVersion toContentVersion(String string, int version) { return toContentVersion(string, version, null, null, null); } private ContentVersion toContentVersion(String string, int version, String lastKnownFilename, String lastKnownModifiedBy, String lastKnownMimetype) { if (version == 2) { return toContentVersion2(string, lastKnownFilename, lastKnownModifiedBy, lastKnownMimetype); } else { return toContentVersion1(string); } } private ContentVersion toContentVersion1(String string) { if (string.equals(NULL_STRING)) { return null; } StringTokenizer tokenizer = new StringTokenizer(string, ":"); String filename = afterEqual(tokenizer.nextToken()); String hash = afterEqual(tokenizer.nextToken()); String lengthStr = afterEqual(tokenizer.nextToken()); String mimetype = afterEqual(tokenizer.nextToken()); String modifiedBy = afterEqual(tokenizer.nextToken()); String modifiedDateTime = afterEqual(tokenizer.nextToken()); String version = afterEqual(tokenizer.nextToken()); long length = Long.valueOf(lengthStr); ContentVersionDataSummary contentVersionDataSummary = new ContentVersionDataSummary(hash, mimetype, length); return new ContentVersion(contentVersionDataSummary, filename, version, modifiedBy, toDateTime(modifiedDateTime), null); } private ContentVersion toContentVersion2(String string, String lastKnownFilename, String lastKnownModifiedBy, String lastKnownMimetype) { if (string.equals(NULL_STRING)) { return null; } StringTokenizer tokenizer = new StringTokenizer(string, ":"); String filename = afterEqual(tokenizer.nextToken()); String hash = afterEqual(tokenizer.nextToken()); String lengthStr = afterEqual(tokenizer.nextToken()); String mimetype = afterEqual(tokenizer.nextToken()); String modifiedBy = afterEqual(tokenizer.nextToken()); String modifiedDateTime = afterEqual(tokenizer.nextToken()); String version = afterEqual(tokenizer.nextToken()); String comment = readComment(afterEqual(tokenizer.nextToken())); if (StringUtils.isBlank(filename)) { filename = lastKnownFilename; } if (StringUtils.isBlank(modifiedBy)) { modifiedBy = lastKnownModifiedBy; } if (StringUtils.isBlank(mimetype)) { mimetype = lastKnownMimetype; } long length = Long.valueOf(lengthStr); ContentVersionDataSummary contentVersionDataSummary = new ContentVersionDataSummary(hash, mimetype, length); return new ContentVersion(contentVersionDataSummary, filename, version, modifiedBy, toDateTime(modifiedDateTime), comment); } private LocalDateTime toDateTime(String dateTimeString) { if (dateTimeString.equals(NULL_STRING)) { return null; } Long timestamp = Long.valueOf(dateTimeString); return new LocalDateTime(timestamp); } private String toString(LocalDateTime localDateTime) { if (localDateTime == null) { return NULL_STRING; } long timestamp = localDateTime.toDate().getTime(); return "" + timestamp; } private String afterEqual(String keyValue) { int equalIndex = keyValue.indexOf("="); return keyValue.substring(equalIndex + 1); } private String readComment(String comment) { return comment.equals(NULL_STRING) ? null : comment.replace(COLON_REPLACER, ":"); } private String writeComment(String comment) { return StringUtils.isBlank(comment) ? null : comment.replace(":", COLON_REPLACER); } @Override public int hashCode() { return HashCodeBuilder.reflectionHashCode(this); } @Override public boolean equals(Object obj) { return EqualsBuilder.reflectionEquals(this, obj); } }