use of com.xpn.xwiki.objects.ObjectDiff in project xwiki-platform by xwiki.
the class WatchListEvent method getObjectsHTMLDiff.
/**
* @param objectDiffs List of object diff
* @param isXWikiClass is the diff to compute the diff for a xwiki class, the other possibility being a plain xwiki
* object
* @param documentFullName full name of the document the diff is computed for
* @param diff the diff plugin API
* @return The HTML diff
*/
private String getObjectsHTMLDiff(List<List<ObjectDiff>> objectDiffs, boolean isXWikiClass, String documentFullName, DiffPluginApi diff) {
StringBuffer result = new StringBuffer();
String propSeparator = ": ";
String prefix = (isXWikiClass) ? "class" : "object";
try {
for (List<ObjectDiff> oList : objectDiffs) {
if (oList.size() > 0) {
Div mainDiv = createDiffDiv(prefix + "Diff");
Span objectName = createDiffSpan(prefix + "ClassName");
if (isXWikiClass) {
objectName.addElement(getFullName());
} else {
objectName.addElement(oList.get(0).getClassName());
}
mainDiv.addElement(prefix + HTML_IMG_PLACEHOLDER_SUFFIX);
mainDiv.addElement(objectName);
for (ObjectDiff oDiff : oList) {
String propDiff = getPropertyHTMLDiff(oDiff, diff);
if (!StringUtils.isBlank(oDiff.getPropName()) && !StringUtils.isBlank(propDiff)) {
Div propDiv = createDiffDiv("propDiffContainer");
Span propNameSpan = createDiffSpan("propName");
propNameSpan.addElement(oDiff.getPropName() + propSeparator);
String shortPropType = StringUtils.removeEnd(oDiff.getPropType(), "Class").toLowerCase();
if (StringUtils.isBlank(shortPropType)) {
// When the diff shows a property that has been deleted, its type is not available.
shortPropType = HTML_IMG_METADATA_PREFIX;
}
propDiv.addElement(shortPropType + HTML_IMG_PLACEHOLDER_SUFFIX);
propDiv.addElement(propNameSpan);
Div propDiffDiv = createDiffDiv("propDiff");
propDiffDiv.addElement(propDiff);
propDiv.addElement(propDiffDiv);
mainDiv.addElement(propDiv);
}
}
result.append(mainDiv);
}
}
} catch (XWikiException e) {
// Catch the exception to be sure we won't send emails containing stacktraces to users.
e.printStackTrace();
}
return result.toString();
}
use of com.xpn.xwiki.objects.ObjectDiff 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.objects.ObjectDiff in project xwiki-platform by xwiki.
the class XWikiDocument method getClassDiff.
public List<List<ObjectDiff>> getClassDiff(XWikiDocument fromDoc, XWikiDocument toDoc, XWikiContext context) {
List<List<ObjectDiff>> difflist = new ArrayList<List<ObjectDiff>>();
BaseClass oldClass = fromDoc.getXClass();
BaseClass newClass = toDoc.getXClass();
if ((newClass == null) && (oldClass == null)) {
return difflist;
}
List<ObjectDiff> dlist = newClass.getDiff(oldClass, context);
if (!dlist.isEmpty()) {
difflist.add(dlist);
}
return difflist;
}
use of com.xpn.xwiki.objects.ObjectDiff in project xwiki-platform by xwiki.
the class BaseClass method getDiff.
@Override
public List<ObjectDiff> getDiff(Object oldObject, XWikiContext context) {
ArrayList<ObjectDiff> difflist = new ArrayList<ObjectDiff>();
BaseClass oldClass = (BaseClass) oldObject;
for (PropertyClass newProperty : (Collection<PropertyClass>) getFieldList()) {
String propertyName = newProperty.getName();
PropertyClass oldProperty = (PropertyClass) oldClass.get(propertyName);
String propertyType = newProperty.getClassType();
if (oldProperty == null) {
difflist.add(new ObjectDiff(getXClassReference(), getNumber(), "", ObjectDiff.ACTION_PROPERTYADDED, propertyName, propertyType, "", ""));
} else if (!oldProperty.equals(newProperty)) {
difflist.add(new ObjectDiff(getXClassReference(), getNumber(), "", ObjectDiff.ACTION_PROPERTYCHANGED, propertyName, propertyType, "", ""));
}
}
for (PropertyClass oldProperty : (Collection<PropertyClass>) oldClass.getFieldList()) {
String propertyName = oldProperty.getName();
PropertyClass newProperty = (PropertyClass) get(propertyName);
String propertyType = oldProperty.getClassType();
if (newProperty == null) {
difflist.add(new ObjectDiff(getXClassReference(), getNumber(), "", ObjectDiff.ACTION_PROPERTYREMOVED, propertyName, propertyType, "", ""));
}
}
return difflist;
}
use of com.xpn.xwiki.objects.ObjectDiff in project xwiki-platform by xwiki.
the class DefaultWatchListEventHTMLDiffExtractor method getObjectsHTMLDiff.
/**
* @param objectDiffs the list of object diff
* @param isXWikiClass true if the diff to compute is for an XWiki class, false if it's a plain XWiki object
* @param documentFullName full name of the document the diff is computed for
* @param diff the diff plugin API
* @return the HTML string displaying the specified list of diffs
*/
private String getObjectsHTMLDiff(List<List<ObjectDiff>> objectDiffs, boolean isXWikiClass, String documentFullName, DiffPluginApi diff) {
StringBuffer result = new StringBuffer();
String propSeparator = ": ";
String prefix = (isXWikiClass) ? "class" : "object";
try {
for (List<ObjectDiff> oList : objectDiffs) {
if (oList.isEmpty()) {
continue;
}
// The main container
Div mainDiv = createDiffDiv(prefix + "Diff");
// Class name
Span objectName = createDiffSpan(prefix + "ClassName");
if (isXWikiClass) {
objectName.addElement(documentFullName);
} else {
objectName.addElement(oList.get(0).getClassName());
}
mainDiv.addElement(prefix + HTML_IMG_PLACEHOLDER_SUFFIX);
mainDiv.addElement(objectName);
// Diffs for each property
for (ObjectDiff oDiff : oList) {
String propDiff = getPropertyHTMLDiff(oDiff, diff);
if (StringUtils.isBlank(oDiff.getPropName()) || StringUtils.isBlank(propDiff)) {
// Skip invalid properties or the ones that have no changed.
continue;
}
Div propDiv = createDiffDiv("propDiffContainer");
// Property name
Span propNameSpan = createDiffSpan("propName");
propNameSpan.addElement(oDiff.getPropName() + propSeparator);
String shortPropType = StringUtils.removeEnd(oDiff.getPropType(), "Class").toLowerCase();
if (StringUtils.isBlank(shortPropType)) {
// When the diff shows a property that has been deleted, its type is not available.
shortPropType = HTML_IMG_METADATA_PREFIX;
}
propDiv.addElement(shortPropType + HTML_IMG_PLACEHOLDER_SUFFIX);
propDiv.addElement(propNameSpan);
// Property diff
Div propDiffDiv = createDiffDiv("propDiff");
propDiffDiv.addElement(propDiff);
propDiv.addElement(propDiffDiv);
// Add it to the main div
mainDiv.addElement(propDiv);
}
result.append(mainDiv);
}
} catch (XWikiException e) {
// Catch the exception to be sure we won't send emails containing stacktraces to users.
logger.error("Failed to compute HTML objects or class diff", e);
}
return result.toString();
}
Aggregations