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
@@ -0,0 +1,57 @@
package io.cloudquery.helper;

import java.lang.reflect.Field;

/**
* A simple reflection-based path resolver.
*/
public class ReflectionPathResolver {

public static class PathResolverException extends Exception {
public PathResolverException(String message, Throwable ex) {
super(message, ex);
}
}

/**
* Resolve a path of an object using reflection.
* <p>
* e.g. if we have a class:
* <pre>
* class TestClass {
* private String name;
* private TestClass child;
* }
* </pre>
* <p>
* Then the following are valid paths to retrieve the associated values:
* <p>
* `name`
* `child.name`
* </p>
* NOTE: this implementation is currently very simplistic and only supports simple field and nested field resolution.
* It does not support collection resolution - unlike the Go SDK which uses <a href="https://github.com/thoas/go-funk#get">go-funk</a>.
*
* @param object The object to resolve the path on
* @param path The path to resolve
* @return The value of the property at the resolved path
* @throws PathResolverException If the path cannot be resolved
*/
public static Object resolve(Object object, String path) throws PathResolverException {
Object current = object;

for (String currentPath : path.split("\\.")) {
try {
Field currentField = object.getClass().getDeclaredField(currentPath);
if (!currentField.canAccess(current)) {
currentField.setAccessible(true);
}
object = currentField.get(object);
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new PathResolverException("Unable to resolve path " + currentPath, e);
}
}

return object;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package io.cloudquery.helper;

import lombok.Builder;
import org.junit.jupiter.api.Test;

import java.util.List;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;

class ReflectionPathResolverTest {
@Builder
private static class TestClass {
private String name;

@Builder.Default
private List<Integer> numbers = List.of(1, 2, 3);

private TestClass singleChild;

private List<TestClass> multipleChildren;
}

private static final TestClass TEST_DATA = TestClass.builder().name("root").
singleChild(TestClass.builder().name("single-child1").build()).
multipleChildren(
List.of(
TestClass.builder().name("multi-child1").build(),
TestClass.builder().name("multi-child2").build()
)
).
build();

@Test
public void shouldResolveSimpleFields() throws ReflectionPathResolver.PathResolverException {
assertEquals("root", ReflectionPathResolver.resolve(TEST_DATA, "name"));
assertEquals(List.of(1, 2, 3), ReflectionPathResolver.resolve(TEST_DATA, "numbers"));
}

@Test
public void shouldResolveNestedField() throws ReflectionPathResolver.PathResolverException {
assertEquals("single-child1", ReflectionPathResolver.resolve(TEST_DATA, "singleChild.name"));
}

@Test
public void shouldThrowAnErrorIfWeEncounterACollection() {
assertThrows(ReflectionPathResolver.PathResolverException.class, () -> ReflectionPathResolver.resolve(TEST_DATA, "multiplChildren.name"));
}
}