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