Search in sources :

Example 1 with ObjectDiff

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();
}
Also used : Div(org.apache.ecs.html.Div) ObjectDiff(com.xpn.xwiki.objects.ObjectDiff) Span(org.apache.ecs.html.Span) XWikiException(com.xpn.xwiki.XWikiException)

Example 2 with ObjectDiff

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;
}
Also used : ObjectDiff(com.xpn.xwiki.objects.ObjectDiff) PropertyInterface(com.xpn.xwiki.objects.PropertyInterface) MergeResult(com.xpn.xwiki.doc.merge.MergeResult) BaseClass(com.xpn.xwiki.objects.classes.BaseClass) XWikiAttachmentList(com.xpn.xwiki.internal.doc.XWikiAttachmentList) ArrayList(java.util.ArrayList) List(java.util.List) XWikiException(com.xpn.xwiki.XWikiException) BaseObject(com.xpn.xwiki.objects.BaseObject)

Example 3 with ObjectDiff

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;
}
Also used : ObjectDiff(com.xpn.xwiki.objects.ObjectDiff) ArrayList(java.util.ArrayList) BaseClass(com.xpn.xwiki.objects.classes.BaseClass) XWikiAttachmentList(com.xpn.xwiki.internal.doc.XWikiAttachmentList) ArrayList(java.util.ArrayList) List(java.util.List)

Example 4 with ObjectDiff

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;
}
Also used : ObjectDiff(com.xpn.xwiki.objects.ObjectDiff) ArrayList(java.util.ArrayList) BaseCollection(com.xpn.xwiki.objects.BaseCollection) Collection(java.util.Collection)

Example 5 with ObjectDiff

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();
}
Also used : Div(org.apache.ecs.html.Div) ObjectDiff(com.xpn.xwiki.objects.ObjectDiff) Span(org.apache.ecs.html.Span) XWikiException(com.xpn.xwiki.XWikiException)

Aggregations

ObjectDiff (com.xpn.xwiki.objects.ObjectDiff)8 ArrayList (java.util.ArrayList)4 XWikiException (com.xpn.xwiki.XWikiException)3 XWikiAttachmentList (com.xpn.xwiki.internal.doc.XWikiAttachmentList)3 BaseObject (com.xpn.xwiki.objects.BaseObject)3 BaseClass (com.xpn.xwiki.objects.classes.BaseClass)3 List (java.util.List)3 PropertyInterface (com.xpn.xwiki.objects.PropertyInterface)2 Div (org.apache.ecs.html.Div)2 Span (org.apache.ecs.html.Span)2 MergeResult (com.xpn.xwiki.doc.merge.MergeResult)1 BaseCollection (com.xpn.xwiki.objects.BaseCollection)1 Collection (java.util.Collection)1