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
33 changes: 24 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ The `DotPathQL` is the core component of this project that allows you to extract
- 📋 **Collection Handling**: Process Lists, Arrays, and other Collections
- 🗺️ **Map Support**: Handle both simple and complex Map structures
- 📝 **Record & POJO Support**: Works with Java Records and traditional classes
- 🔒 **Private Field Access**: Can access private fields when getters aren't available
- 🔒 **Private Field Access**: Can access private fields when getters aren't available (filtering only)
- 🚀 **Performance Optimized**: Efficient reflection-based property access

## Quick Start
Expand All @@ -38,10 +38,8 @@ The `DotPathQL` is the core component of this project that allows you to extract
### Filter Usage

```java
DotPathQL filterUtil = new DotPathQL();

// Filter specific properties from an object
Map<String, Object> result = filterUtil.filter(userObject, List.of(
Map<String, Object> result = new DotPathQL().filter(userObject, List.of(
"username",
"address.street",
"address.city"
Expand All @@ -51,10 +49,8 @@ Map<String, Object> result = filterUtil.filter(userObject, List.of(
### Exclude Usage

```java
DotPathQL filterUtil = new DotPathQL();

// Exclude specific properties and return everything else
Map<String, Object> result = filterUtil.exclude(userObject, List.of(
Map<String, Object> result = new DotPathQL().exclude(userObject, List.of(
"password",
"ssn",
"address.country"
Expand All @@ -63,7 +59,7 @@ Map<String, Object> result = filterUtil.exclude(userObject, List.of(

## Supported Data Structures

- Simple Properties
- Simple Properties (primitive and object types)
- Nested Objects
- Collections and Arrays
- Map Structures
Expand Down Expand Up @@ -96,7 +92,7 @@ The utility uses a multi-layered approach to access object properties:

1. **Record Components** (Most Efficient): For Java Records, uses the generated accessor methods
2. **Getter Methods**: Tries standard getter methods (`getName()`, `getAddress()`)
3. **Direct Field Access**: Falls back to direct field access for private fields
3. **Direct Field Access**: Falls back to direct field access for private fields (except for the exclude API)

### Nested Structure Processing

Expand Down Expand Up @@ -178,6 +174,25 @@ List<String> reportFields = List.of(
);
```

## Helper Utilities

You can also easy access the map result using the `DotPathQL` utility methods:

```java
// Step 1
var dotPathQL = new DotPathQL();
var result = dotPathQL.filter(userObject, List.of(
"address",
"friendList",
"games"
));

// Step 2: Accessing the result
var address = dotPathQL.mapFrom(result, "address");
var friendList = dotPathQL.listFrom(result, "friendList");
var games = dotPathQL.arrayFrom(result, "games");
```

## Technical Requirements

- **Java**: 17 or higher
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<groupId>io.github.trackerforce</groupId>
<artifactId>dot-path-ql</artifactId>
<version>1.0.1-SNAPSHOT</version>
<version>1.1.0-SNAPSHOT</version>

<name>dot-path-ql</name>
<description>dotPathQL allows object attribute filtering</description>
Expand Down
19 changes: 14 additions & 5 deletions src/main/java/io/github/trackerforce/DotPathQL.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
@SuppressWarnings("unchecked")
public class DotPathQL {

private final PathFilter pathFilter;
private final PathExclude pathExclude;
private final PathCommon pathFilter;
private final PathCommon pathExclude;

/**
* Constructs a DotPathQL instance with an empty list of default filter paths.
Expand All @@ -36,7 +36,7 @@ public DotPathQL() {
* @return a map containing the filtered properties
*/
public <T> Map<String, Object> filter(T source, List<String> filterPaths) {
return pathFilter.filter(source, filterPaths);
return pathFilter.run(source, filterPaths);
}

/**
Expand All @@ -51,7 +51,7 @@ public <T> Map<String, Object> filter(T source, List<String> filterPaths) {
* @return a map containing all properties except the excluded ones
*/
public <T> Map<String, Object> exclude(T source, List<String> excludePaths) {
return pathExclude.exclude(source, excludePaths);
return pathExclude.run(source, excludePaths);
}

/**
Expand Down Expand Up @@ -108,7 +108,16 @@ public Object[] arrayFrom(Map<String, Object> source, String property) {
* @param paths the list of default filter paths to add
*/
public void addDefaultFilterPaths(List<String> paths) {
pathFilter.addDefaultFilterPaths(paths);
pathFilter.addDefaultPaths(paths);
}

/**
* Adds default exclude paths that will be included in every exclusion operation.
*
* @param paths the list of default exclude paths to add
*/
public void addDefaultExcludePaths(List<String> paths) {
pathExclude.addDefaultPaths(paths);
}

private boolean isInvalid(Map<String, Object> source, String property) {
Expand Down
48 changes: 37 additions & 11 deletions src/main/java/io/github/trackerforce/PathCommon.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

/**
* Common functionality for handling paths in the DotPathQL library.
Expand All @@ -14,28 +16,52 @@
abstract class PathCommon {

/**
* Default filter paths that can be used across different implementations.
* This allows for easy configuration of common paths to filter.
* Default paths that can be used across different implementations.
*/
protected final List<String> defaultFilterPaths;
protected final List<String> defaultPaths;

/**
* Constructor to initialize the PathCommon with an empty list of default filter paths.
* Executes the path processing logic for the given source object.
*
* @param <T> the type of the source object
* @param source the source object to process
* @param filterPaths the list of paths to filter or exclude
* @return a map containing the processed properties
*/
abstract <T> Map<String, Object> execute(T source, List<String> filterPaths);

/**
* Runs the path processing logic for the given source object with the specified paths.
*
* @param <T> the type of the source object
* @param source the source object to process
* @param excludePaths the list of paths to exclude
* @return a map containing the processed properties
*/
<T> Map<String, Object> run(T source, List<String> excludePaths) {
if (source == null) {
return Collections.emptyMap();
}

return execute(source, expandGroupedPaths(excludePaths));
}

/**
* Constructor to initialize the PathCommon with an empty list of default paths.
* This allows subclasses to add their own default paths as needed.
*/
protected PathCommon() {
this.defaultFilterPaths = new ArrayList<>();
this.defaultPaths = new ArrayList<>();
}

/**
* Adds default filter paths to the list of paths that can be used
* when filtering objects. This allows for easy configuration of common
* paths that should always be available for filtering.
* Adds default paths to the list of paths that can be used
* when processing objects.
*
* @param paths the list of paths to add as default filter paths
* @param paths the list of paths to add as default paths
*/
public void addDefaultFilterPaths(List<String> paths) {
defaultFilterPaths.addAll(paths);
public void addDefaultPaths(List<String> paths) {
defaultPaths.addAll(paths);
}

/**
Expand Down
10 changes: 4 additions & 6 deletions src/main/java/io/github/trackerforce/PathExclude.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,11 @@ private enum SkipValue {
INSTANCE
}

public <T> Map<String, Object> exclude(T source, List<String> excludePaths) {
if (source == null) {
return Collections.emptyMap();
}

ExclusionNode root = buildExclusionTree(expandGroupedPaths(excludePaths));
public <T> Map<String, Object> execute(T source, List<String> excludePaths) {
Map<String, Object> result = new LinkedHashMap<>();
excludePaths.addAll(0, defaultPaths);

ExclusionNode root = buildExclusionTree(excludePaths);
buildExcluding(result, source, "", root);
return result;
}
Expand Down
11 changes: 3 additions & 8 deletions src/main/java/io/github/trackerforce/PathFilter.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,11 @@
@SuppressWarnings("unchecked")
class PathFilter extends PathCommon {

public <T> Map<String, Object> filter(T source, List<String> filterPaths) {
if (source == null) {
return Collections.emptyMap();
}

public <T> Map<String, Object> execute(T source, List<String> filterPaths) {
Map<String, Object> result = new LinkedHashMap<>();
List<String> expandedPaths = expandGroupedPaths(filterPaths);
expandedPaths.addAll(0, defaultFilterPaths);
filterPaths.addAll(0, defaultPaths);

for (String path : expandedPaths) {
for (String path : filterPaths) {
addPathToResult(result, source, path);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,21 @@ void shouldExcludeGroupedPaths(String implementation, Object userDetail) {
assertFalse(work.containsKey("city"));
}

@ParameterizedTest(name = "{0}")
@MethodSource("userDetailProvider")
void shouldAddDefaultExclusionPaths(String implementation, Object userDetail) {
// When
dotPathQL.addDefaultExcludePaths(List.of("username"));
var result = dotPathQL.exclude(userDetail, List.of("address.city"));

// Then
assertFalse(result.containsKey("username"));
var address = dotPathQL.mapFrom(result, "address");
assertNotNull(address);
assertFalse(address.containsKey("city"));
assertTrue(address.containsKey("street"));
}

@Test
void shouldReturnEmptyMapWhenSourceIsNull() {
// When
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,13 +126,9 @@ void shouldReturnFilteredObjectWithComplexMap(String implementation, Object user
@ParameterizedTest(name = "{0}")
@MethodSource("userDetailProvider")
void shouldAddDefaultFilterPaths(String implementation, Object userDetail) {
// Given
var defaultPaths = List.of("username");
var filterPaths = List.of("address.city");

// When
dotPathQL.addDefaultFilterPaths(defaultPaths);
var result = dotPathQL.filter(userDetail, filterPaths);
dotPathQL.addDefaultFilterPaths(List.of("username"));
var result = dotPathQL.filter(userDetail, List.of("address.city"));

// Then
assertEquals(2, result.size());
Expand Down
Loading