diff --git a/core/src/main/java/com/turkraft/springfilter/converter/FilterStringConverter.java b/core/src/main/java/com/turkraft/springfilter/converter/FilterStringConverter.java index 9edb6e59..9079e3d1 100644 --- a/core/src/main/java/com/turkraft/springfilter/converter/FilterStringConverter.java +++ b/core/src/main/java/com/turkraft/springfilter/converter/FilterStringConverter.java @@ -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); diff --git a/core/src/main/java/com/turkraft/springfilter/converter/FilterStringConverterImpl.java b/core/src/main/java/com/turkraft/springfilter/converter/FilterStringConverterImpl.java index a8e12e33..b13c8fb4 100644 --- a/core/src/main/java/com/turkraft/springfilter/converter/FilterStringConverterImpl.java +++ b/core/src/main/java/com/turkraft/springfilter/converter/FilterStringConverterImpl.java @@ -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 @@ -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 diff --git a/core/src/main/java/com/turkraft/springfilter/parser/AntlrParser.java b/core/src/main/java/com/turkraft/springfilter/parser/AntlrParser.java index 595138a8..0b3e0149 100644 --- a/core/src/main/java/com/turkraft/springfilter/parser/AntlrParser.java +++ b/core/src/main/java/com/turkraft/springfilter/parser/AntlrParser.java @@ -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 @@ -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); } diff --git a/core/src/main/java/com/turkraft/springfilter/parser/FilterParser.java b/core/src/main/java/com/turkraft/springfilter/parser/FilterParser.java index ee694aa7..163aac22 100644 --- a/core/src/main/java/com/turkraft/springfilter/parser/FilterParser.java +++ b/core/src/main/java/com/turkraft/springfilter/parser/FilterParser.java @@ -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); + } } diff --git a/core/src/main/java/com/turkraft/springfilter/parser/FilterParserImpl.java b/core/src/main/java/com/turkraft/springfilter/parser/FilterParserImpl.java index 92dd73c0..3ae8cd56 100644 --- a/core/src/main/java/com/turkraft/springfilter/parser/FilterParserImpl.java +++ b/core/src/main/java/com/turkraft/springfilter/parser/FilterParserImpl.java @@ -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 @@ -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), @@ -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); } diff --git a/core/src/main/java/com/turkraft/springfilter/parser/ParseContext.java b/core/src/main/java/com/turkraft/springfilter/parser/ParseContext.java new file mode 100644 index 00000000..34f087ba --- /dev/null +++ b/core/src/main/java/com/turkraft/springfilter/parser/ParseContext.java @@ -0,0 +1,11 @@ +package com.turkraft.springfilter.parser; + +import java.util.function.UnaryOperator; + +public interface ParseContext { + + default UnaryOperator getFieldMapper() { + return UnaryOperator.identity(); + } + +} diff --git a/core/src/main/java/com/turkraft/springfilter/parser/ParseContextImpl.java b/core/src/main/java/com/turkraft/springfilter/parser/ParseContextImpl.java new file mode 100644 index 00000000..6ad6892c --- /dev/null +++ b/core/src/main/java/com/turkraft/springfilter/parser/ParseContextImpl.java @@ -0,0 +1,18 @@ +package com.turkraft.springfilter.parser; + +import java.util.function.UnaryOperator; + +public class ParseContextImpl implements ParseContext { + + private final UnaryOperator fieldMapper; + + public ParseContextImpl(UnaryOperator fieldMapper) { + this.fieldMapper = fieldMapper; + } + + @Override + public UnaryOperator getFieldMapper() { + return fieldMapper; + } + +} diff --git a/core/src/test/java/com/turkraft/springfilter/FieldMapperTest.java b/core/src/test/java/com/turkraft/springfilter/FieldMapperTest.java new file mode 100644 index 00000000..f46ba707 --- /dev/null +++ b/core/src/test/java/com/turkraft/springfilter/FieldMapperTest.java @@ -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)); + + } + +} diff --git a/core/src/test/java/com/turkraft/springfilter/FilterBuilderTest.java b/core/src/test/java/com/turkraft/springfilter/FilterBuilderTest.java index 8ef6f008..3438c009 100644 --- a/core/src/test/java/com/turkraft/springfilter/FilterBuilderTest.java +++ b/core/src/test/java/com/turkraft/springfilter/FilterBuilderTest.java @@ -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()));