View Javadoc
1   /*
2    *    Copyright 2016-2026 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.render;
17  
18  import static org.mybatis.dynamic.sql.util.StringUtilities.spaceBefore;
19  
20  import java.util.Objects;
21  import java.util.concurrent.atomic.AtomicInteger;
22  
23  import org.jspecify.annotations.Nullable;
24  import org.mybatis.dynamic.sql.BindableColumn;
25  import org.mybatis.dynamic.sql.SqlColumn;
26  import org.mybatis.dynamic.sql.SqlTable;
27  import org.mybatis.dynamic.sql.configuration.StatementConfiguration;
28  
29  /**
30   * This class encapsulates all the supporting items related to rendering, and contains many utility methods
31   * used during the rendering process.
32   *
33   * @since 1.5.1
34   * @author Jeff Butler
35   */
36  public class RenderingContext {
37  
38      private final RenderingStrategy renderingStrategy;
39      private final AtomicInteger sequence;
40      private final TableAliasCalculator tableAliasCalculator;
41      private static final String PARAMETER_NAME = RenderingStrategy.DEFAULT_PARAMETER_PREFIX;
42      private final StatementConfiguration statementConfiguration;
43  
44      private RenderingContext(Builder builder) {
45          renderingStrategy = Objects.requireNonNull(builder.renderingStrategy);
46          tableAliasCalculator = Objects.requireNonNull(builder.tableAliasCalculator);
47          statementConfiguration = Objects.requireNonNull(builder.statementConfiguration);
48          sequence = Objects.requireNonNullElseGet(builder.sequence, () -> new AtomicInteger(1));
49      }
50  
51      private String nextMapKey() {
52          return renderingStrategy.formatParameterMapKey(sequence);
53      }
54  
55      private <T> String renderedPlaceHolder(String mapKey, BindableColumn<T> column) {
56          return  column.renderingStrategy().orElse(renderingStrategy)
57                  .getFormattedJdbcPlaceholder(column, PARAMETER_NAME, mapKey);
58      }
59  
60      public RenderedParameterInfo calculateFetchFirstRowsParameterInfo() {
61          String mapKey = renderingStrategy.formatParameterMapKeyForFetchFirstRows(sequence);
62          return new RenderedParameterInfo(mapKey,
63                  renderingStrategy.getFormattedJdbcPlaceholderForPagingParameters(PARAMETER_NAME, mapKey));
64      }
65  
66      public RenderedParameterInfo calculateLimitParameterInfo() {
67          String mapKey = renderingStrategy.formatParameterMapKeyForLimit(sequence);
68          return new RenderedParameterInfo(mapKey,
69                  renderingStrategy.getFormattedJdbcPlaceholderForPagingParameters(PARAMETER_NAME, mapKey));
70      }
71  
72      public RenderedParameterInfo calculateOffsetParameterInfo() {
73          String mapKey = renderingStrategy.formatParameterMapKeyForOffset(sequence);
74          return new RenderedParameterInfo(mapKey,
75                  renderingStrategy.getFormattedJdbcPlaceholderForPagingParameters(PARAMETER_NAME, mapKey));
76      }
77  
78      public <T> RenderedParameterInfo calculateParameterInfo(BindableColumn<T> column) {
79          String mapKey = nextMapKey();
80          return new RenderedParameterInfo(mapKey, renderedPlaceHolder(mapKey, column));
81      }
82  
83      public <T> String aliasedColumnName(SqlColumn<T> column) {
84          return tableAliasCalculator.aliasForColumn(column.table())
85                  .map(alias -> aliasedColumnName(column, alias))
86                  .orElseGet(column::name);
87      }
88  
89      public <T> String aliasedColumnName(SqlColumn<T> column, String explicitAlias) {
90          return explicitAlias + "." + column.name();  //$NON-NLS-1$
91      }
92  
93      public String aliasedTableName(SqlTable table) {
94          return tableAliasCalculator.aliasForTable(table)
95                  .map(a -> table.tableName() + spaceBefore(a))
96                  .orElseGet(table::tableName);
97      }
98  
99      public boolean isNonRenderingClauseAllowed() {
100         return statementConfiguration.isNonRenderingWhereClauseAllowed();
101     }
102 
103     /**
104      * Create a new rendering context based on this, with the table alias calculator modified to include the
105      * specified child table alias calculator. This is used by the query expression renderer when the alias calculator
106      * may change during rendering.
107      *
108      * @param childTableAliasCalculator the child table alias calculator
109      * @return a new rendering context whose table alias calculator is composed of the former calculator as parent, and
110      *     the new child calculator
111      */
112     public RenderingContext withChildTableAliasCalculator(TableAliasCalculator childTableAliasCalculator) {
113         TableAliasCalculator tac = new TableAliasCalculatorWithParent.Builder()
114                 .withParent(tableAliasCalculator)
115                 .withChild(childTableAliasCalculator)
116                 .build();
117 
118         return new Builder()
119                 .withRenderingStrategy(this.renderingStrategy)
120                 .withSequence(this.sequence)
121                 .withTableAliasCalculator(tac)
122                 .withStatementConfiguration(statementConfiguration)
123                 .build();
124     }
125 
126     public static Builder withRenderingStrategy(RenderingStrategy renderingStrategy) {
127         return new Builder().withRenderingStrategy(renderingStrategy);
128     }
129 
130     public static class Builder {
131         private @Nullable RenderingStrategy renderingStrategy;
132         private @Nullable AtomicInteger sequence;
133         private @Nullable TableAliasCalculator tableAliasCalculator = TableAliasCalculator.empty();
134         private @Nullable StatementConfiguration statementConfiguration;
135 
136         public Builder withRenderingStrategy(RenderingStrategy renderingStrategy) {
137             this.renderingStrategy = renderingStrategy;
138             return this;
139         }
140 
141         public Builder withSequence(AtomicInteger sequence) {
142             this.sequence = sequence;
143             return this;
144         }
145 
146         public Builder withTableAliasCalculator(TableAliasCalculator tableAliasCalculator) {
147             this.tableAliasCalculator = tableAliasCalculator;
148             return this;
149         }
150 
151         public Builder withStatementConfiguration(StatementConfiguration statementConfiguration) {
152             this.statementConfiguration = statementConfiguration;
153             return this;
154         }
155 
156         public RenderingContext build() {
157             return new RenderingContext(this);
158         }
159     }
160 }