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     public boolean isEmptyListConditionRenderingAllowed() {
105         return statementConfiguration.isEmptyListConditionRenderingAllowed();
106     }
107 
108     /**
109      * Create a new rendering context based on this, with the table alias calculator modified to include the
110      * specified child table alias calculator. This is used by the query expression renderer when the alias calculator
111      * may change during rendering.
112      *
113      * @param childTableAliasCalculator the child table alias calculator
114      * @return a new rendering context whose table alias calculator is composed of the former calculator as parent, and
115      *     the new child calculator
116      */
117     public RenderingContext withChildTableAliasCalculator(TableAliasCalculator childTableAliasCalculator) {
118         TableAliasCalculator tac = new TableAliasCalculatorWithParent.Builder()
119                 .withParent(tableAliasCalculator)
120                 .withChild(childTableAliasCalculator)
121                 .build();
122 
123         return new Builder()
124                 .withRenderingStrategy(this.renderingStrategy)
125                 .withSequence(this.sequence)
126                 .withParameterName(this.configuredParameterName)
127                 .withTableAliasCalculator(tac)
128                 .withStatementConfiguration(statementConfiguration)
129                 .build();
130     }
131 
132     public static Builder withRenderingStrategy(RenderingStrategy renderingStrategy) {
133         return new Builder().withRenderingStrategy(renderingStrategy);
134     }
135 
136     public static class Builder {
137         private RenderingStrategy renderingStrategy;
138         private AtomicInteger sequence;
139         private TableAliasCalculator tableAliasCalculator = TableAliasCalculator.empty();
140         private String parameterName;
141         private StatementConfiguration statementConfiguration;
142 
143         public Builder withRenderingStrategy(RenderingStrategy renderingStrategy) {
144             this.renderingStrategy = renderingStrategy;
145             return this;
146         }
147 
148         public Builder withSequence(AtomicInteger sequence) {
149             this.sequence = sequence;
150             return this;
151         }
152 
153         public Builder withTableAliasCalculator(TableAliasCalculator tableAliasCalculator) {
154             this.tableAliasCalculator = tableAliasCalculator;
155             return this;
156         }
157 
158         public Builder withParameterName(String parameterName) {
159             this.parameterName = parameterName;
160             return this;
161         }
162 
163         public Builder withStatementConfiguration(StatementConfiguration statementConfiguration) {
164             this.statementConfiguration = statementConfiguration;
165             return this;
166         }
167 
168         public RenderingContext build() {
169             return new RenderingContext(this);
170         }
171     }
172 }