Skip to content

Commit

Permalink
ParseContext + FieldMapper
Browse files Browse the repository at this point in the history
  • Loading branch information
torshid committed Jun 13, 2023
1 parent 154183a commit c3c728c
Show file tree
Hide file tree
Showing 9 changed files with 136 additions and 50 deletions.
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
package com.turkraft.springfilter.converter;

import com.turkraft.springfilter.parser.ParseContext;
import com.turkraft.springfilter.parser.node.FilterNode;
import org.springframework.core.convert.converter.GenericConverter;
import org.springframework.lang.Nullable;

public interface FilterStringConverter extends GenericConverter {

FilterNode convert(String source);
FilterNode convert(String source, @Nullable ParseContext ctx);

default FilterNode convert(String source) {
return convert(source, null);
}

String convert(FilterNode source);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package com.turkraft.springfilter.converter;

import com.turkraft.springfilter.parser.FilterParser;
import com.turkraft.springfilter.parser.ParseContext;
import com.turkraft.springfilter.parser.node.FilterNode;
import com.turkraft.springfilter.transformer.FilterStringTransformer;
import java.util.Set;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Service;

@Service
Expand Down Expand Up @@ -38,8 +40,8 @@ public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor t
}

@Override
public FilterNode convert(String node) {
return filterParser.parse(node);
public FilterNode convert(String node, @Nullable ParseContext ctx) {
return filterParser.parse(node, ctx);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import java.util.stream.Collectors;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.tree.TerminalNode;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Service;

@Service
Expand All @@ -40,100 +41,101 @@ public AntlrParser(FilterOperators operators, FilterPlaceholders placeholders,
this.functions = functions;
}

public FilterNode parse(AntlrBaseContext ctx) {
public FilterNode parse(AntlrBaseContext antlrCtx, @Nullable ParseContext ctx) {

if (ctx instanceof FilterContext) {
return parse(((FilterContext) ctx).expression());
if (antlrCtx instanceof FilterContext) {
return parse(((FilterContext) antlrCtx).expression(), ctx);
}

if (ctx instanceof PriorityContext) {
return new PriorityNode(parse(((PriorityContext) ctx).expression()));
if (antlrCtx instanceof PriorityContext) {
return new PriorityNode(parse(((PriorityContext) antlrCtx).expression(), ctx));
}

if (ctx instanceof InputContext) {
String text = ctx.getText().startsWith("'") && ctx.getText().endsWith("'")
? ctx.getText().substring(1, ctx.getText().length() - 1)
: ctx.getText();
if (antlrCtx instanceof InputContext) {
String text = antlrCtx.getText().startsWith("'") && antlrCtx.getText().endsWith("'")
? antlrCtx.getText().substring(1, antlrCtx.getText().length() - 1)
: antlrCtx.getText();
return new InputNode(text.replace("\\'", "'"));
}

if (ctx instanceof FieldContext) {
return new FieldNode(ctx.getText());
if (antlrCtx instanceof FieldContext) {
return new FieldNode(
ctx != null ? ctx.getFieldMapper().apply(antlrCtx.getText()) : antlrCtx.getText());
}

if (ctx instanceof PlaceholderContext) {
if (antlrCtx instanceof PlaceholderContext) {
return new PlaceholderNode(
placeholders.getPlaceholder(
ctx.getText().substring(1, ctx.getText().length() - 1)));
antlrCtx.getText().substring(1, antlrCtx.getText().length() - 1)));
}

if (ctx instanceof CollectionContext) {
if (antlrCtx instanceof CollectionContext) {
return new CollectionNode(
((CollectionContext) ctx).items.stream().map(this::parse)
((CollectionContext) antlrCtx).items.stream().map(antlrCtx1 -> parse(antlrCtx1, ctx))
.collect(Collectors.toList()));
}

if (ctx instanceof FunctionContext) {
if (antlrCtx instanceof FunctionContext) {
return new FunctionNode(
functions.getFunction(((FunctionContext) ctx).ID().getText()),
((FunctionContext) ctx).arguments.stream().map(this::parse)
functions.getFunction(((FunctionContext) antlrCtx).ID().getText()),
((FunctionContext) antlrCtx).arguments.stream().map(antlrCtx1 -> parse(antlrCtx1, ctx))
.collect(Collectors.toList()));
}

if (ctx instanceof PrefixExpressionContext) {
return operators.getPrefixOperator(ctx.getChild(0).getText())
.toNode(parse((AntlrBaseContext) ctx.getChild(1)));
if (antlrCtx instanceof PrefixExpressionContext) {
return operators.getPrefixOperator(antlrCtx.getChild(0).getText())
.toNode(parse((AntlrBaseContext) antlrCtx.getChild(1), ctx));
}

if (ctx instanceof ExpressionContext) {
if (antlrCtx instanceof ExpressionContext) {

if (ctx.getChildCount() == 1) {
return parse((AntlrBaseContext) ctx.getChild(0));
} else if (ctx.getChildCount() == 2) {
if (antlrCtx.getChildCount() == 1) {
return parse((AntlrBaseContext) antlrCtx.getChild(0), ctx);
} else if (antlrCtx.getChildCount() == 2) {

if (ctx.getChild(0) instanceof TerminalNode) {
return operators.getPrefixOperator(ctx.getChild(0).getText())
.toNode(parse((AntlrBaseContext) ctx.getChild(1)));
if (antlrCtx.getChild(0) instanceof TerminalNode) {
return operators.getPrefixOperator(antlrCtx.getChild(0).getText())
.toNode(parse((AntlrBaseContext) antlrCtx.getChild(1), ctx));
}

return operators.getPostfixOperator(ctx.getChild(1).getText())
.toNode(parse((AntlrBaseContext) ctx.getChild(0)));
return operators.getPostfixOperator(antlrCtx.getChild(1).getText())
.toNode(parse((AntlrBaseContext) antlrCtx.getChild(0), ctx));

} else {

int lowestPriorityIndex = -1;
int lowestPriorityValue = Integer.MAX_VALUE;

for (int i = 0; i < ctx.getChildCount(); i++) {
if (ctx.getChild(i) instanceof ExpressionContext) {
if (((ExpressionContext) ctx.getChild(i))._p <= lowestPriorityValue) {
lowestPriorityValue = ((ExpressionContext) ctx.getChild(i))._p;
for (int i = 0; i < antlrCtx.getChildCount(); i++) {
if (antlrCtx.getChild(i) instanceof ExpressionContext) {
if (((ExpressionContext) antlrCtx.getChild(i))._p <= lowestPriorityValue) {
lowestPriorityValue = ((ExpressionContext) antlrCtx.getChild(i))._p;
lowestPriorityIndex = i;
}
}
}

ExpressionContext subCtx = new ExpressionContext(ctx, 0);
ExpressionContext subCtx = new ExpressionContext(antlrCtx, 0);

for (int i = 0; i < lowestPriorityIndex - 1; i++) {

if (ctx.getChild(i) instanceof TerminalNode) {
subCtx.addChild((TerminalNode) ctx.getChild(i));
} else if (ctx.getChild(i) instanceof ParserRuleContext) {
subCtx.addChild((ParserRuleContext) ctx.getChild(i));
if (antlrCtx.getChild(i) instanceof TerminalNode) {
subCtx.addChild((TerminalNode) antlrCtx.getChild(i));
} else if (antlrCtx.getChild(i) instanceof ParserRuleContext) {
subCtx.addChild((ParserRuleContext) antlrCtx.getChild(i));
}

}

return operators.getInfixOperator(ctx.getChild(lowestPriorityIndex - 1).getText())
.toNode(parse(subCtx),
parse((AntlrBaseContext) ctx.getChild(lowestPriorityIndex)));
return operators.getInfixOperator(antlrCtx.getChild(lowestPriorityIndex - 1).getText())
.toNode(parse(subCtx, ctx),
parse((AntlrBaseContext) antlrCtx.getChild(lowestPriorityIndex), ctx));

}

}

throw new UnsupportedOperationException("Unsupported context " + ctx);
throw new UnsupportedOperationException("Unsupported context " + antlrCtx);

}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
package com.turkraft.springfilter.parser;

import com.turkraft.springfilter.parser.node.FilterNode;
import org.springframework.lang.Nullable;

public interface FilterParser {

FilterNode parse(String input);
FilterNode parse(String input, @Nullable ParseContext ctx);

default FilterNode parse(String input) {
return parse(input, null);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Service;

@Service
Expand All @@ -20,7 +21,7 @@ public FilterParserImpl(FilterOperators operators) {
}

@Override
public FilterNode parse(String input) {
public FilterNode parse(String input, @Nullable ParseContext ctx) {

com.turkraft.springfilter.parser.AntlrFilterLexer lexer = new com.turkraft.springfilter.parser.AntlrFilterLexer(
CharStreams.fromString(input),
Expand All @@ -34,7 +35,7 @@ public FilterNode parse(String input) {
parser.removeErrorListeners();
parser.addErrorListener(ThrowingErrorListener.INSTANCE);

return antlrParser.parse(parser.filter());
return antlrParser.parse(parser.filter(), ctx);

}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.turkraft.springfilter.parser;

import java.util.function.UnaryOperator;

public interface ParseContext {

default UnaryOperator<String> getFieldMapper() {
return UnaryOperator.identity();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.turkraft.springfilter.parser;

import java.util.function.UnaryOperator;

public class ParseContextImpl implements ParseContext {

private final UnaryOperator<String> fieldMapper;

public ParseContextImpl(UnaryOperator<String> fieldMapper) {
this.fieldMapper = fieldMapper;
}

@Override
public UnaryOperator<String> getFieldMapper() {
return fieldMapper;
}

}
41 changes: 41 additions & 0 deletions core/src/test/java/com/turkraft/springfilter/FieldMapperTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.turkraft.springfilter;

import com.turkraft.springfilter.converter.FilterStringConverter;
import com.turkraft.springfilter.parser.FilterParser;
import com.turkraft.springfilter.parser.ParseContextImpl;
import com.turkraft.springfilter.parser.node.FilterNode;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.junit.jupiter.SpringExtension;

@ExtendWith(SpringExtension.class)
public class FieldMapperTest {

@Configuration
@ComponentScan("com.turkraft.springfilter")
static class Config {

}

@Autowired
private FilterParser filterParser;

@Autowired
private FilterStringConverter filterStringConverter;

@Test
void test() {

FilterNode node = filterParser.parse("x : y : z", new ParseContextImpl(
(String field) -> field.equalsIgnoreCase("x") ? "a"
: field.equalsIgnoreCase("y") ? "b" : field));

Assertions.assertEquals("a : b : z", filterStringConverter.convert(node));

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ static class Config {
private HelloWorldPlaceholder helloWorldPlaceholder;

@Autowired
FilterStringConverter filterStringConverter;
private FilterStringConverter filterStringConverter;

private void test(String expected, StepWithResult filter) {
Assertions.assertEquals(expected, filterStringConverter.convert(filter.get()));
Expand Down

0 comments on commit c3c728c

Please sign in to comment.