From 7ab07ddaefe8956ebf6682036896dcde078f76c0 Mon Sep 17 00:00:00 2001 From: Simon Urli Date: Wed, 27 Nov 2024 14:54:08 +0100 Subject: [PATCH] [TBD] Allow to resolve relative entity references properly * Provide EntityReference#getParentType * Use it in AbstractReferenceEntityReferenceResolver * Provide a new test --- ...tractReferenceEntityReferenceResolver.java | 14 +++++--- .../model/reference/EntityReference.java | 35 ++++++++++++++++++- ...tReferenceEntityReferenceResolverTest.java | 24 ++++++++++--- 3 files changed, 64 insertions(+), 9 deletions(-) diff --git a/xwiki-platform-core/xwiki-platform-model/xwiki-platform-model-api/src/main/java/org/xwiki/model/internal/reference/AbstractReferenceEntityReferenceResolver.java b/xwiki-platform-core/xwiki-platform-model/xwiki-platform-model-api/src/main/java/org/xwiki/model/internal/reference/AbstractReferenceEntityReferenceResolver.java index c62aa22c9052..0ed295203a8b 100644 --- a/xwiki-platform-core/xwiki-platform-model/xwiki-platform-model-api/src/main/java/org/xwiki/model/internal/reference/AbstractReferenceEntityReferenceResolver.java +++ b/xwiki-platform-core/xwiki-platform-model/xwiki-platform-model-api/src/main/java/org/xwiki/model/internal/reference/AbstractReferenceEntityReferenceResolver.java @@ -90,18 +90,19 @@ private EntityReference normalizeReference(EntityReference referenceToResolve, O EntityReference reference = normalizedReference; while (reference != null) { List types = reference.getType().getAllowedParents(); - if (reference.getParent() != null && !types.isEmpty() && !types.contains(reference.getParent().getType())) { + if (reference.getParent() != null + && isParentTypeAndAllowedTypeNotMatching(types, reference.getParentType())) { // The parent reference isn't the allowed parent: insert an allowed reference EntityReference newReference = resolveDefaultReference(types.get(0), parameters).appendParent(reference.getParent()); normalizedReference = normalizedReference.replaceParent(reference.getParent(), newReference); reference = newReference; - } else if (reference.getParent() == null && !types.isEmpty()) { + } else if (reference.getParent() == null && reference.getParentType() != null) { // The top reference isn't the allowed top level reference, add a parent reference - EntityReference newReference = resolveDefaultReference(types.get(0), parameters); + EntityReference newReference = resolveDefaultReference(reference.getParentType(), parameters); normalizedReference = normalizedReference.appendParent(newReference); reference = newReference; - } else if (reference.getParent() != null && types.isEmpty()) { + } else if (reference.getParent() != null && (types.isEmpty() || reference.getParentType() == null)) { // There's a parent but no one is allowed throw new InvalidEntityReferenceException(); } else { @@ -112,4 +113,9 @@ private EntityReference normalizeReference(EntityReference referenceToResolve, O return normalizedReference; } + + private boolean isParentTypeAndAllowedTypeNotMatching(List allowedTypes, EntityType parentType) + { + return !allowedTypes.isEmpty() && parentType != null && !allowedTypes.contains(parentType); + } } 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 f5abe926e63e..41590f354b5b 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 @@ -33,6 +33,7 @@ import org.xwiki.model.EntityType; import org.xwiki.model.internal.reference.DefaultSymbolScheme; import org.xwiki.model.internal.reference.LocalizedStringEntityReferenceSerializer; +import org.xwiki.stability.Unstable; /** * Represents a reference to an Entity (Document, Attachment, Space, Wiki, etc). @@ -42,6 +43,13 @@ */ public class EntityReference implements Serializable, Cloneable, Comparable { + /** + * See {@link #getParentType()}. + * @since 17.0.0RC1 + */ + @Unstable + public static final String PARENT_TYPE_PARAMETER = "ParentType"; + /** * Used to provide a nice and readable pretty name for the {@link #toString()} method. */ @@ -190,7 +198,7 @@ public EntityReference(String name, EntityType type, EntityReference parent, Map } /** - * Clone an EntityReference, but use the specified paramaters. + * Clone an EntityReference, but use the specified parameters. * * @param reference the reference to clone * @param parameters parameters for this reference, may be null @@ -679,6 +687,31 @@ public boolean equalsNonRecursive(EntityReference otherReference) && (parameters == null ? otherReference.parameters == null : parameters.equals(otherReference.parameters)); } + /** + * The parent type information is used by resolvers to identify which part of the base reference should be kept. + * If the entity reference has a parent (see {@link #getParent()}) then this type should always be the type of + * the parent. Now if the entity reference doesn't have the parent this value can be given by the + * {@link #PARENT_TYPE_PARAMETER} parameter (see {@link #getParameter(String)}), and if none is given it will + * fall back on first allowed parents (see {@link EntityType#getAllowedParents()} of current type returned by + * {@link #getType()}. + * @return the type of the parent to be used for computing the proper base reference in resolvers. + * @since 17.0.0RC1 + */ + @Unstable + public EntityType getParentType() + { + EntityType result; + if (parent == null) { + result = getParameter(PARENT_TYPE_PARAMETER); + if (result == null && !getType().getAllowedParents().isEmpty()) { + result = getType().getAllowedParents().get(0); + } + } else { + result = parent.getType(); + } + return result; + } + @Override public int hashCode() { diff --git a/xwiki-platform-core/xwiki-platform-model/xwiki-platform-model-api/src/test/java/org/xwiki/model/internal/reference/DefaultReferenceEntityReferenceResolverTest.java b/xwiki-platform-core/xwiki-platform-model/xwiki-platform-model-api/src/test/java/org/xwiki/model/internal/reference/DefaultReferenceEntityReferenceResolverTest.java index 9717c5dbdd91..befa78827424 100644 --- a/xwiki-platform-core/xwiki-platform-model/xwiki-platform-model-api/src/test/java/org/xwiki/model/internal/reference/DefaultReferenceEntityReferenceResolverTest.java +++ b/xwiki-platform-core/xwiki-platform-model/xwiki-platform-model-api/src/test/java/org/xwiki/model/internal/reference/DefaultReferenceEntityReferenceResolverTest.java @@ -19,7 +19,8 @@ */ package org.xwiki.model.internal.reference; -import java.util.Arrays; +import java.util.List; +import java.util.Map; import org.junit.jupiter.api.Test; import org.xwiki.model.EntityType; @@ -303,7 +304,7 @@ public void resolveDocumentReferenceWhenTypeIsPage() new EntityReference("page1", EntityType.PAGE, new EntityReference("page2", EntityType.PAGE)), EntityType.DOCUMENT); - assertEquals(new DocumentReference(DEFAULT_WIKI, Arrays.asList("page2", "page1"), DEFAULT_DOCUMENT), reference); + assertEquals(new DocumentReference(DEFAULT_WIKI, List.of("page2", "page1"), DEFAULT_DOCUMENT), reference); } @Test @@ -312,12 +313,27 @@ public void resolveSpaceReferenceWhenTypeIsPage() EntityReference reference = this.resolver.resolve(new EntityReference("page", EntityType.PAGE), EntityType.SPACE); - assertEquals(new SpaceReference(DEFAULT_WIKI, Arrays.asList("page")), reference); + assertEquals(new SpaceReference(DEFAULT_WIKI, List.of("page")), reference); reference = this.resolver.resolve( new EntityReference("page1", EntityType.PAGE, new EntityReference("page2", EntityType.PAGE)), EntityType.SPACE); - assertEquals(new SpaceReference(DEFAULT_WIKI, Arrays.asList("page2", "page1")), reference); + assertEquals(new SpaceReference(DEFAULT_WIKI, List.of("page2", "page1")), reference); + } + + @Test + void resolveRelativeEntityReference() + { + // When the space reference has a parent type parameter of tye space, then it's resolved using the base + // reference space has parent of it. + EntityReference relativeSpaceReference = new EntityReference("Space", EntityType.SPACE, + Map.of(EntityReference.PARENT_TYPE_PARAMETER, EntityType.SPACE)); + + EntityReference reference = this.resolver.resolve(relativeSpaceReference, EntityType.SPACE); + SpaceReference spaceReference = new SpaceReference(DEFAULT_WIKI, List.of(DEFAULT_SPACE, "Space")); + EntityReference expectedReference = + new EntityReference(spaceReference, Map.of(EntityReference.PARENT_TYPE_PARAMETER, EntityType.SPACE)); + assertEquals(expectedReference, reference); } }