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

future-architect / uroborosql / #692

pending completion
#692

push

HidekiSugimoto189
Added output judgment for log output arguments that are string processed

53 of 53 new or added lines in 6 files covered. (100.0%)

7895 of 8761 relevant lines covered (90.12%)

0.9 hits per line

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

83.72
/src/main/java/jp/co/future/uroborosql/store/SqlLoaderImpl.java
1
/**
2
 * Copyright (c) 2017-present, Future Corporation
3
 *
4
 * This source code is licensed under the MIT license found in the
5
 * LICENSE file in the root directory of this source tree.
6
 */
7
package jp.co.future.uroborosql.store;
8

9
import java.io.BufferedReader;
10
import java.io.File;
11
import java.io.FileInputStream;
12
import java.io.IOException;
13
import java.io.InputStream;
14
import java.io.InputStreamReader;
15
import java.net.JarURLConnection;
16
import java.net.URL;
17
import java.net.URLDecoder;
18
import java.nio.charset.StandardCharsets;
19
import java.util.Enumeration;
20
import java.util.Map;
21
import java.util.concurrent.ConcurrentHashMap;
22
import java.util.jar.JarEntry;
23
import java.util.jar.JarFile;
24

25
import org.slf4j.Logger;
26
import org.slf4j.LoggerFactory;
27

28
import jp.co.future.uroborosql.exception.UroborosqlRuntimeException;
29
import jp.co.future.uroborosql.utils.StringUtils;
30

31
/**
32
 * SQL読み込みクラス<br>
33
 *
34
 * @author H.Sugimoto
35
 */
36
public class SqlLoaderImpl implements SqlLoader {
37
        /** ロガー */
38
        private static final Logger LOG = LoggerFactory.getLogger(SqlLoaderImpl.class);
1✔
39

40
        /** SQLファイルをロードするルートパス */
41
        private String loadPath = DEFAULT_LOAD_PATH;
1✔
42

43
        /**
44
         * SQLファイル拡張子
45
         */
46
        private String fileExtension = DEFAULT_FILE_EXTENSION;
1✔
47

48
        /**
49
         * SQLファイルエンコーディング
50
         */
51
        private String sqlEncoding = System.getProperty("file.encoding");
1✔
52

53
        /**
54
         * コンストラクタ<br>
55
         */
56
        public SqlLoaderImpl() {
1✔
57
        }
1✔
58

59
        /**
60
         * コンストラクタ<br>
61
         *
62
         * @param loadPath SQLファイルをロードするルートパス
63
         */
64
        public SqlLoaderImpl(final String loadPath) {
1✔
65
                setLoadPath(loadPath);
1✔
66
        }
1✔
67

68
        /**
69
         * コンストラクタ<br>
70
         *
71
         * @param loadPath SQLファイルをロードするルートパス
72
         * @param fileExtension SQLファイル拡張子
73
         */
74
        public SqlLoaderImpl(final String loadPath, final String fileExtension) {
1✔
75
                setLoadPath(loadPath);
1✔
76
                setFileExtension(fileExtension);
1✔
77
        }
1✔
78

79
        /**
80
         * {@inheritDoc}
81
         *
82
         * @see jp.co.future.uroborosql.store.SqlLoader#getLoadPath()
83
         */
84
        @Override
85
        public String getLoadPath() {
86
                return loadPath;
1✔
87
        }
88

89
        /**
90
         * {@inheritDoc}
91
         *
92
         * @see jp.co.future.uroborosql.store.SqlLoader#setLoadPath(java.lang.String)
93
         */
94
        @Override
95
        public void setLoadPath(final String loadPath) {
96
                if (loadPath == null) {
1✔
97
                        if (LOG.isWarnEnabled()) {
1✔
98
                                LOG.warn("Use the default value because SQL template path is set to NULL.");
×
99
                                LOG.warn("Default load path[{}]", DEFAULT_LOAD_PATH);
×
100
                        }
101
                        this.loadPath = DEFAULT_LOAD_PATH;
1✔
102
                } else {
103
                        this.loadPath = loadPath;
1✔
104
                }
105
        }
1✔
106

107
        /**
108
         * {@inheritDoc}
109
         *
110
         * @see jp.co.future.uroborosql.store.SqlLoader#getFileExtension()
111
         */
112
        @Override
113
        public String getFileExtension() {
114
                return fileExtension;
1✔
115
        }
116

117
        /**
118
         * {@inheritDoc}
119
         *
120
         * @see jp.co.future.uroborosql.store.SqlLoader#setFileExtension(java.lang.String)
121
         */
122
        @Override
123
        public void setFileExtension(final String fileExtension) {
124
                if (fileExtension == null) {
1✔
125
                        if (LOG.isWarnEnabled()) {
1✔
126
                                LOG.warn("Use the default value because SQL template extension is set to NULL.");
×
127
                                LOG.warn("Default SQL template extension[{}]", DEFAULT_FILE_EXTENSION);
×
128
                        }
129
                        this.fileExtension = DEFAULT_FILE_EXTENSION;
1✔
130
                } else {
131
                        this.fileExtension = fileExtension;
1✔
132
                }
133
        }
1✔
134

135
        /**
136
         * {@inheritDoc}
137
         *
138
         * @see jp.co.future.uroborosql.store.SqlLoader#getSqlEncoding()
139
         */
140
        @Override
141
        public String getSqlEncoding() {
142
                return sqlEncoding;
1✔
143
        }
144

145
        /**
146
         * {@inheritDoc}
147
         *
148
         * @see jp.co.future.uroborosql.store.SqlLoader#setSqlEncoding(java.lang.String)
149
         */
150
        @Override
151
        public void setSqlEncoding(final String encoding) {
152
                this.sqlEncoding = encoding;
1✔
153
        }
1✔
154

155
        /**
156
         * {@inheritDoc}
157
         *
158
         * @see jp.co.future.uroborosql.store.SqlLoader#load()
159
         */
160
        @Override
161
        public ConcurrentHashMap<String, String> load() {
162
                ConcurrentHashMap<String, String> loadedSqlMap = new ConcurrentHashMap<>();
1✔
163
                try {
164
                        Enumeration<URL> resources = Thread.currentThread().getContextClassLoader().getResources(loadPath);
1✔
165
                        while (resources.hasMoreElements()) {
1✔
166
                                URL resource = resources.nextElement();
1✔
167
                                File rootDir = new File(URLDecoder.decode(resource.getFile(), StandardCharsets.UTF_8.toString()));
1✔
168

169
                                if (!rootDir.exists() || !rootDir.isDirectory()) {
1✔
170
                                        if ("jar".equalsIgnoreCase(resource.getProtocol())) {
1✔
171
                                                putAllIfAbsent(loadedSqlMap, load((JarURLConnection) resource.openConnection(), loadPath));
1✔
172
                                                continue;
1✔
173
                                        }
174

175
                                        if (LOG.isWarnEnabled()) {
×
176
                                                LOG.warn("Ignore because not directory.[{}]", rootDir.getAbsolutePath());
×
177
                                        }
178
                                        continue;
179
                                }
180

181
                                if (LOG.isDebugEnabled()) {
1✔
182
                                        LOG.debug("Start loading SQL template.[{}]", rootDir.getAbsolutePath());
×
183
                                }
184
                                putAllIfAbsent(loadedSqlMap, load(new StringBuilder(), rootDir));
1✔
185
                        }
1✔
186
                } catch (IOException e) {
×
187
                        throw new UroborosqlRuntimeException("Failed to load SQL template.", e);
×
188
                }
1✔
189

190
                if (loadedSqlMap.isEmpty() && LOG.isWarnEnabled()) {
1✔
191
                        LOG.warn("SQL template could not be found.");
×
192
                        LOG.warn("Returns an empty SQL cache.");
×
193
                }
194

195
                return loadedSqlMap;
1✔
196
        }
197

198
        /**
199
         * キーが存在する場合は上書きを行わずに指定されたマップのすべてのマッピングをこのマップにコピーします
200
         *
201
         * @param baseMap マージ先の対象データ(マップの内容が更新されます)
202
         * @param map コピーの対象データ
203
         */
204
        private void putAllIfAbsent(final Map<String, String> baseMap, final Map<String, String> map) {
205
                for (Map.Entry<String, String> entry : map.entrySet()) {
1✔
206
                        if (!baseMap.containsKey(entry.getKey())) {
1✔
207
                                baseMap.put(entry.getKey(), entry.getValue());
1✔
208
                        }
209
                }
1✔
210
        }
1✔
211

212
        /**
213
         * Jar ファイル内の指定のフォルダパス以下にある .sqlファイルを読み込み SQL識別子をキーとしたSQL文のMapを返します
214
         *
215
         * @param jarUrlConnection Jarファイル内のURLコレクション
216
         * @param loadPath ロードパス
217
         * @return SQL識別子をキーとしたSQL文のMap
218
         */
219
        private ConcurrentHashMap<String, String> load(final JarURLConnection jarUrlConnection, final String loadPath)
220
                        throws IOException {
221
                if (LOG.isDebugEnabled()) {
1✔
222
                        LOG.debug("Loading the following SQL template.[{}]", jarUrlConnection);
×
223
                }
224

225
                ConcurrentHashMap<String, String> sqlMap = new ConcurrentHashMap<>();
1✔
226
                JarFile jarFile = jarUrlConnection.getJarFile();
1✔
227
                Enumeration<JarEntry> jarEnum = jarFile.entries();
1✔
228
                while (jarEnum.hasMoreElements()) {
1✔
229
                        JarEntry jarEntry = jarEnum.nextElement();
1✔
230
                        String fileName = jarEntry.getName();
1✔
231
                        if (fileName.startsWith(loadPath) && fileName.toLowerCase().endsWith(fileExtension)) {
1✔
232
                                String sql = trimSlash(
1✔
233
                                                read(new BufferedReader(new InputStreamReader(jarFile.getInputStream(jarEntry)))));
1✔
234
                                fileName = fileName.substring(loadPath.length() + 1, fileName.length() - 4);
1✔
235
                                sqlMap.put(fileName, sql);
1✔
236

237
                                if (LOG.isTraceEnabled()) {
1✔
238
                                        LOG.trace("SQL template loaded.[{}]", fileName);
×
239
                                        LOG.trace("Add SQL template. [{}],[{}]", fileName, sql);
×
240
                                }
241
                        }
242
                }
1✔
243
                return sqlMap;
1✔
244

245
        }
246

247
        /**
248
         * 指定されたパッケージ以下のSQLを順次読み込みする<br>
249
         * 文末の"/"は削除される<br>
250
         *
251
         * @param packageName パッケージ名を格納するStringBuilder
252
         * @param dir 探索対象ディレクトリ
253
         * @return SQL識別子をキーとしたSQL文のMap
254
         * @throws IOException ファイルアクセスに失敗した場合
255
         */
256
        private ConcurrentHashMap<String, String> load(final StringBuilder packageName, final File dir) throws IOException {
257
                if (LOG.isDebugEnabled()) {
1✔
258
                        LOG.debug("Loading SQL template.[{}]", packageName);
×
259
                }
260

261
                ConcurrentHashMap<String, String> sqlMap = new ConcurrentHashMap<>();
1✔
262
                File[] files = dir.listFiles();
1✔
263
                for (File file : files) {
1✔
264
                        String fileName = file.getName();
1✔
265
                        if (file.isDirectory()) {
1✔
266
                                sqlMap.putAll(load(makeNewPackageName(packageName, file), file));
1✔
267
                        } else if (fileName.toLowerCase().endsWith(fileExtension)) {
1✔
268
                                String sql = trimSlash(read(new BufferedReader(new InputStreamReader(new FileInputStream(file),
1✔
269
                                                getSqlEncoding()))));
1✔
270
                                String sqlName = makeSqlName(packageName, fileName);
1✔
271
                                sqlMap.put(sqlName, sql);
1✔
272

273
                                if (LOG.isTraceEnabled()) {
1✔
274
                                        LOG.trace("Loaded SQL template.[{}]", fileName);
×
275
                                        LOG.trace("Add SQL template.[{}],[{}]", sqlName, sql);
×
276
                                }
277
                        }
278
                }
279
                return sqlMap;
1✔
280
        }
281

282
        /**
283
         * ファイルパス指定のSQL読み込み<br>
284
         * 文末の"/"は削除される<br>
285
         *
286
         * @param filePath ルートフォルダからの相対ファイルパス
287
         *
288
         * @return SQL
289
         *
290
         * @see jp.co.future.uroborosql.store.SqlLoader#load(java.lang.String)
291
         */
292
        @Override
293
        public String load(final String filePath) {
294
                if (StringUtils.isEmpty(filePath)) {
1✔
295
                        throw new IllegalArgumentException("Invalid file path. filePath=" + filePath);
1✔
296
                }
297
                String targetFilePath = getFilePath(trimSqlExtension(filePath.replace(".", PATH_SEPARATOR)));
1✔
298
                String sql = null;
1✔
299

300
                URL resource = Thread.currentThread().getContextClassLoader().getResource(targetFilePath);
1✔
301
                if (resource != null) {
1✔
302
                        try (InputStream is = resource.openStream()) {
1✔
303

304
                                sql = trimSlash(read(new BufferedReader(new InputStreamReader(is))));
1✔
305

306
                                if (LOG.isDebugEnabled()) {
1✔
307
                                        LOG.debug("Loaded SQL template.[{}]", targetFilePath);
×
308
                                }
309
                        } catch (IOException e) {
×
310
                                throw new UroborosqlRuntimeException("Failed to load SQL template[" + targetFilePath + "].", e);
×
311
                        }
1✔
312
                }
313

314
                if (sql == null) {
1✔
315
                        throw new UroborosqlRuntimeException("SQL template could not found.[" + targetFilePath + "]");
1✔
316
                }
317
                return sql;
1✔
318
        }
319

320
        /**
321
         * {@inheritDoc}
322
         *
323
         * @see jp.co.future.uroborosql.store.SqlLoader#existSql(java.lang.String)
324
         */
325
        @Override
326
        public boolean existSql(final String fileName) {
327
                return Thread.currentThread().getContextClassLoader().getResource(getFilePath(fileName)) != null;
1✔
328
        }
329

330
        /**
331
         * ファイルの絶対パスを取得
332
         *
333
         * @param filePath ルートパスからの相対パス(拡張子はつけない)
334
         * @return 絶対パス(拡張子付)
335
         */
336
        private String getFilePath(final String filePath) {
337
                return loadPath + PATH_SEPARATOR + filePath + fileExtension;
1✔
338
        }
339

340
        /**
341
         * SQL名作成<br>
342
         *
343
         * @param packageName パッケージ名
344
         * @param filePath ルートパスからの相対パス
345
         * @return
346
         */
347
        private String makeSqlName(final StringBuilder packageName, final String filePath) {
348
                if (packageName.length() == 0) {
1✔
349
                        return trimSqlExtension(filePath);
×
350
                } else {
351
                        return new StringBuilder(packageName).append(PATH_SEPARATOR).append(trimSqlExtension(filePath)).toString();
1✔
352
                }
353
        }
354

355
        /**
356
         * SQL定義ファイルの拡張子を除く<br>
357
         *
358
         * @param filePath ルートパスからの相対パス
359
         * @return 拡張子を除去した文字列
360
         */
361
        private String trimSqlExtension(final String filePath) {
362
                return StringUtils.removeEnd(filePath, fileExtension);
1✔
363
        }
364

365
        /**
366
         * パッケージ名作成<br>
367
         *
368
         * @param packageName パッケージ名
369
         * @param dir 対象ディレクトリ
370
         * @return パッケージ名にディレクトリを付与したStringBuilder
371
         */
372
        private StringBuilder makeNewPackageName(final StringBuilder packageName, final File dir) {
373
                if (packageName.length() == 0) {
1✔
374
                        return new StringBuilder(dir.getName());
1✔
375
                } else {
376
                        return new StringBuilder(packageName).append(PATH_SEPARATOR).append(dir.getName());
1✔
377
                }
378
        }
379

380
        /**
381
         * ファイル読み込み<br>
382
         *
383
         * 以下2種類に対応
384
         * <ol>
385
         * <li>キャッシュ時のディレクトリからFileの読み込む場合</li>
386
         * <li>キャッシュなしで クラスパス内のリソース(jarファイル内も含む)を読み込む場合</li>
387
         * </ol>
388
         *
389
         * @param reader リーダ
390
         * @return SQL文を読み込み 文末のSQLコマンド分割文字(; または / )を除いた文字列を返す
391
         * @throws IOException
392
         */
393
        private String read(final BufferedReader reader) throws IOException {
394
                StringBuilder sqlBuilder = new StringBuilder();
1✔
395
                try {
396
                        for (String line : reader.lines().toArray(String[]::new)) {
1✔
397
                                sqlBuilder.append(line).append(System.lineSeparator());
1✔
398
                        }
399
                } finally {
400
                        if (reader != null) {
1✔
401
                                reader.close();
1✔
402
                        }
403
                }
404

405
                return sqlBuilder.toString();
1✔
406
        }
407

408
        /**
409
         * SQL文末尾の"/"を取り除く<br>
410
         *
411
         * @param sql SQL文字列
412
         * @return トリム後文字列
413
         */
414
        private String trimSlash(final String sql) {
415
                String trimmedSql = sql.trim();
1✔
416
                if (trimmedSql.endsWith(PATH_SEPARATOR) && !trimmedSql.endsWith("*/")) {
1✔
417
                        return StringUtils.removeEnd(trimmedSql, PATH_SEPARATOR);
1✔
418
                } else {
419
                        return sql;
1✔
420
                }
421
        }
422

423
}
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

© 2026 Coveralls, Inc