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

future-architect / uroborosql / #771

12 Sep 2024 03:50PM UTC coverage: 90.374% (-0.8%) from 91.197%
#771

Pull #331

HidekiSugimoto189
Merge branch 'feature/add_new_dialect' of https://github.com/future-architect/uroborosql into feature/add_new_dialect
Pull Request #331: add oracle v12-23 and Mariadb 5,10 dialect

497 of 703 new or added lines in 42 files covered. (70.7%)

9 existing lines in 7 files now uncovered.

8928 of 9879 relevant lines covered (90.37%)

0.9 hits per line

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

84.17
/src/main/java/jp/co/future/uroborosql/event/subscriber/DumpResultEventSubscriber.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.event.subscriber;
8

9
import java.io.UnsupportedEncodingException;
10
import java.nio.ByteBuffer;
11
import java.nio.CharBuffer;
12
import java.nio.charset.CharacterCodingException;
13
import java.nio.charset.Charset;
14
import java.nio.charset.CoderResult;
15
import java.nio.charset.CodingErrorAction;
16
import java.sql.ResultSet;
17
import java.sql.SQLException;
18
import java.util.ArrayList;
19
import java.util.HashMap;
20
import java.util.Map;
21

22
import org.slf4j.Logger;
23

24
import jp.co.future.uroborosql.event.AfterSqlQueryEvent;
25
import jp.co.future.uroborosql.log.support.EventLoggingSupport;
26
import jp.co.future.uroborosql.utils.ObjectUtils;
27

28
/**
29
 * 実行結果をダンプ出力するイベントサブスクライバ.<br>
30
 *
31
 * このイベントサブスクライバを使用する際は、PreparedStatementを生成する際、ResultSetTypeに
32
 * <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code> または<code>ResultSet.TYPE_SCROLL_SENSITIVE</code>
33
 * を指定してください。
34
 *
35
 * @author H.Sugimoto
36
 * @since v1.0.0
37
 *
38
 */
39
public class DumpResultEventSubscriber extends EventSubscriber implements EventLoggingSupport {
1✔
40
        /** 改行文字 */
41
        private static final String LINE_SEPARATOR = System.lineSeparator();
1✔
42

43
        /** ロガー */
44
        private static final Logger EVENT_LOG = EventLoggingSupport.getEventLogger("dumpresult");
1✔
45

46
        /** 文字数計算用のエンコーディング */
47
        private static final String ENCODING_SHIFT_JIS = "Shift-JIS";
48

49
        /**
50
         *
51
         * {@inheritDoc}
52
         *
53
         * @see jp.co.future.uroborosql.event.subscriber.EventSubscriber#initialize()
54
         */
55
        @Override
56
        public void initialize() {
57
                afterSqlQueryListener(this::afterSqlQuery);
1✔
58
        }
1✔
59

60
        void afterSqlQuery(final AfterSqlQueryEvent evt) {
61
                try {
62
                        if (evt.getResultSet().getType() == ResultSet.TYPE_FORWARD_ONLY) {
1✔
NEW
63
                                warnWith(EVENT_LOG)
×
NEW
64
                                                .log("ResultSet type is TYPE_FORWARD_ONLY. DumpResultEventSubscriber use ResultSet#beforeFirst(). Please Set TYPE_SCROLL_INSENSITIVE or TYPE_SCROLL_SENSITIVE.");
×
65
                        }
66
                        debugWith(EVENT_LOG)
1✔
67
                                        .log(() -> displayResult(evt.getResultSet()).toString());
1✔
68
                } catch (SQLException ex) {
×
NEW
69
                        warnWith(EVENT_LOG)
×
NEW
70
                                        .setMessage(ex.getMessage())
×
NEW
71
                                        .setCause(ex)
×
NEW
72
                                        .log();
×
73
                }
1✔
74
        }
1✔
75

76
        /**
77
         * 検索結果を表示
78
         *
79
         * @param rs 検索結果のResultSet
80
         * @return 表示文字列
81
         */
82
        private StringBuilder displayResult(final ResultSet rs) {
83
                try {
84
                        var keys = new ArrayList<String>();
1✔
85
                        var maxLengthList = new HashMap<String, Integer>();
1✔
86
                        var rsmd = rs.getMetaData();
1✔
87
                        var columnCount = rsmd.getColumnCount();
1✔
88
                        for (var i = 1; i <= columnCount; i++) {
1✔
89
                                var columnLabel = rsmd.getColumnLabel(i);
1✔
90
                                keys.add(columnLabel);
1✔
91
                                maxLengthList.put(columnLabel, getByteLength(columnLabel));
1✔
92
                        }
93

94
                        var rows = new ArrayList<Map<String, Object>>();
1✔
95

96
                        while (rs.next()) {
1✔
97
                                var data = new HashMap<String, Object>();
1✔
98

99
                                for (var key : keys) {
1✔
100
                                        var val = rs.getObject(key);
1✔
101
                                        data.put(key, val);
1✔
102

103
                                        var currentLength = getByteLength(val);
1✔
104
                                        maxLengthList.compute(key, (k, v) -> v < currentLength ? currentLength : v);
1✔
105
                                }
1✔
106
                                rows.add(data);
1✔
107
                        }
1✔
108

109
                        var builder = new StringBuilder(LINE_SEPARATOR);
1✔
110
                        // ヘッダ部出力
111
                        builder.append("+");
1✔
112
                        for (var key : keys) {
1✔
113
                                builder.append(ObjectUtils.repeat('-', maxLengthList.get(key))).append("+");
1✔
114
                        }
1✔
115
                        builder.append(LINE_SEPARATOR).append("|");
1✔
116
                        for (var key : keys) {
1✔
117
                                builder.append(fillHeader(key, maxLengthList.get(key))).append("|");
1✔
118
                        }
1✔
119
                        builder.append(LINE_SEPARATOR).append("+");
1✔
120
                        for (var key : keys) {
1✔
121
                                builder.append(ObjectUtils.repeat('-', maxLengthList.get(key))).append("+");
1✔
122
                        }
1✔
123

124
                        // データ部出力
125

126
                        if (rows.isEmpty()) {
1✔
127
                                builder.append(LINE_SEPARATOR).append("|");
1✔
128
                                var len = 1;
1✔
129
                                for (var key : keys) {
1✔
130
                                        len = len + maxLengthList.get(key) + 1;
1✔
131
                                }
1✔
132

133
                                if (len >= 13) {
1✔
134
                                        builder.append("empty data.").append(ObjectUtils.repeat(' ', len - 13)).append("|");
1✔
135
                                } else {
136
                                        builder.append("-").append(ObjectUtils.repeat(' ', len - 3)).append("|");
1✔
137
                                }
138
                        } else {
1✔
139
                                for (var row : rows) {
1✔
140
                                        builder.append(LINE_SEPARATOR).append("|");
1✔
141
                                        for (var key : keys) {
1✔
142
                                                builder.append(fillData(row.get(key), maxLengthList.get(key))).append("|");
1✔
143
                                        }
1✔
144
                                }
1✔
145

146
                        }
147
                        builder.append(LINE_SEPARATOR).append("+");
1✔
148
                        for (var key : keys) {
1✔
149
                                builder.append(ObjectUtils.repeat('-', maxLengthList.get(key))).append("+");
1✔
150
                        }
1✔
151

152
                        // カーソルを先頭の前に戻す
153
                        if (rs.getType() != ResultSet.TYPE_FORWARD_ONLY) {
1✔
154
                                rs.beforeFirst();
1✔
155
                        }
156

157
                        return builder;
1✔
158
                } catch (Exception ex) {
×
NEW
159
                        errorWith(EVENT_LOG)
×
NEW
160
                                        .setMessage(ex.getMessage())
×
NEW
161
                                        .setCause(ex)
×
NEW
162
                                        .log();
×
163
                }
164
                return null;
×
165
        }
166

167
        private String fillHeader(final String str, final int length) {
168
                var strLen = getByteLength(str);
1✔
169
                var spaceSize = (length - strLen) / 2;
1✔
170

171
                var spaceStr = ObjectUtils.repeat(' ', spaceSize);
1✔
172
                var ans = spaceStr + str + spaceStr;
1✔
173
                var fillLen = getByteLength(ans);
1✔
174
                if (length > fillLen) {
1✔
175
                        ans = ans + ObjectUtils.repeat(' ', length - fillLen);
1✔
176
                }
177
                return ans;
1✔
178
        }
179

180
        private String fillData(final Object val, final int length) throws CharacterCodingException,
181
                        UnsupportedEncodingException {
182
                var valLen = getByteLength(val);
1✔
183
                var spaceSize = length - valLen;
1✔
184

185
                if (val instanceof Number) {
1✔
186
                        return ObjectUtils.repeat(' ', spaceSize) + getSubstringByte(val, length);
1✔
187
                } else {
188
                        return getSubstringByte(val, length) + ObjectUtils.repeat(' ', spaceSize);
1✔
189
                }
190

191
        }
192

193
        /**
194
         * オブジェクトの文字列表現のバイト数(Shift-JIS換算)を取得する
195
         *
196
         * @param val 計算対象オブジェクト
197
         * @return バイト数。200バイトを超える場合は200を返す
198
         */
199
        private int getByteLength(final Object val) {
200
                if (val == null) {
1✔
201
                        return 4;
×
202
                }
203
                var str = val.toString();
1✔
204
                try {
205
                        var len = str.getBytes(ENCODING_SHIFT_JIS).length;
1✔
206
                        return len <= 200 ? len : 200;
1✔
207
                } catch (UnsupportedEncodingException ex) {
×
208
                        return 1;
×
209
                }
210
        }
211

212
        /**
213
         * 指定したバイト数で文字列をカットする
214
         *
215
         * @param obj 対象オブジェクト
216
         * @param capacity カットするバイト数
217
         * @return String
218
         * @throws CharacterCodingException
219
         * @throws UnsupportedEncodingException
220
         */
221
        private String getSubstringByte(final Object obj, final int capacity) throws CharacterCodingException,
222
                        UnsupportedEncodingException {
223
                var str = obj == null ? "null" : obj.toString();
1✔
224
                if (capacity < 1) {
1✔
225
                        return str;
×
226
                }
227

228
                var ce = Charset.forName(ENCODING_SHIFT_JIS).newEncoder()
1✔
229
                                .onMalformedInput(CodingErrorAction.REPLACE)
1✔
230
                                .onUnmappableCharacter(CodingErrorAction.REPLACE)
1✔
231
                                .reset();
1✔
232
                if (capacity >= ce.maxBytesPerChar() * str.length()) {
1✔
233
                        return str;
1✔
234
                }
235
                var cb = CharBuffer.wrap(new char[Math.min(str.length(), capacity)]);
1✔
236
                str.getChars(0, Math.min(str.length(), cb.length()), cb.array(), 0);
1✔
237

238
                if (capacity >= ce.maxBytesPerChar() * cb.limit()) {
1✔
239
                        return cb.toString();
×
240
                }
241
                var out = ByteBuffer.allocate(capacity);
1✔
242
                ce.reset();
1✔
243
                CoderResult cr = null;
1✔
244
                if (cb.hasRemaining()) {
1✔
245
                        cr = ce.encode(cb, out, true);
1✔
246
                } else {
247
                        cr = CoderResult.UNDERFLOW;
×
248
                }
249
                if (cr.isUnderflow()) {
1✔
250
                        cr = ce.flush(out);
1✔
251
                }
252
                return cb.flip().toString();
1✔
253
        }
254
}
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