Skip to content

Commit

Permalink
XWIKI-22702: Provide parentType information in EntityReference (#3698)
Browse files Browse the repository at this point in the history
  * Provide EntityReference#getParentType
  * Use it in AbstractReferenceEntityReferenceResolver
  * Provide a new test
  * add more explanatory tests
  * Improve a bit the checks when setting the fallbackParentType
    parameter value
  * use back parentType parameter key name

(cherry picked from commit c5b9c60)
  • Loading branch information
surli committed Dec 10, 2024
1 parent 5e68d1c commit 0093a3d
Show file tree
Hide file tree
Showing 8 changed files with 171 additions and 43 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -90,15 +90,16 @@ private EntityReference normalizeReference(EntityReference referenceToResolve, O
EntityReference reference = normalizedReference;
while (reference != null) {
List<EntityType> 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()) {
Expand All @@ -112,4 +113,9 @@ private EntityReference normalizeReference(EntityReference referenceToResolve, O

return normalizedReference;
}

private boolean isParentTypeAndAllowedTypeNotMatching(List<EntityType> allowedTypes, EntityType parentType)
{
return !allowedTypes.isEmpty() && parentType != null && !allowedTypes.contains(parentType);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,13 @@
*/
public class EntityReference implements Serializable, Cloneable, Comparable<EntityReference>
{
/**
* 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.
*/
Expand Down Expand Up @@ -118,7 +125,6 @@ protected EntityReference(EntityReference reference, EntityReference oldReferenc

setName(reference.name);
setType(reference.type);
setParameters(reference.parameters);
if (reference.parent == null) {
if (oldReference == null) {
setParent(newReference);
Expand All @@ -131,6 +137,7 @@ protected EntityReference(EntityReference reference, EntityReference oldReferenc
} else {
setParent(new EntityReference(reference.parent, oldReference, newReference));
}
setParameters(reference.parameters);
}

/**
Expand Down Expand Up @@ -191,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
Expand Down Expand Up @@ -298,15 +305,37 @@ protected void setParameter(String name, Serializable value)
if (this.parameters == null) {
this.parameters = new TreeMap<>();
}
this.parameters.put(name, value);
if (PARENT_TYPE_PARAMETER.equals(name)) {
setParentTypeParameter(value);
} else {
this.parameters.put(name, value);
}
} else if (parameters != null) {
this.parameters.remove(name);
if (this.parameters.size() == 0) {
if (this.parameters.isEmpty()) {
this.parameters = null;
}
}
}

private void setParentTypeParameter(Serializable value)
{
if (value != null && getParent() == null) {
EntityType parentType;
if (value instanceof EntityType entityType) {
parentType = entityType;
} else {
parentType = EntityType.valueOf(value.toString());
}
if (getType().getAllowedParents().contains(parentType)) {
this.parameters.put(PARENT_TYPE_PARAMETER, parentType);
} else {
throw new IllegalArgumentException(
"The parent type [" + parentType + "] does not belong to the allowed parents");
}
}
}

/**
* Get the value of a parameter. Return null if the parameter is not set. This method is final so there is no way to
* override the map, and the private field in all other methods of this implementation (faster).
Expand Down Expand Up @@ -681,6 +710,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 (getParent() == null) {
result = getParameter(PARENT_TYPE_PARAMETER);
if (result == null && !getType().getAllowedParents().isEmpty()) {
result = getType().getAllowedParents().get(0);
}
} else {
result = getParent().getType();
}
return result;
}

@Override
public int hashCode()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand All @@ -312,12 +313,25 @@ 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"));
assertEquals(spaceReference, reference);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,13 @@
@ComponentList({
DefaultSymbolScheme.class
})
public class RelativeStringEntityReferenceResolverTest
class RelativeStringEntityReferenceResolverTest
{
@InjectMockComponents
private RelativeStringEntityReferenceResolver resolver;

@Test
public void resolveDocumentReference()
void resolveDocumentReference()
{
EntityReference reference = this.resolver.resolve("", EntityType.DOCUMENT);
assertNull(reference);
Expand All @@ -62,7 +62,7 @@ public void resolveDocumentReference()
}

@Test
public void resolveDocumentReferenceWithBaseReference()
void resolveDocumentReferenceWithBaseReference()
{
EntityReference reference =
this.resolver.resolve("", EntityType.DOCUMENT, new EntityReference("space", EntityType.SPACE));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
package org.xwiki.model.internal.reference;

import java.util.Locale;
import java.util.Map;

import org.junit.jupiter.api.Test;
import org.xwiki.model.EntityType;
Expand Down Expand Up @@ -67,5 +68,11 @@ void serializeResolveDocument()
EntityReference resolved = this.resolver.resolve(serialized, EntityType.DOCUMENT);

assertEquals(documentReference, resolved);

EntityReference reference = resolver.resolve("wiki:space;param1=value2.page", EntityType.DOCUMENT);
assertEquals("wiki", reference.extractReference(EntityType.WIKI).getName());
assertEquals("space", reference.extractReference(EntityType.SPACE).getName());
assertEquals("page", reference.getName());
assertEquals(Map.of("param1", "value2"), reference.extractReference(EntityType.SPACE).getParameters());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@

import javax.inject.Inject;

import java.util.Map;

import org.junit.jupiter.api.Test;
import org.xwiki.model.EntityType;
import org.xwiki.model.reference.EntityReference;
Expand Down Expand Up @@ -57,7 +59,8 @@ void convertDocumentFromString()
new EntityReference("wiki", EntityType.WIKI)));
assertEquals(reference, this.converterManager.convert(EntityReference.class, "document:wiki:space.page"));

reference = new EntityReference("page", EntityType.DOCUMENT, new EntityReference("space", EntityType.SPACE));
reference = new EntityReference("page", EntityType.DOCUMENT,
new EntityReference("space", EntityType.SPACE));
assertEquals(reference, this.converterManager.convert(EntityReference.class, "document:space.page"));
assertEquals(reference, this.converterManager.convert(EntityReference.class, "space.page"));

Expand Down
Loading

0 comments on commit 0093a3d

Please sign in to comment.