View Javadoc
1   /*
2    *    Copyright 2016-2025 the original author or authors.
3    *
4    *    Licensed under the Apache License, Version 2.0 (the "License");
5    *    you may not use this file except in compliance with the License.
6    *    You may obtain a copy of the License at
7    *
8    *       https://www.apache.org/licenses/LICENSE-2.0
9    *
10   *    Unless required by applicable law or agreed to in writing, software
11   *    distributed under the License is distributed on an "AS IS" BASIS,
12   *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *    See the License for the specific language governing permissions and
14   *    limitations under the License.
15   */
16  package org.mybatis.dynamic.sql.select.render;
17  
18  import java.util.Objects;
19  import java.util.stream.Collectors;
20  
21  import org.jspecify.annotations.Nullable;
22  import org.mybatis.dynamic.sql.common.OrderByModel;
23  import org.mybatis.dynamic.sql.common.OrderByRenderer;
24  import org.mybatis.dynamic.sql.render.RenderingContext;
25  import org.mybatis.dynamic.sql.select.PagingModel;
26  import org.mybatis.dynamic.sql.select.QueryExpressionModel;
27  import org.mybatis.dynamic.sql.select.SelectModel;
28  import org.mybatis.dynamic.sql.util.FragmentAndParameters;
29  import org.mybatis.dynamic.sql.util.FragmentCollector;
30  
31  public class SubQueryRenderer {
32      private final SelectModel selectModel;
33      private final RenderingContext renderingContext;
34      private final String prefix;
35      private final String suffix;
36  
37      private SubQueryRenderer(Builder builder) {
38          selectModel = Objects.requireNonNull(builder.selectModel);
39          renderingContext = Objects.requireNonNull(builder.renderingContext);
40          prefix = builder.prefix == null ? "" : builder.prefix; //$NON-NLS-1$
41          suffix = builder.suffix == null ? "" : builder.suffix; //$NON-NLS-1$
42      }
43  
44      public FragmentAndParameters render() {
45          FragmentCollector fragmentCollector = selectModel
46                  .queryExpressions()
47                  .map(this::renderQueryExpression)
48                  .collect(FragmentCollector.collect());
49  
50          selectModel.orderByModel()
51                  .map(this::renderOrderBy)
52                  .ifPresent(fragmentCollector::add);
53  
54          selectModel.pagingModel()
55                  .map(this::renderPagingModel)
56                  .ifPresent(fragmentCollector::add);
57  
58          selectModel.forClause()
59                  .map(FragmentAndParameters::fromFragment)
60                  .ifPresent(fragmentCollector::add);
61  
62          selectModel.waitClause()
63                  .map(FragmentAndParameters::fromFragment)
64                  .ifPresent(fragmentCollector::add);
65  
66          return fragmentCollector.toFragmentAndParameters(Collectors.joining(" ", prefix, suffix)); //$NON-NLS-1$
67      }
68  
69      private FragmentAndParameters renderQueryExpression(QueryExpressionModel queryExpressionModel) {
70          return QueryExpressionRenderer.withQueryExpression(queryExpressionModel)
71                  .withRenderingContext(renderingContext)
72                  .build()
73                  .render();
74      }
75  
76      private FragmentAndParameters renderOrderBy(OrderByModel orderByModel) {
77          return new OrderByRenderer(renderingContext).render(orderByModel);
78      }
79  
80      private FragmentAndParameters renderPagingModel(PagingModel pagingModel) {
81          return new PagingModelRenderer.Builder()
82                  .withPagingModel(pagingModel)
83                  .withRenderingContext(renderingContext)
84                  .build()
85                  .render();
86      }
87  
88      public static Builder withSelectModel(SelectModel selectModel) {
89          return new Builder().withSelectModel(selectModel);
90      }
91  
92      public static class Builder {
93          private @Nullable SelectModel selectModel;
94          private @Nullable RenderingContext renderingContext;
95          private @Nullable String prefix;
96          private @Nullable String suffix;
97  
98          public Builder withRenderingContext(RenderingContext renderingContext) {
99              this.renderingContext = renderingContext;
100             return this;
101         }
102 
103         public Builder withSelectModel(SelectModel selectModel) {
104             this.selectModel = selectModel;
105             return this;
106         }
107 
108         public Builder withPrefix(String prefix) {
109             this.prefix = prefix;
110             return this;
111         }
112 
113         public Builder withSuffix(String suffix) {
114             this.suffix = suffix;
115             return this;
116         }
117 
118         public SubQueryRenderer build() {
119             return new SubQueryRenderer(this);
120         }
121     }
122 }