• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mybatis / freemarker-scripting / #339

01 Dec 2023 06:16PM CUT coverage: 93.561%. Remained the same
#339

Pull #175

github

web-flow
Update dependency org.mybatis:mybatis-parent to v41
Pull Request #175: Update dependency org.mybatis:mybatis-parent to v41

78 of 86 branches covered (0.0%)

247 of 264 relevant lines covered (93.56%)

0.94 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.47
/src/main/java/org/mybatis/scripting/freemarker/FreeMarkerLanguageDriverConfig.java
1
/*
2
 *    Copyright 2015-2023 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.scripting.freemarker;
17

18
import java.io.BufferedReader;
19
import java.io.IOException;
20
import java.io.InputStream;
21
import java.io.InputStreamReader;
22
import java.nio.charset.Charset;
23
import java.nio.charset.StandardCharsets;
24
import java.util.Collections;
25
import java.util.HashMap;
26
import java.util.Map;
27
import java.util.Objects;
28
import java.util.Optional;
29
import java.util.Properties;
30
import java.util.function.Consumer;
31
import java.util.function.Function;
32

33
import org.apache.commons.text.WordUtils;
34
import org.apache.ibatis.io.Resources;
35
import org.apache.ibatis.logging.Log;
36
import org.apache.ibatis.logging.LogFactory;
37
import org.apache.ibatis.reflection.DefaultReflectorFactory;
38
import org.apache.ibatis.reflection.MetaObject;
39
import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
40
import org.apache.ibatis.reflection.wrapper.DefaultObjectWrapperFactory;
41

42
/**
43
 * Configuration class for {@link FreeMarkerLanguageDriver}.
44
 *
45
 * @author Kazuki Shimizu
46
 *
47
 * @since 1.2.0
48
 */
49
public class FreeMarkerLanguageDriverConfig {
1✔
50
  private static final String PROPERTY_KEY_CONFIG_FILE = "mybatis-freemarker.config.file";
51
  private static final String PROPERTY_KEY_CONFIG_ENCODING = "mybatis-freemarker.config.encoding";
52
  private static final String DEFAULT_PROPERTIES_FILE = "mybatis-freemarker.properties";
53
  private static final Map<Class<?>, Function<String, Object>> TYPE_CONVERTERS;
54

55
  static {
56
    Map<Class<?>, Function<String, Object>> converters = new HashMap<>();
1✔
57
    converters.put(String.class, String::trim);
1✔
58
    converters.put(boolean.class, v -> Boolean.valueOf(v.trim()));
1✔
59
    converters.put(Object.class, v -> v);
1✔
60
    TYPE_CONVERTERS = Collections.unmodifiableMap(converters);
1✔
61
  }
62

63
  private static final Log log = LogFactory.getLog(FreeMarkerLanguageDriverConfig.class);
1✔
64

65
  /**
66
   * The configuration properties.
67
   */
68
  private final Map<String, String> freemarkerSettings = new HashMap<>();
1✔
69

70
  /**
71
   * Template file configuration.
72
   */
73
  private final TemplateFileConfig templateFile = new TemplateFileConfig();
1✔
74

75
  /**
76
   * Get FreeMarker settings.
77
   *
78
   * @return FreeMarker settings
79
   */
80
  public Map<String, String> getFreemarkerSettings() {
81
    return freemarkerSettings;
1✔
82
  }
83

84
  /**
85
   * Get a base directory for reading template resources.
86
   * <p>
87
   * Default is none (just under classpath).
88
   * </p>
89
   *
90
   * @return a base directory for reading template resources
91
   *
92
   * @deprecated Recommend to use the {@link TemplateFileConfig#getBaseDir()}} because this method defined for keeping
93
   *             backward compatibility (There is possibility that this method removed at a future version)
94
   */
95
  @Deprecated
96
  public String getBasePackage() {
97
    return templateFile.getBaseDir();
1✔
98
  }
99

100
  /**
101
   * Set a base directory for reading template resources.
102
   *
103
   * @param basePackage
104
   *          a base directory for reading template resources
105
   *
106
   * @deprecated Recommend to use the {@link TemplateFileConfig#setBaseDir(String)} because this method defined for
107
   *             keeping backward compatibility (There is possibility that this method removed at a future version)
108
   */
109
  @Deprecated
110
  public void setBasePackage(String basePackage) {
111
    log.warn("The 'basePackage' has been deprecated since 1.2.0. Please use the 'templateFile.baseDir'.");
1✔
112
    templateFile.setBaseDir(basePackage);
1✔
113
  }
1✔
114

115
  /**
116
   * Get a template file configuration.
117
   *
118
   * @return a template file configuration
119
   */
120
  public TemplateFileConfig getTemplateFile() {
121
    return templateFile;
1✔
122
  }
123

124
  /**
125
   * Template file configuration.
126
   */
127
  public static class TemplateFileConfig {
1✔
128

129
    /**
130
     * The base directory for reading template resources.
131
     */
132
    private String baseDir = "";
1✔
133

134
    /**
135
     * The template file path provider configuration.
136
     */
137
    private final PathProviderConfig pathProvider = new PathProviderConfig();
1✔
138

139
    /**
140
     * Get the base directory for reading template resource file.
141
     * <p>
142
     * Default is {@code ""}(none).
143
     * </p>
144
     *
145
     * @return the base directory for reading template resource file
146
     */
147
    public String getBaseDir() {
148
      return baseDir;
1✔
149
    }
150

151
    /**
152
     * Set the base directory for reading template resource file.
153
     *
154
     * @param baseDir
155
     *          the base directory for reading template resource file
156
     */
157
    public void setBaseDir(String baseDir) {
158
      this.baseDir = baseDir;
1✔
159
    }
1✔
160

161
    /**
162
     * Get the template file path provider configuration.
163
     *
164
     * @return the template file path provider configuration
165
     */
166
    public PathProviderConfig getPathProvider() {
167
      return pathProvider;
1✔
168
    }
169

170
    /**
171
     * The template file path provider configuration.
172
     */
173
    public static class PathProviderConfig {
1✔
174

175
      /**
176
       * The prefix for adding to template file path.
177
       */
178
      private String prefix = "";
1✔
179

180
      /**
181
       * Whether includes package path part.
182
       */
183
      private boolean includesPackagePath = true;
1✔
184

185
      /**
186
       * Whether separate directory per mapper.
187
       */
188
      private boolean separateDirectoryPerMapper = true;
1✔
189

190
      /**
191
       * Whether includes mapper name into file name when separate directory per mapper.
192
       */
193
      private boolean includesMapperNameWhenSeparateDirectory = true;
1✔
194

195
      /**
196
       * Whether cache a resolved template file path.
197
       */
198
      private boolean cacheEnabled = true;
1✔
199

200
      /**
201
       * Get a prefix for adding to template file path.
202
       * <p>
203
       * Default is {@code ""}.
204
       * </p>
205
       *
206
       * @return a prefix for adding to template file path
207
       */
208
      public String getPrefix() {
209
        return prefix;
1✔
210
      }
211

212
      /**
213
       * Set the prefix for adding to template file path.
214
       *
215
       * @param prefix
216
       *          The prefix for adding to template file path
217
       */
218
      public void setPrefix(String prefix) {
219
        this.prefix = prefix;
1✔
220
      }
1✔
221

222
      /**
223
       * Get whether includes package path part.
224
       * <p>
225
       * Default is {@code true}.
226
       * </p>
227
       *
228
       * @return If includes package path, return {@code true}
229
       */
230
      public boolean isIncludesPackagePath() {
231
        return includesPackagePath;
1✔
232
      }
233

234
      /**
235
       * Set whether includes package path part.
236
       *
237
       * @param includesPackagePath
238
       *          If want to includes, set {@code true}
239
       */
240
      public void setIncludesPackagePath(boolean includesPackagePath) {
241
        this.includesPackagePath = includesPackagePath;
1✔
242
      }
1✔
243

244
      /**
245
       * Get whether separate directory per mapper.
246
       *
247
       * @return If separate directory per mapper, return {@code true}
248
       */
249
      public boolean isSeparateDirectoryPerMapper() {
250
        return separateDirectoryPerMapper;
1✔
251
      }
252

253
      /**
254
       * Set whether separate directory per mapper.
255
       * <p>
256
       * Default is {@code true}.
257
       * </p>
258
       *
259
       * @param separateDirectoryPerMapper
260
       *          If want to separate directory, set {@code true}
261
       */
262
      public void setSeparateDirectoryPerMapper(boolean separateDirectoryPerMapper) {
263
        this.separateDirectoryPerMapper = separateDirectoryPerMapper;
1✔
264
      }
1✔
265

266
      /**
267
       * Get whether includes mapper name into file name when separate directory per mapper.
268
       * <p>
269
       * Default is {@code true}.
270
       * </p>
271
       *
272
       * @return If includes mapper name, return {@code true}
273
       */
274
      public boolean isIncludesMapperNameWhenSeparateDirectory() {
275
        return includesMapperNameWhenSeparateDirectory;
1✔
276
      }
277

278
      /**
279
       * Set whether includes mapper name into file name when separate directory per mapper.
280
       * <p>
281
       * Default is {@code true}.
282
       * </p>
283
       *
284
       * @param includesMapperNameWhenSeparateDirectory
285
       *          If want to includes, set {@code true}
286
       */
287
      public void setIncludesMapperNameWhenSeparateDirectory(boolean includesMapperNameWhenSeparateDirectory) {
288
        this.includesMapperNameWhenSeparateDirectory = includesMapperNameWhenSeparateDirectory;
1✔
289
      }
1✔
290

291
      /**
292
       * Get whether cache a resolved template file path.
293
       * <p>
294
       * Default is {@code true}.
295
       * </p>
296
       *
297
       * @return If cache a resolved template file path, return {@code true}
298
       */
299
      public boolean isCacheEnabled() {
300
        return cacheEnabled;
1✔
301
      }
302

303
      /**
304
       * Set whether cache a resolved template file path.
305
       *
306
       * @param cacheEnabled
307
       *          If want to cache, set {@code true}
308
       */
309
      public void setCacheEnabled(boolean cacheEnabled) {
310
        this.cacheEnabled = cacheEnabled;
1✔
311
      }
1✔
312

313
    }
314

315
  }
316

317
  /**
318
   * Create an instance from default properties file. <br>
319
   * If you want to customize a default {@code TemplateEngine}, you can configure some property using
320
   * mybatis-freemarker.properties that encoded by UTF-8. Also, you can change the properties file that will read using
321
   * system property (-Dmybatis-freemarker.config.file=... -Dmybatis-freemarker.config.encoding=...). <br>
322
   * Supported properties are as follows:
323
   * <table border="1">
324
   * <caption>Supported properties</caption>
325
   * <tr>
326
   * <th>Property Key</th>
327
   * <th>Description</th>
328
   * <th>Default</th>
329
   * </tr>
330
   * <tr>
331
   * <th colspan="3">General configuration</th>
332
   * </tr>
333
   * <tr>
334
   * <td>base-package</td>
335
   * <td>The base directory for reading template resources</td>
336
   * <td>None(just under classpath)</td>
337
   * </tr>
338
   * <tr>
339
   * <td>freemarker-settings.*</td>
340
   * <td>The settings of freemarker {@link freemarker.core.Configurable#setSetting(String, String)}).</td>
341
   * <td>-</td>
342
   * </tr>
343
   * </table>
344
   *
345
   * @return a configuration instance
346
   */
347
  public static FreeMarkerLanguageDriverConfig newInstance() {
348
    return newInstance(loadDefaultProperties());
1✔
349
  }
350

351
  /**
352
   * Create an instance from specified properties.
353
   *
354
   * @param customProperties
355
   *          custom configuration properties
356
   *
357
   * @return a configuration instance
358
   *
359
   * @see #newInstance()
360
   */
361
  public static FreeMarkerLanguageDriverConfig newInstance(Properties customProperties) {
362
    FreeMarkerLanguageDriverConfig config = new FreeMarkerLanguageDriverConfig();
1✔
363
    Properties properties = loadDefaultProperties();
1✔
364
    Optional.ofNullable(customProperties).ifPresent(properties::putAll);
1✔
365
    override(config, properties);
1✔
366
    return config;
1✔
367
  }
368

369
  /**
370
   * Create an instance using specified customizer and override using a default properties file.
371
   *
372
   * @param customizer
373
   *          baseline customizer
374
   *
375
   * @return a configuration instance
376
   *
377
   * @see #newInstance()
378
   */
379
  public static FreeMarkerLanguageDriverConfig newInstance(Consumer<FreeMarkerLanguageDriverConfig> customizer) {
380
    FreeMarkerLanguageDriverConfig config = new FreeMarkerLanguageDriverConfig();
1✔
381
    customizer.accept(config);
1✔
382
    override(config, loadDefaultProperties());
1✔
383
    return config;
1✔
384
  }
385

386
  private static void override(FreeMarkerLanguageDriverConfig config, Properties properties) {
387
    MetaObject metaObject = MetaObject.forObject(config, new DefaultObjectFactory(), new DefaultObjectWrapperFactory(),
1✔
388
        new DefaultReflectorFactory());
389
    properties.forEach((key, value) -> {
1✔
390
      String propertyPath = WordUtils
1✔
391
          .uncapitalize(WordUtils.capitalize(Objects.toString(key), '-').replaceAll("-", ""));
1✔
392
      Optional.ofNullable(value).ifPresent(v -> {
1✔
393
        Object convertedValue = TYPE_CONVERTERS.get(metaObject.getSetterType(propertyPath)).apply(value.toString());
1✔
394
        metaObject.setValue(propertyPath, convertedValue);
1✔
395
      });
1✔
396
    });
1✔
397
  }
1✔
398

399
  private static Properties loadDefaultProperties() {
400
    return loadProperties(System.getProperty(PROPERTY_KEY_CONFIG_FILE, DEFAULT_PROPERTIES_FILE));
1✔
401
  }
402

403
  private static Properties loadProperties(String resourcePath) {
404
    Properties properties = new Properties();
1✔
405
    InputStream in;
406
    try {
407
      in = Resources.getResourceAsStream(resourcePath);
1✔
408
    } catch (IOException e) {
1✔
409
      in = null;
1✔
410
    }
1✔
411
    if (in != null) {
1✔
412
      Charset encoding = Optional.ofNullable(System.getProperty(PROPERTY_KEY_CONFIG_ENCODING)).map(Charset::forName)
1✔
413
          .orElse(StandardCharsets.UTF_8);
1✔
414
      try (InputStreamReader inReader = new InputStreamReader(in, encoding);
1✔
415
          BufferedReader bufReader = new BufferedReader(inReader)) {
1✔
416
        properties.load(bufReader);
1✔
417
      } catch (IOException e) {
×
418
        throw new IllegalStateException(e);
×
419
      }
1✔
420
    }
421
    return properties;
1✔
422
  }
423

424
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc