use of com.xpn.xwiki.doc.merge.MergeResult in project xwiki-platform by xwiki.
the class DocumentMergeImporter method merge.
private XarEntryMergeResult merge(String comment, XWikiDocument currentDocument, XWikiDocument previousDocument, XWikiDocument nextDocument, PackageConfiguration configuration) throws Exception {
XWikiContext xcontext = this.xcontextProvider.get();
// 3 ways merge
XWikiDocument mergedDocument = currentDocument.clone();
MergeConfiguration mergeConfiguration = new MergeConfiguration();
mergeConfiguration.setProvidedVersionsModifiables(true);
MergeResult documentMergeResult;
try {
documentMergeResult = mergedDocument.merge(previousDocument, nextDocument, mergeConfiguration, xcontext);
} catch (Exception e) {
// Unexpected error, lets behave as if there was a conflict
documentMergeResult = new MergeResult();
documentMergeResult.getLog().error("Unexpected exception thrown. Usually means there is a bug in the merge.", e);
documentMergeResult.setModified(true);
}
documentMergeResult.getLog().log(this.logger);
XWikiDocument documentToSave;
if (documentMergeResult.isModified() || !documentMergeResult.getLog().getLogsFrom(LogLevel.ERROR).isEmpty()) {
documentToSave = askDocumentToSave(currentDocument, previousDocument, nextDocument, mergedDocument, configuration, documentMergeResult);
if (documentToSave != currentDocument) {
saveDocument(documentToSave, comment, false, configuration);
}
}
return new XarEntryMergeResult(new XarEntry(new LocalDocumentReference(mergedDocument.getDocumentReferenceWithLocale())), documentMergeResult);
}
use of com.xpn.xwiki.doc.merge.MergeResult in project xwiki-platform by xwiki.
the class DefaultDocumentMergeImporterTest method setUp.
@Before
public void setUp() throws Exception {
this.xwiki = mock(XWiki.class);
when(this.xcontext.getWiki()).thenReturn(this.xwiki);
// documents
this.previousDocument = mock(XWikiDocument.class, "previous");
when(this.previousDocument.isNew()).thenReturn(false);
when(this.previousDocument.getDocumentReferenceWithLocale()).thenReturn(this.documentReference);
this.currentDocument = mock(XWikiDocument.class, "current");
when(this.currentDocument.isNew()).thenReturn(false);
when(this.currentDocument.getDocumentReferenceWithLocale()).thenReturn(this.documentReference);
when(this.xwiki.getDocument(same(this.documentReference), same(xcontext))).thenReturn(this.currentDocument);
this.nextDocument = mock(XWikiDocument.class, "next");
when(this.nextDocument.isNew()).thenReturn(false);
when(this.nextDocument.getDocumentReferenceWithLocale()).thenReturn(this.documentReference);
this.mergedDocument = mock(XWikiDocument.class, "merged");
when(this.mergedDocument.isNew()).thenReturn(false);
when(this.mergedDocument.getDocumentReferenceWithLocale()).thenReturn(this.documentReference);
when(this.currentDocument.clone()).thenReturn(this.mergedDocument);
// merge
this.configuration = new PackageConfiguration();
this.mergeResult = new MergeResult();
when(this.mergedDocument.merge(same(this.previousDocument), same(this.nextDocument), any(MergeConfiguration.class), any(XWikiContext.class))).thenReturn(this.mergeResult);
// job status
this.jobStatus = mock(JobStatus.class);
this.configuration.setJobStatus(this.jobStatus);
// execution
this.econtext = new ExecutionContext();
this.execution = this.mocker.getInstance(Execution.class);
when(this.execution.getContext()).thenReturn(this.econtext);
}
use of com.xpn.xwiki.doc.merge.MergeResult in project xwiki-platform by xwiki.
the class XWikiDocument method merge.
/**
* Apply a 3 ways merge on the current document based on provided previous and new version of the document.
* <p>
* All 3 documents are supposed to have the same document reference and language already since that's what makes
* them uniques.
*
* @param previousDocument the previous version of the document
* @param newDocument the next version of the document
* @param configuration the configuration of the merge indicates how to deal with some conflicts use cases, etc.
* @param context the XWiki context
* @return a repport of what happen during the merge (errors, etc.)
* @since 3.2M1
*/
public MergeResult merge(XWikiDocument previousDocument, XWikiDocument newDocument, MergeConfiguration configuration, XWikiContext context) {
MergeResult mergeResult = new MergeResult();
// Title
setTitle(MergeUtils.mergeOject(previousDocument.getTitle(), newDocument.getTitle(), getTitle(), mergeResult));
// Content
setContent(MergeUtils.mergeLines(previousDocument.getContent(), newDocument.getContent(), getContent(), mergeResult));
// Syntax
setSyntax(MergeUtils.mergeOject(previousDocument.getSyntax(), newDocument.getSyntax(), getSyntax(), mergeResult));
// Default locale
setDefaultLocale(MergeUtils.mergeOject(previousDocument.getDefaultLocale(), newDocument.getDefaultLocale(), getDefaultLocale(), mergeResult));
// Parent
setParentReference(MergeUtils.mergeOject(previousDocument.getRelativeParentReference(), newDocument.getRelativeParentReference(), getRelativeParentReference(), mergeResult));
// DefaultTemplate
setDefaultTemplate(MergeUtils.mergeOject(previousDocument.getDefaultTemplate(), newDocument.getDefaultTemplate(), getDefaultTemplate(), mergeResult));
// Hidden
setHidden(MergeUtils.mergeOject(previousDocument.isHidden(), newDocument.isHidden(), isHidden(), mergeResult));
// CustomClass
setCustomClass(MergeUtils.mergeLines(previousDocument.getCustomClass(), newDocument.getCustomClass(), getCustomClass(), mergeResult));
// ValidationScript
setValidationScript(MergeUtils.mergeLines(previousDocument.getValidationScript(), newDocument.getValidationScript(), getValidationScript(), mergeResult));
// Objects
List<List<ObjectDiff>> objectsDiff = previousDocument.getObjectDiff(previousDocument, newDocument, context);
if (!objectsDiff.isEmpty()) {
// Apply diff on result
for (List<ObjectDiff> objectClassDiff : objectsDiff) {
for (ObjectDiff diff : objectClassDiff) {
BaseObject objectResult = getXObject(diff.getXClassReference(), diff.getNumber());
BaseObject previousObject = previousDocument.getXObject(diff.getXClassReference(), diff.getNumber());
BaseObject newObject = newDocument.getXObject(diff.getXClassReference(), diff.getNumber());
PropertyInterface propertyResult = objectResult != null ? objectResult.getField(diff.getPropName()) : null;
PropertyInterface previousProperty = previousObject != null ? previousObject.getField(diff.getPropName()) : null;
PropertyInterface newProperty = newObject != null ? newObject.getField(diff.getPropName()) : null;
if (diff.getAction() == ObjectDiff.ACTION_OBJECTADDED) {
if (objectResult == null) {
setXObject(newObject.getNumber(), configuration.isProvidedVersionsModifiables() ? newObject : newObject.clone());
mergeResult.setModified(true);
} else {
// collision between DB and new: object to add but already exists in the DB
mergeResult.getLog().error("Collision found on object [{}]", objectResult.getReference());
}
} else if (diff.getAction() == ObjectDiff.ACTION_OBJECTREMOVED) {
if (objectResult != null) {
if (objectResult.equals(previousObject)) {
removeXObject(objectResult);
mergeResult.setModified(true);
} else {
// collision between DB and new: object to remove but not the same as previous
// version
mergeResult.getLog().error("Collision found on object [{}]", objectResult.getReference());
}
} else {
// Already removed from DB, lets assume the user is prescient
mergeResult.getLog().warn("Object [{}] already removed", previousObject.getReference());
}
} else if (previousObject != null && newObject != null) {
if (objectResult != null) {
if (diff.getAction() == ObjectDiff.ACTION_PROPERTYADDED) {
if (propertyResult == null) {
objectResult.safeput(diff.getPropName(), newProperty);
mergeResult.setModified(true);
} else {
// collision between DB and new: property to add but already exists in the DB
mergeResult.getLog().error("Collision found on object property [{}]", propertyResult.getReference());
}
} else if (diff.getAction() == ObjectDiff.ACTION_PROPERTYREMOVED) {
if (propertyResult != null) {
if (propertyResult.equals(previousProperty)) {
objectResult.removeField(diff.getPropName());
mergeResult.setModified(true);
} else {
// collision between DB and new: supposed to be removed but the DB version is
// not the same as the previous version
mergeResult.getLog().error("Collision found on object property [{}]", propertyResult.getReference());
}
} else {
// Already removed from DB, lets assume the user is prescient
mergeResult.getLog().warn("Object property [{}] already removed", previousProperty.getReference());
}
} else if (diff.getAction() == ObjectDiff.ACTION_PROPERTYCHANGED) {
if (propertyResult != null) {
if (propertyResult.equals(previousProperty)) {
objectResult.safeput(diff.getPropName(), newProperty);
mergeResult.setModified(true);
} else {
// Try to apply a 3 ways merge on the property
propertyResult.merge(previousProperty, newProperty, configuration, context, mergeResult);
}
} else {
// collision between DB and new: property to modify but does not exists in DB
// Lets assume it's a mistake to fix
mergeResult.getLog().warn("Object [{}] does not exists", newProperty.getReference());
objectResult.safeput(diff.getPropName(), newProperty);
mergeResult.setModified(true);
}
}
} else {
// Object explitely removed from the DB, lets assume we don't care about the changes
mergeResult.getLog().warn("Object [{}] already removed", previousObject.getReference());
}
}
}
}
}
// Class
BaseClass classResult = getXClass();
BaseClass previousClass = previousDocument.getXClass();
BaseClass newClass = newDocument.getXClass();
classResult.merge(previousClass, newClass, configuration, context, mergeResult);
// Attachments
List<AttachmentDiff> attachmentsDiff = previousDocument.getAttachmentDiff(previousDocument, newDocument, context);
if (!attachmentsDiff.isEmpty()) {
// Apply deleted attachment diff on result (new attachment has already been saved)
for (AttachmentDiff diff : attachmentsDiff) {
XWikiAttachment previousAttachment = diff.getOrigAttachment();
XWikiAttachment nextAttachment = diff.getNewAttachment();
XWikiAttachment attachment = getAttachment(diff.getFileName());
switch(diff.getType()) {
case DELETE:
if (attachment != null) {
try {
if (attachment.equalsData(previousAttachment, context)) {
removeAttachment(attachment);
mergeResult.setModified(true);
} else {
// collision between DB and new: attachment modified by user
mergeResult.getLog().error("Collision found on attachment [{}]", attachment.getReference());
}
} catch (XWikiException e) {
mergeResult.getLog().error("Failed to compare attachments with reference [{}]", attachment.getReference());
}
} else {
// Already removed from DB, lets assume the user is prescient
mergeResult.getLog().warn("Attachment [{}] already removed", previousAttachment.getReference());
}
break;
case INSERT:
if (attachment != null) {
try {
if (!attachment.equalsData(nextAttachment, context)) {
// collision between DB and new: attachment to add but a different one already
// exists in the DB
mergeResult.getLog().error("Collision found on attachment [{}]", attachment.getReference());
} else {
// Already added to the DB, lets assume the user is prescient
mergeResult.getLog().warn("Attachment [{}] already added", previousAttachment.getReference());
}
} catch (XWikiException e) {
mergeResult.getLog().error("Failed to compare attachments with reference [{}]", attachment.getReference());
}
} else {
addAttachment(configuration.isProvidedVersionsModifiables() ? nextAttachment : (XWikiAttachment) nextAttachment.clone());
mergeResult.setModified(true);
}
break;
case CHANGE:
if (attachment != null) {
attachment.merge(previousAttachment, nextAttachment, configuration, context, mergeResult);
} else {
// collision between DB and new: attachment modified but does not exist in the DB
mergeResult.getLog().error("Collision found on attachment [{}]", previousAttachment.getReference());
}
break;
default:
break;
}
}
}
return mergeResult;
}
use of com.xpn.xwiki.doc.merge.MergeResult in project xwiki-platform by xwiki.
the class XWikiDocumentMergeTest method testMergeAttachmentNew.
@Test
public void testMergeAttachmentNew() throws Exception {
XWikiAttachment attachment = new XWikiAttachment();
attachment.setContent(new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 });
attachment.setLongSize(10);
attachment.setFilename("file");
this.nextDocument.addAttachment(attachment);
MergeResult result = merge();
Assert.assertTrue(result.isModified());
XWikiAttachment newAttachment = this.currentDocument.getAttachment("file");
Assert.assertNotNull(newAttachment);
Assert.assertEquals(10, newAttachment.getLongSize());
Assert.assertArrayEquals(new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }, newAttachment.getContent(null));
}
use of com.xpn.xwiki.doc.merge.MergeResult in project xwiki-platform by xwiki.
the class XWikiDocumentMergeTest method testMergeAttachmentDeleted.
@Test
public void testMergeAttachmentDeleted() throws Exception {
XWikiAttachment attachment = new XWikiAttachment();
attachment.setContent(new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 });
attachment.setLongSize(10);
attachment.setFilename("file");
this.currentDocument.addAttachment(attachment);
this.previousDocument.addAttachment((XWikiAttachment) attachment.clone());
MergeResult result = merge();
Assert.assertTrue(result.isModified());
XWikiAttachment newAttachment = this.currentDocument.getAttachment("file");
Assert.assertNull(newAttachment);
}
Aggregations