public PropertyPattern<F, C, T> matching(Predicate<? super T> predicate) { return matching((t, context) -> predicate.test(t)); }
public static <F, C, T> Property<F, C, T> property(String name, BiFunction<F, C, T> function) { return optionalProperty(name, (source, context) -> Optional.of(function.apply(source, context))); }
public static Property<LateralJoinNode, Lookup, PlanNode> subquery() { return property("subquery", LateralJoinNode::getSubquery); } }
@Test public void propertyMatchers() { Pattern<String> aString = typeOf(String.class); Property<String, Integer> length = Property.property("length", String::length); String string = "a"; assertMatch(aString.with(length.equalTo(1)), string); assertMatch(project().with(source().matching(scan())), new ProjectNode(new ScanNode("T"))); assertMatch(aString.with(length.matching(any())), string); assertMatch(aString.with(length.matching(x -> x > 0)), string); assertMatch(aString.with(length.matching((Number x) -> x.intValue() > 0)), string); assertNoMatch(aString.with(length.equalTo(0)), string); assertNoMatch(project().with(source().matching(scan())), new ProjectNode(new ProjectNode(new ScanNode("T")))); assertNoMatch(aString.with(length.matching(typeOf(Void.class))), string); assertNoMatch(aString.with(length.matching(x -> x < 1)), string); assertNoMatch(aString.with(length.matching((Number x) -> x.intValue() < 1)), string); }
@Test public void optionalProperties() { Property<RelNode, RelNode> onlySource = Property.optionalProperty("onlySource", node -> Optional.of(node.getSources()) .filter(sources -> sources.size() == 1) .map((List<RelNode> sources) -> sources.get(0))); Pattern<RelNode> relNodeWithExactlyOneSource = plan() .with(onlySource.matching(any())); assertMatch(relNodeWithExactlyOneSource, new ProjectNode(new ScanNode("t"))); assertNoMatch(relNodeWithExactlyOneSource, new ScanNode("t")); assertNoMatch(relNodeWithExactlyOneSource, new JoinNode(new ScanNode("t"), new ScanNode("t"))); }
@Test public void capturingMatchesInATypesafeManner() { Capture<FilterNode> filter = newCapture(); Capture<ScanNode> scan = newCapture(); Capture<String> name = newCapture(); Pattern<ProjectNode> pattern = project() .with(source().matching(filter().capturedAs(filter) .with(source().matching(scan().capturedAs(scan) .with(tableName().capturedAs(name)))))); ProjectNode tree = new ProjectNode(new FilterNode(new ScanNode("orders"), null)); Match<ProjectNode> match = assertMatch(pattern, tree); //notice the concrete type despite no casts: FilterNode capturedFilter = match.capture(filter); assertEquals(tree.getSource(), capturedFilter); assertEquals(((FilterNode) tree.getSource()).getSource(), match.capture(scan)); assertEquals("orders", match.capture(name)); }
@Override public <C> Stream<Match> accept(Object object, Captures captures, C context) { //TODO remove cast BiFunction<? super T, C, Optional<?>> property = (BiFunction<? super T, C, Optional<?>>) propertyPattern.getProperty().getFunction(); Optional<?> propertyValue = property.apply((T) object, context); return propertyValue.map(value -> propertyPattern.getPattern().match(value, captures, context)) .orElse(Stream.of()); }
public static <F, C, T> Property<F, C, T> optionalProperty(String name, BiFunction<F, C, Optional<T>> function) { return new Property<>(name, function); }
@Override public void visitWith(WithPattern<?> pattern) { visitPrevious(pattern); appendLine("with(%s)", pattern.getProperty().getName()); level += 1; pattern.getPattern().accept(this); level -= 1; }
@Override public <T> Match<T> matchWith(WithPattern<T> withPattern, Object object, Captures captures) { Function<? super T, Optional<?>> property = withPattern.getProperty().getFunction(); Optional<?> propertyValue = property.apply((T) object); Match<?> propertyMatch = propertyValue .map(value -> match(withPattern.getPattern(), value, captures)) .orElse(Match.empty()); return propertyMatch.map(ignored -> (T) object); }
public static <F, T> Property<F, T> optionalProperty(String name, Function<F, Optional<T>> function) { return new Property<>(name, function); }
@Override public void visitWith(WithPattern<?> pattern) { visitPrevious(pattern); appendLine("with(%s)", pattern.getProperty().getName()); level += 1; pattern.getPattern().accept(this); level -= 1; }
public static Property<SampleNode, SampleNode.Type> sampleType() { return property("sampleType", SampleNode::getSampleType); } }
public static <F, C, T extends Iterable<S>, S> PropertyPattern<F, C, T> nonEmpty(Property<F, C, T> property) { return PropertyPattern.upcast(property.matching(not(Iterables::isEmpty))); }
public static <F, T> Property<F, T> property(String name, Function<F, T> function) { return optionalProperty(name, source -> Optional.of(function.apply(source))); }
@Override public <T> Match<T> matchWith(WithPattern<T> withPattern, Object object, Captures captures) { Function<? super T, Optional<?>> property = withPattern.getProperty().getFunction(); Optional<?> propertyValue = property.apply((T) object); Optional<?> resolvedValue = propertyValue .map(value -> value instanceof GroupReference ? lookup.resolve(((GroupReference) value)) : value); Match<?> propertyMatch = resolvedValue .map(value -> match(withPattern.getPattern(), value, captures)) .orElse(Match.empty()); return propertyMatch.map(ignored -> (T) object); } }
public static <F, C, T> Property<F, C, T> optionalProperty(String name, Function<F, Optional<T>> function) { return new Property<>(name, (source, context) -> function.apply(source)); }
public static Property<ExchangeNode, Lookup, ExchangeNode.Scope> scope() { return property("scope", ExchangeNode::getScope); } }
public PropertyPattern<F, C, T> matching(BiPredicate<? super T, ?> predicate) { return matching(new FilterPattern<>(predicate, Optional.empty())); } }
public static Property<PlanNode, PlanNode> source() { return optionalProperty("source", node -> node.getSources().size() == 1 ? Optional.of(node.getSources().get(0)) : Optional.empty()); }