TrimSqlNode.java

  1. /*
  2.  *    Copyright 2009-2021 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.  *       http://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.apache.ibatis.scripting.xmltags;

  17. import java.util.ArrayList;
  18. import java.util.Collections;
  19. import java.util.List;
  20. import java.util.Locale;
  21. import java.util.Map;
  22. import java.util.StringTokenizer;

  23. import org.apache.ibatis.session.Configuration;

  24. /**
  25.  * @author Clinton Begin
  26.  */
  27. public class TrimSqlNode implements SqlNode {

  28.   private final SqlNode contents;
  29.   private final String prefix;
  30.   private final String suffix;
  31.   private final List<String> prefixesToOverride;
  32.   private final List<String> suffixesToOverride;
  33.   private final Configuration configuration;

  34.   public TrimSqlNode(Configuration configuration, SqlNode contents, String prefix, String prefixesToOverride, String suffix, String suffixesToOverride) {
  35.     this(configuration, contents, prefix, parseOverrides(prefixesToOverride), suffix, parseOverrides(suffixesToOverride));
  36.   }

  37.   protected TrimSqlNode(Configuration configuration, SqlNode contents, String prefix, List<String> prefixesToOverride, String suffix, List<String> suffixesToOverride) {
  38.     this.contents = contents;
  39.     this.prefix = prefix;
  40.     this.prefixesToOverride = prefixesToOverride;
  41.     this.suffix = suffix;
  42.     this.suffixesToOverride = suffixesToOverride;
  43.     this.configuration = configuration;
  44.   }

  45.   @Override
  46.   public boolean apply(DynamicContext context) {
  47.     FilteredDynamicContext filteredDynamicContext = new FilteredDynamicContext(context);
  48.     boolean result = contents.apply(filteredDynamicContext);
  49.     filteredDynamicContext.applyAll();
  50.     return result;
  51.   }

  52.   private static List<String> parseOverrides(String overrides) {
  53.     if (overrides != null) {
  54.       final StringTokenizer parser = new StringTokenizer(overrides, "|", false);
  55.       final List<String> list = new ArrayList<>(parser.countTokens());
  56.       while (parser.hasMoreTokens()) {
  57.         list.add(parser.nextToken().toUpperCase(Locale.ENGLISH));
  58.       }
  59.       return list;
  60.     }
  61.     return Collections.emptyList();
  62.   }

  63.   private class FilteredDynamicContext extends DynamicContext {
  64.     private DynamicContext delegate;
  65.     private boolean prefixApplied;
  66.     private boolean suffixApplied;
  67.     private StringBuilder sqlBuffer;

  68.     public FilteredDynamicContext(DynamicContext delegate) {
  69.       super(configuration, null);
  70.       this.delegate = delegate;
  71.       this.prefixApplied = false;
  72.       this.suffixApplied = false;
  73.       this.sqlBuffer = new StringBuilder();
  74.     }

  75.     public void applyAll() {
  76.       sqlBuffer = new StringBuilder(sqlBuffer.toString().trim());
  77.       String trimmedUppercaseSql = sqlBuffer.toString().toUpperCase(Locale.ENGLISH);
  78.       if (trimmedUppercaseSql.length() > 0) {
  79.         applyPrefix(sqlBuffer, trimmedUppercaseSql);
  80.         applySuffix(sqlBuffer, trimmedUppercaseSql);
  81.       }
  82.       delegate.appendSql(sqlBuffer.toString());
  83.     }

  84.     @Override
  85.     public Map<String, Object> getBindings() {
  86.       return delegate.getBindings();
  87.     }

  88.     @Override
  89.     public void bind(String name, Object value) {
  90.       delegate.bind(name, value);
  91.     }

  92.     @Override
  93.     public int getUniqueNumber() {
  94.       return delegate.getUniqueNumber();
  95.     }

  96.     @Override
  97.     public void appendSql(String sql) {
  98.       sqlBuffer.append(sql);
  99.     }

  100.     @Override
  101.     public String getSql() {
  102.       return delegate.getSql();
  103.     }

  104.     private void applyPrefix(StringBuilder sql, String trimmedUppercaseSql) {
  105.       if (!prefixApplied) {
  106.         prefixApplied = true;
  107.         if (prefixesToOverride != null) {
  108.           for (String toRemove : prefixesToOverride) {
  109.             if (trimmedUppercaseSql.startsWith(toRemove)) {
  110.               sql.delete(0, toRemove.trim().length());
  111.               break;
  112.             }
  113.           }
  114.         }
  115.         if (prefix != null) {
  116.           sql.insert(0, " ");
  117.           sql.insert(0, prefix);
  118.         }
  119.       }
  120.     }

  121.     private void applySuffix(StringBuilder sql, String trimmedUppercaseSql) {
  122.       if (!suffixApplied) {
  123.         suffixApplied = true;
  124.         if (suffixesToOverride != null) {
  125.           for (String toRemove : suffixesToOverride) {
  126.             if (trimmedUppercaseSql.endsWith(toRemove) || trimmedUppercaseSql.endsWith(toRemove.trim())) {
  127.               int start = sql.length() - toRemove.trim().length();
  128.               int end = sql.length();
  129.               sql.delete(start, end);
  130.               break;
  131.             }
  132.           }
  133.         }
  134.         if (suffix != null) {
  135.           sql.append(" ");
  136.           sql.append(suffix);
  137.         }
  138.       }
  139.     }

  140.   }

  141. }