Skip to content

Commit

Permalink
Add support of path aliases
Browse files Browse the repository at this point in the history
  • Loading branch information
m-dzianishchyts committed Feb 11, 2024
1 parent 1fead33 commit ab3bfcf
Show file tree
Hide file tree
Showing 6 changed files with 121 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,7 @@ public Expression visit(ExpNamedParameter node, Expression data) {
@Override
public Expression visit(ExpPath node, Expression parent) {
ASTPath path = PathOps.parsePath((String) node.jjtGetValue());
path.setPathAliases(node.getPathAliases());
return process(node, parent, path);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,14 @@ public void parseNamedParams() {
assertEquals(ExpressionFactory.exp("a = 'x'"), e);
}

@Test
public void parsePathAliases() {
Expression e = parser.parse(Exp.parse("a.b.c#p1.d#p2"));
Expression expected = ExpressionFactory.exp("a.b.c#p1.d#p2");
assertEquals(expected, e);
assertEquals(expected.getPathAliases(), e.getPathAliases());
}

@Test
public void parsePositionalParams() {
Expression e = parser.parse(Exp.parse("a = $a").positionalParams("x"));
Expand Down
35 changes: 35 additions & 0 deletions agrest-engine/src/main/java/io/agrest/exp/parser/ExpPath.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,16 @@
/* JavaCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=false,NODE_PREFIX=Exp,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */
package io.agrest.exp.parser;

import io.agrest.exp.AgExpressionException;

import java.util.Collections;
import java.util.Map;

public
class ExpPath extends SimpleNode {

protected Map<String, String> pathAliases = Collections.emptyMap();

public ExpPath(int id) {
super(id);
}
Expand All @@ -16,6 +24,11 @@ public ExpPath() {
super(AgExpressionParserTreeConstants.JJTPATH);
}

public ExpPath(String path) {
this();
setPath(path);
}

public String getPath() {
return (String)jjtGetValue();
}
Expand All @@ -24,13 +37,35 @@ public void setPath(String path) {
jjtSetValue(path);
}

public Map<String, String> getPathAliases() {
return pathAliases;
}

public void setPathAliases(Map<String, String> pathAliases) {
this.pathAliases = pathAliases;
}

@Override
public void jjtSetValue(Object value) {
super.jjtSetValue(value);
syncAliases();
}

/** Accept the visitor. **/
public <T> T jjtAccept(AgExpressionParserVisitor<T> visitor, T data) {

return
visitor.visit(this, data);
}

protected void syncAliases() {
try {
ParsingUtils.processPathAliases(this);
} catch (ParseException e) {
throw new AgExpressionException(e);
}
}

@Override
protected ExpPath shallowCopy() {
ExpPath copy = new ExpPath(id);
Expand Down
36 changes: 36 additions & 0 deletions agrest-engine/src/main/java/io/agrest/exp/parser/ParsingUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package io.agrest.exp.parser;

import java.util.HashMap;
import java.util.Map;

public final class ParsingUtils {

private ParsingUtils() {
}

static void processPathAliases(ExpPath pathExp) throws ParseException {
String path = pathExp.getPath();
if (!path.contains("#")) {
return;
}

String[] pathSegments = path.split("\\.");
Map<String, String> aliasMap = new HashMap<>();
for (int i = 0; i < pathSegments.length; i++) {
if (pathSegments[i].contains("#")) {
String[] splitSegment = pathSegments[i].split("#");
if (splitSegment[1].endsWith("+")) {
splitSegment[0] += '+';
splitSegment[1] = splitSegment[1].substring(0, splitSegment[1].length() - 1);
}
String previousAlias = aliasMap.putIfAbsent(splitSegment[1], splitSegment[0]);
if (previousAlias != null && !previousAlias.equals(splitSegment[0])) {
throw new ParseException("Can't add the same alias to different path segments.");
}
pathSegments[i] = splitSegment[1];
}
}
pathExp.setPath(String.join(".", pathSegments));
pathExp.setPathAliases(aliasMap);
}
}
4 changes: 1 addition & 3 deletions agrest-engine/src/main/java/io/agrest/protocol/Exp.java
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,7 @@ static Exp withPositionalParams(String template, Object... params) {
* @since 5.0
*/
static Exp path(String path) {
ExpPath pathExp = new ExpPath();
pathExp.jjtSetValue(Objects.requireNonNull(path));
return pathExp;
return new ExpPath(Objects.requireNonNull(path));
}

/**
Expand Down
40 changes: 40 additions & 0 deletions agrest-engine/src/test/java/io/agrest/exp/parser/ExpPathTest.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
package io.agrest.exp.parser;

import io.agrest.AgException;
import io.agrest.exp.AgExpressionException;
import io.agrest.protocol.Exp;
import org.junit.jupiter.api.extension.ParameterContext;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.aggregator.AggregateWith;
import org.junit.jupiter.params.aggregator.ArgumentsAccessor;
import org.junit.jupiter.params.aggregator.ArgumentsAggregator;
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.ValueSource;

Expand Down Expand Up @@ -63,6 +68,23 @@ public void parsedToString(String expString, String expected) {
assertEquals(expected, Exp.parse(expString).toString());
}

@ParameterizedTest
@CsvSource(delimiter = '|', value = {
"a.b.c.d|a.b.c.d",
"a.b+.c+.d|a.b+.c+.d",
"a.b.c#p1.d#p2|a.b.p1.p2|p1-c|p2-d",
"a.b+.c#p1+.d#p2|a.b+.p1.p2|p1-c+|p2-d",
})
public void pathAliases(String expString, String expectedPath, @AggregateWith(VarargsAggregator.class) String... expectedAliases) {
ExpPath expPath = new ExpPath(expString);
assertEquals(expectedPath, expPath.getPath());
assertEquals(expectedAliases.length, expPath.getPathAliases().size());
for (String expectedAlias : expectedAliases) {
String[] aliasMapping = expectedAlias.split("-");
assertEquals(aliasMapping[1], expPath.getPathAliases().get(aliasMapping[0]));
}
}

@ParameterizedTest
@ValueSource(strings = {
"0a",
Expand All @@ -78,4 +100,22 @@ public void parsedToString(String expString, String expected) {
public void parseInvalidGrammar(String expString) {
assertThrows(AgException.class, () -> Exp.parse(expString));
}

@ParameterizedTest
@ValueSource(strings = {
"a.b.c#p1.d#p1"
})
public void parseInvalidAliases(String expString) {
assertThrows(AgExpressionException.class, () -> Exp.path(expString));
}

static class VarargsAggregator implements ArgumentsAggregator {
@Override
public Object aggregateArguments(ArgumentsAccessor accessor, ParameterContext context) {
return accessor.toList().stream()
.skip(context.getIndex())
.map(String::valueOf)
.toArray(String[]::new);
}
}
}

0 comments on commit ab3bfcf

Please sign in to comment.