Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,20 @@

import java.io.Serial;
import java.io.Serializable;
import java.lang.invoke.MethodHandles;
import java.util.Iterator;

import org.hibernate.validator.path.Path;
import org.hibernate.validator.internal.util.logging.Log;
import org.hibernate.validator.internal.util.logging.LoggerFactory;
import org.hibernate.validator.path.RandomAccessPath;


final class MaterializedPath implements Path, Serializable {
final class MaterializedPath implements RandomAccessPath, Serializable {

private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() );

@Serial
private static final long serialVersionUID = 1264131890253015968L;
private static final long serialVersionUID = -329465327521818082L;

private static final String PROPERTY_PATH_SEPARATOR = ".";

Expand All @@ -26,6 +31,29 @@ final class MaterializedPath implements Path, Serializable {
this.leafNode = nodes[nodes.length - 1];
}

@Override
public Node getLeafNode() {
return leafNode;
}

@Override
public Node getRootNode() {
return nodes[0];
}

@Override
public Node getNode(int index) {
if ( index < 0 || index >= nodes.length ) {
throw LOG.pathIndexOutOfBounds( index, nodes.length );
}
return nodes[index];
}

@Override
public int length() {
return nodes.length;
}

@Override
public Iterator<Node> iterator() {
return new MaterializedNode.NodeIterator( nodes );
Expand Down Expand Up @@ -72,9 +100,4 @@ static String asString(MaterializedNode currentLeafNode) {
}
return builder.toString();
}

@Override
public Node getLeafNode() {
return leafNode;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -959,4 +959,7 @@ ConstraintDefinitionException getConstraintValidatorDefinitionConstraintMismatch
@Message(id = 272,
value = "Using `@Valid` on a container is deprecated. You should apply the annotation on the type argument(s). (%1$s) can potentially be a container at runtime. Affected element: %2$s")
void potentiallyDeprecatedUseOfValidOnContainer(@FormatWith(ClassObjectFormatter.class) Class<?> valueType, Object context);

@Message(id = 273, value = "Index %1$d is out of bounds for the path of length %2$d.")
IndexOutOfBoundsException pathIndexOutOfBounds(int index, int length);
}
2 changes: 2 additions & 0 deletions engine/src/main/java/org/hibernate/validator/path/Path.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

/**
* An extended representation of the validation path, provides Hibernate Validator specific functionality.
*
* @since 9.1
*/
@Incubating
public interface Path extends jakarta.validation.Path {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* SPDX-License-Identifier: Apache-2.0
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.validator.path;

import java.util.RandomAccess;

import org.hibernate.validator.Incubating;

/**
* An extended representation of the validation path, provides Hibernate Validator specific functionality.
* Represents a path with access to the nodes by their index.
*
* @since 9.1
*/
@Incubating
public interface RandomAccessPath extends Path, RandomAccess {

/**
* @return The first node in the path, i.e. {@code path.iterator().next()}.
*/
jakarta.validation.Path.Node getRootNode();

/**
* @param index The index of the node to return.
* @return The node in the path for a given index.
* @throws IndexOutOfBoundsException if the index is out of range, i.e. {@code index < 0 || index >= length() }
*/
jakarta.validation.Path.Node getNode(int index);

/**
* @return The length of the path, i.e. the number of nodes this path contains.
*/
int length();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* SPDX-License-Identifier: Apache-2.0
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.validator.test.internal.engine.path;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

import jakarta.validation.Path;

import org.hibernate.validator.internal.engine.path.MutablePath;
import org.hibernate.validator.path.RandomAccessPath;

import org.assertj.core.api.Assertions;
import org.testng.annotations.Test;

public class RandomAccessPathTest {

@Test
public void testParsing() {
String property = "orders[3].deliveryAddress.addressline[1]";
Path path = MutablePath.createPathFromString( property ).materialize();
assertThat( path ).isInstanceOf( RandomAccessPath.class );

if ( path instanceof RandomAccessPath randomAccessPath ) {
assertThat( randomAccessPath.length() ).isEqualTo( 4 );

// Get the root node and assert its properties
Path.Node elem = randomAccessPath.getRootNode();
assertThat( elem ).isNotNull();
assertThat( elem.getName() ).isEqualTo( "orders" );
assertThat( elem.isInIterable() ).isFalse();

// Get the node by index 0 and assert its properties
elem = randomAccessPath.getNode( 0 );
assertThat( elem ).isNotNull();
assertThat( elem.getName() ).isEqualTo( "orders" );
assertThat( elem.isInIterable() ).isFalse();

// Get the node by index 1 and assert its properties
elem = randomAccessPath.getNode( 1 );
assertThat( elem ).isNotNull();
assertThat( elem.getName() ).isEqualTo( "deliveryAddress" );
assertThat( elem.isInIterable() ).isTrue();
assertThat( elem.getIndex() ).isEqualTo( 3 );

// Get the node by index 2 and assert its properties
elem = randomAccessPath.getNode( 2 );
assertThat( elem ).isNotNull();
assertThat( elem.getName() ).isEqualTo( "addressline" );
assertThat( elem.isInIterable() ).isFalse();

// Get the node by index 3 and assert its properties
elem = randomAccessPath.getNode( 3 );
assertThat( elem ).isNotNull();
assertThat( elem.getName() ).isNull();
assertThat( elem.isInIterable() ).isTrue();
assertThat( elem.getIndex() ).isEqualTo( 1 );

assertThatThrownBy( () -> randomAccessPath.getNode( 4 ) )
.isInstanceOf( IndexOutOfBoundsException.class );

assertThat( path.toString() ).isEqualTo( property );
}
else {
Assertions.fail( "Path is not IndexedPath" );
}
}
}
Loading