1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.mybatis.dynamic.sql.common;
17
18 import static org.mybatis.dynamic.sql.util.StringUtilities.spaceAfter;
19
20 import java.util.Objects;
21 import java.util.Optional;
22 import java.util.stream.Collectors;
23
24 import org.jspecify.annotations.Nullable;
25 import org.mybatis.dynamic.sql.SqlCriterion;
26 import org.mybatis.dynamic.sql.render.RenderingContext;
27 import org.mybatis.dynamic.sql.util.FragmentAndParameters;
28 import org.mybatis.dynamic.sql.util.FragmentCollector;
29 import org.mybatis.dynamic.sql.where.render.CriterionRenderer;
30 import org.mybatis.dynamic.sql.where.render.RenderedCriterion;
31
32 public abstract class AbstractBooleanExpressionRenderer {
33 protected final AbstractBooleanExpressionModel model;
34 private final String prefix;
35 private final CriterionRenderer criterionRenderer;
36 protected final RenderingContext renderingContext;
37
38 protected AbstractBooleanExpressionRenderer(String prefix, AbstractBuilder<?> builder) {
39 model = Objects.requireNonNull(builder.model);
40 this.prefix = Objects.requireNonNull(prefix);
41 renderingContext = Objects.requireNonNull(builder.renderingContext);
42 criterionRenderer = new CriterionRenderer(renderingContext);
43 }
44
45 public Optional<FragmentAndParameters> render() {
46 return model.initialCriterion()
47 .map(this::renderWithInitialCriterion)
48 .orElseGet(this::renderWithoutInitialCriterion)
49 .map(RenderedCriterion::fragmentAndParameters);
50 }
51
52 private Optional<RenderedCriterion> renderWithInitialCriterion(SqlCriterion initialCriterion) {
53 return criterionRenderer.render(initialCriterion, model.subCriteria(), this::calculateClause);
54 }
55
56 private Optional<RenderedCriterion> renderWithoutInitialCriterion() {
57 return criterionRenderer.render(model.subCriteria(), this::calculateClause);
58 }
59
60 private String calculateClause(FragmentCollector collector) {
61 if (collector.hasMultipleFragments()) {
62 return collector.collectFragments(
63 Collectors.joining(" ", spaceAfter(prefix), ""));
64 } else {
65 return collector.firstFragment()
66 .map(this::stripEnclosingParenthesesIfPresent)
67 .map(this::addPrefix)
68 .orElse("");
69 }
70 }
71
72 private String stripEnclosingParenthesesIfPresent(String fragment) {
73
74
75 if (fragment.startsWith("(") && fragment.endsWith(")")) {
76 return fragment.substring(1, fragment.length() - 1);
77 } else {
78 return fragment;
79 }
80 }
81
82 private String addPrefix(String fragment) {
83 return spaceAfter(prefix) + fragment;
84 }
85
86 public abstract static class AbstractBuilder<B extends AbstractBuilder<B>> {
87 private final AbstractBooleanExpressionModel model;
88 private @Nullable RenderingContext renderingContext;
89
90 protected AbstractBuilder(AbstractBooleanExpressionModel model) {
91 this.model = model;
92 }
93
94 public B withRenderingContext(RenderingContext renderingContext) {
95 this.renderingContext = renderingContext;
96 return getThis();
97 }
98
99 protected abstract B getThis();
100 }
101 }