diff --git a/xwiki-platform-core/xwiki-platform-model/xwiki-platform-model-api/src/main/java/org/xwiki/model/reference/EntityReference.java b/xwiki-platform-core/xwiki-platform-model/xwiki-platform-model-api/src/main/java/org/xwiki/model/reference/EntityReference.java index 5dbfc3c13c71..207e8a05fe0a 100644 --- a/xwiki-platform-core/xwiki-platform-model/xwiki-platform-model-api/src/main/java/org/xwiki/model/reference/EntityReference.java +++ b/xwiki-platform-core/xwiki-platform-model/xwiki-platform-model-api/src/main/java/org/xwiki/model/reference/EntityReference.java @@ -22,6 +22,7 @@ import java.beans.Transient; import java.io.Serializable; import java.util.Collections; +import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -677,16 +678,6 @@ public int hashCode() .append(this.parameters).toHashCode(); } - /** - * {@inheritDoc} - *

- * Note: The default implementation relies on comparing the string serialization of the 2 entities. It is the - * caller's responsibility to make sure that the entities are either first resolved or at least of the same type, in - * order for the comparison to actually make sense. - *

- * - * @see java.lang.Comparable#compareTo(java.lang.Object) - */ @Override public int compareTo(EntityReference reference) { @@ -698,47 +689,71 @@ public int compareTo(EntityReference reference) return 0; } - // Generically compare the string serializations of the 2 references. - int stringCompareResult = toString().compareTo(reference.toString()); - if (stringCompareResult != 0) { - return stringCompareResult; + List references = getReversedReferenceChain(); + Iterator it = references.iterator(); + List otherReferences = reference.getReversedReferenceChain(); + Iterator otherIt = otherReferences.iterator(); + while (it.hasNext() && otherIt.hasNext()) { + EntityReference element = it.next(); + EntityReference otherElement = otherIt.next(); + + // Compare the names + int result = element.getName().compareTo(otherElement.getName()); + if (result != 0) { + return result; + } + + // Compare the parameters + result = compareParameters(element.getParameters(), otherElement.getParameters()); + if (result != 0) { + return result; + } } - // If the string serializations are the same, compare the parameters. - return compareParameters(reference); + return references.size() - otherReferences.size(); } /** - * Compare parameters of this reference and another reference. + * Compare parameters of two references. * - * @param reference the other reference to be compare with - * @return 0 if parameters are equals, -1 if this reference has lower parameters, +1 otherwise - */ - @SuppressWarnings("unchecked") - private int compareParameters(EntityReference reference) - { - if (parameters != null && reference.parameters == null) { - return 1; + * @param parameters the first parameters to compare + * @param otherParameters the other parameters to compare + * @return 0 if parameters are equals, -1 if the first parameters are lower, +1 otherwise + */ + private int compareParameters(Map parameters, Map otherParameters) + { + for (Map.Entry entry : parameters.entrySet()) { + Object value = entry.getValue(); + Object otherValue = otherParameters.get(entry.getKey()); + int result = compareTo(value, otherValue); + if (result != 0) { + return result; + } } - if (parameters != null) { - for (Map.Entry entry : parameters.entrySet()) { - Object obj = reference.parameters.get(entry.getKey()); - Object myobj = entry.getValue(); - if (myobj instanceof Comparable) { - if (obj == null) { - return 1; - } - - int number = ((Comparable) myobj).compareTo(obj); - - if (number != 0) { - return number; - } - } + return parameters.size() - otherParameters.size(); + } + + private int compareTo(Object value, Object otherValue) + { + if (value != otherValue) { + if (value == null) { + return -1; + } + + if (otherValue == null) { + return 1; + } + + if (value.getClass() == otherValue.getClass() && value instanceof Comparable) { + return ((Comparable) value).compareTo(otherValue); + } + + if (!value.equals(otherValue)) { + return 1; } } - return (reference.parameters == null) ? 0 : -1; + return 0; } } diff --git a/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/doc/XWikiDocument.java b/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/doc/XWikiDocument.java index b5034f3fc101..031e3b289627 100644 --- a/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/doc/XWikiDocument.java +++ b/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/doc/XWikiDocument.java @@ -60,6 +60,7 @@ import javax.inject.Provider; import javax.servlet.http.HttpServletResponse; +import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; @@ -3306,7 +3307,7 @@ public void addXObject(BaseObject object) { object.setOwnerDocument(this); - List vobj = this.xObjects.get(object.getXClassReference()); + BaseObjects vobj = this.xObjects.get(object.getXClassReference()); if (vobj == null) { setXObject(0, object); } else { @@ -3335,15 +3336,9 @@ public void setXObject(DocumentReference classReference, int nb, BaseObject obje object.setNumber(nb); } - BaseObjects objects = this.xObjects.get(classReference); - if (objects == null) { - objects = new BaseObjects(); - this.xObjects.put(classReference, objects); - } - while (nb >= objects.size()) { - objects.add(null); - } - objects.set(nb, object); + BaseObjects objects = this.xObjects.computeIfAbsent(classReference, k -> new BaseObjects()); + objects.put(nb, object); + setMetaDataDirty(true); } @@ -3361,15 +3356,9 @@ public void setXObject(int nb, BaseObject object) object.setOwnerDocument(this); object.setNumber(nb); - BaseObjects objects = this.xObjects.get(object.getXClassReference()); - if (objects == null) { - objects = new BaseObjects(); - this.xObjects.put(object.getXClassReference(), objects); - } - while (nb >= objects.size()) { - objects.add(null); - } - objects.set(nb, object); + BaseObjects objects = this.xObjects.computeIfAbsent(object.getXClassReference(), k -> new BaseObjects()); + objects.put(nb, object); + setMetaDataDirty(true); } @@ -3473,31 +3462,32 @@ public void duplicateXObjects(XWikiDocument templatedoc) /** * Copy specified document objects into current document. * - * @param templatedoc the document to copy - * @param keepsIdentity if true it does an exact java copy, otherwise it duplicate objects with the new document - * name (and new class names) + * @param templateDocument the document to copy + * @param keepsIdentity if true it does an exact copy (same guid), otherwise it create a new object with the same + * values */ - private void cloneXObjects(XWikiDocument templatedoc, boolean keepsIdentity) + private void cloneXObjects(XWikiDocument templateDocument, boolean keepsIdentity) { // clean map this.xObjects.clear(); // fill map - for (Map.Entry> entry : templatedoc.getXObjects().entrySet()) { - List tobjects = entry.getValue(); - - // clone and insert xobjects - for (BaseObject otherObject : tobjects) { - if (otherObject != null) { - if (keepsIdentity) { - addXObject(otherObject.clone()); - } else { - BaseObject newObject = otherObject.duplicate(getDocumentReference()); - setXObject(newObject.getNumber(), newObject); + for (Map.Entry entry : templateDocument.xObjects.entrySet()) { + BaseObjects tobjects = entry.getValue(); + + if (CollectionUtils.isNotEmpty(tobjects)) { + BaseObjects objects = new BaseObjects(this, tobjects, keepsIdentity); + + if (!objects.isEmpty()) { + DocumentReference xclassReference = entry.getKey(); + WikiReference wikiReference = getDocumentReference().getWikiReference(); + if (!wikiReference.equals(xclassReference.getWikiReference())) { + // Make sure the class reference is in the same wiki as the document in which the object is + // stored + xclassReference = xclassReference.setWikiReference(wikiReference); } - } else if (keepsIdentity) { - // set null object to make sure to have exactly the same thing when cloning a document - addXObject(entry.getKey(), null); + + this.xObjects.put(xclassReference, objects); } } } @@ -4597,13 +4587,12 @@ private XWikiDocument cloneInternal(DocumentReference newDocumentReference, bool if (keepsIdentity) { doc.setXClassXML(getXClassXML()); - doc.cloneXObjects(this); doc.cloneAttachments(this); } else { doc.getXClass().setCustomMapping(null); - doc.duplicateXObjects(this); doc.copyAttachments(this); } + doc.cloneXObjects(this, keepsIdentity); doc.setContentDirty(isContentDirty()); doc.setMetaDataDirty(isMetaDataDirty()); diff --git a/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/internal/doc/BaseObjects.java b/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/internal/doc/BaseObjects.java index 1c06623e2447..acf1496ad342 100644 --- a/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/internal/doc/BaseObjects.java +++ b/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/internal/doc/BaseObjects.java @@ -24,6 +24,7 @@ import java.util.Map; import java.util.concurrent.ConcurrentSkipListMap; +import com.xpn.xwiki.doc.XWikiDocument; import com.xpn.xwiki.objects.BaseObject; /** @@ -61,6 +62,38 @@ public BaseObjects(Collection collection) collection.forEach(this::add); } + /** + * @param document the document when the current {@link BaseObjects} is stored + * @param otherObjects the objects to copy + * @param keepsIdentity if true it does an exact copy (same guid), otherwise it create a new object with the same + * values + * @since 16.10.0RC1 + * @since 16.4.5 + * @since 15.10.14 + */ + public BaseObjects(XWikiDocument document, BaseObjects otherObjects, boolean keepsIdentity) + { + // Make sure to keep the same size if keepsIdentity is true + this.size = keepsIdentity ? otherObjects.size() : 0; + + for (Map.Entry entry : otherObjects.map.entrySet()) { + BaseObject otherObject = entry.getValue(); + BaseObject clonedObject = + keepsIdentity ? otherObject.clone() : otherObject.duplicate(document.getDocumentReference()); + + // Make sure the cloned object has the right document (in case was is cloned in the document) + clonedObject.setOwnerDocument(document); + + // Make sure to maintain the same position in the list since it's part of the object reference + put(entry.getKey(), clonedObject); + + // Update the size + if (this.size <= otherObject.getNumber()) { + this.size = otherObject.getNumber() + 1; + } + } + } + @Override public BaseObject get(int index) { @@ -75,27 +108,6 @@ public int size() return this.size; } - private BaseObject put(int index, BaseObject element) - { - BaseObject old; - if (element == null) { - // We don't want to keep null values in memory - old = this.map.remove(index); - } else { - // Make sure the object number is right - element.setNumber(index); - - old = this.map.put(index, element); - } - - // Increment size if needed - if (this.size <= index) { - this.size = index + 1; - } - - return old; - } - @Override public void add(int index, BaseObject element) { @@ -123,6 +135,32 @@ public BaseObject set(int index, BaseObject element) return put(index, element); } + /** + * @param index the index of the object in the list + * @param object the object + * @return the object previously at the specified position + */ + public BaseObject put(int index, BaseObject object) + { + BaseObject old; + if (object == null) { + // We don't want to keep null values in memory + old = this.map.remove(index); + } else { + // Make sure the object number is right + object.setNumber(index); + + old = this.map.put(index, object); + } + + // Increment size if needed + if (this.size <= index) { + this.size = index + 1; + } + + return old; + } + @Override public BaseObject remove(int index) {