shikeying
2024-01-11 3b67e947e36133e2a40eb2737b15ea375e157ea0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
package com.walker.infrastructure.utils;
 
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
 
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
 
/**
 * 语句分段读取器,它能根据关键词分段读出中间的内容。</p>
 * 你可以设置多个分段关键词让分析器来处理,例如:
 * <pre>
 * I am a boy, but my sister is a good girl!</br>
 * 你可以设置关键词对来解析内容:[I,boy],[but ,girl!]
 * 经过这两个关键词分析后,会返回以下结果:</br>
 * [ am a],[ my sister is a good ]
 * </pre>
 * @author shikeying
 * @date 2013-7-17
 *
 */
public class SegmentReader {
 
    /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     * 定义组件的选项开关
     * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
    
    /* 关键词是否大小写敏感,默认否 */
    private boolean keyCaseSensitive = false;
    
    public boolean isKeyCaseSensitive() {
        return keyCaseSensitive;
    }
 
    public void setKeyCaseSensitive(boolean keyCaseSensitive) {
        this.keyCaseSensitive = keyCaseSensitive;
    }
 
    /* 返回结果中是否包含关键词,默认不包含 */
    private boolean contentIncludeKey = false;
    
    /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     * 定义组件内部私有变量
     * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
    
    public boolean isContentIncludeKey() {
        return contentIncludeKey;
    }
 
    public void setContentIncludeKey(boolean contentIncludeKey) {
        this.contentIncludeKey = contentIncludeKey;
    }
 
    private Logger logger = LoggerFactory.getLogger(this.getClass());
    
    /* 最终分析过后的字符串内容 */
    private final StringBuilder totalResult = new StringBuilder(512);
    
    /* 解析后的最终结果,可以通过关键词对来查找对应结果 */
//    private final Map<KeyExpression, StringBuilder> result = new HashMap<KeyExpression, StringBuilder>(2);
    private final Map<KeyExpression, List<StringBuilder>> result2 = new HashMap<KeyExpression, List<StringBuilder>>(2);
    
    /* 用户添加的所有关键词信息 */
    private final List<KeyExpression> keys = new ArrayList<KeyExpression>(2);
    
    /* 开始关键词与结束关键词对应,便于快速查找 */
    private final Map<String, String> startEndKeys = new HashMap<String, String>(2);
    
    private int maxStartKeyLength, maxEndKeyLength = 0;
    
    /* 当前已经扫描过入栈字符 */
    private LinkedList<Character> scanStack = new LinkedList<Character>();
    
    /* 已入栈的关键词 */
    private LinkedList<String> existKeysStack = new LinkedList<String>();
    
    /* 关键词排除的字符集合,即有些关键词不能出现在特定的字符中 */
    /* 例如SQL语句中关键词分号不能在''中,有些字符串中也会包含关键词 */
    /* 这些被排除的字符都是成对出现的。 */
    public static final Set<Character> keyExcludedCharSet = new HashSet<Character>();
    
    static {
        keyExcludedCharSet.add('\'');
    }
    
    private LinkedList<Character> keyExcludedCharStack = new LinkedList<Character>();
    
    /** ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     * 
     *  组件暴露方法调用
     * 
     ** ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
    
    /**
     * 读取给定的语句,组件准备分析
     * @param sentence
     * @return 返回分析过的最终完整的语句内容
     */
    public String read(String sentence){
        assert (sentence != null && !sentence.equals(""));
        if(keys.size() == 0)
            throw new IllegalStateException("keys is required.");
        
        getMaxKeyLength();
        for(KeyExpression ke : keys){
//            result.put(ke, new StringBuilder(512));
            result2.put(ke, new ArrayList<StringBuilder>());
            startEndKeys.put(ke.getStartKey(), ke.getEndKey());
        }
        
        for(char c : (keyCaseSensitive ? sentence.toCharArray() : sentence.toLowerCase().toCharArray())){
            readOneCharacter(c, false);
        }
        
        /**
         * 修复bug(bug20131202-1):如果把语句整体转换成小写,可能改变原始内容,例如:SQL语句中ID字段</br>
         * 会全部为小写,这样就影响了业务对数据的使用。所以不能转换小写。
         */
//        for(char c : sentence.toCharArray()){
//            readOneCharacter(c, false);
//        }
        
        /* 处理最后一个读取字符问题 */
        readOneCharacter(scanStack.peekLast(), true);
        
        return totalResult.toString();
    }
    
    /**
     * 返回关键词对应的处理结果,
     * @param startKey 开始关键词
     * @param endKey 结束关键词
     * @return
     */
    public String getSolvedContent(String startKey, String endKey){
        KeyExpression ke = new KeyExpression(startKey, endKey);
//        StringBuilder sb = this.result.get(ke);
        List<StringBuilder> sb = this.result2.get(ke);
        return sb == null ? null : sb.size() == 0 ? null : sb.get(0).toString();
    }
    
    /**
     * 根据关键词,返回该关键词包含的分析结果列表。</br>
     * 输入的关键词不能嵌套,是平行关系。如下示例:
     * <pre>
     * select ... from ... (select * from ...).
     * select是开始,from是结束关键词
     * </pre>
     * @param startKey
     * @param endKey
     * @return
     */
    public List<StringBuilder> getSolvedList(String startKey, String endKey){
        return this.result2.get(new KeyExpression(startKey, endKey));
    }
    
    /**
     * 关键词分析后的结果处理模式: 存储
     */
    public static final int RESULT_MODE_STORE     = 1;
 
    /**
     * 关键词分析后的结果处理模式: 删除
     */
    public static final int RESULT_MODE_REMOVE    = 2;
 
    /**
     * 关键词分析后的结果处理模式: 替换内容
     */
    public static final int RESULT_MODE_REPLACE   = 3;
 
    /**
     * 关键词分析后的结果处理模式: 通过回调接口来处理
     */
    public static final int RESULT_MODE_CALLBACK = 4;
 
    /**
     * 添加默认的关键词,系统默认会返回解析的结果
     * @param startKey 开始关键词
     * @param endKey 结束关键词,可以没有
     */
    public void addKey(String startKey, String endKey){
        addKey(startKey, endKey, RESULT_MODE_STORE);
    }
    
    /**
     * 添加关键词,删除行为。即:关键词之间的内容被删除
     * @param startKey
     * @param endKey
     */
    public void addRemoveKey(String startKey, String endKey){
        addKey(startKey, endKey, RESULT_MODE_REMOVE);
    }
    
    /**
     * 添加关键词,替换行为。即:关键词之间内容会被替换
     * @param startKey
     * @param endKey
     * @param replace 要替换的内容
     */
    public void addReplaceKey(String startKey, String endKey, String replace){
        addKey(startKey, endKey, RESULT_MODE_REPLACE, replace);
    }
    
    /**
     * 添加关键词,回调行为。即:关键词之间内容会被回调接口继续调用,最终结果仍会返回
     * @param startKey
     * @param endKey
     * @param callback 用户自定义实现的回调实现
     */
    public void addCallbackKey(String startKey, String endKey, CallBack callback){
        addKey(startKey, endKey, RESULT_MODE_CALLBACK, callback);
    }
    
    
    /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     * 组件内部私有方法
     * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
    
    private void addKey(String startKey, String endKey, int mode, Object ...others){
        assert (startKey != null && !startKey.equals(""));
        if(!keyCaseSensitive){
            startKey = startKey.toLowerCase();
            endKey = (endKey == null ? null : endKey.toLowerCase());
        }
        
        for(KeyExpression ke : keys){
            if(ke.getStartKey().equals(startKey))
                throw new IllegalArgumentException("设置的开始关键字重复, key = " + startKey);
        }
        
        KeyExpression ke = new KeyExpression(startKey, endKey);
        
        if(mode == RESULT_MODE_REPLACE || mode == RESULT_MODE_CALLBACK){
            if(others == null || others.length > 1)
                throw new IllegalArgumentException("argument is required in mode 'REPLACE' and 'CALLBACK'.");
            if(mode == RESULT_MODE_REPLACE){
                if(!(others[0] instanceof String))
                    throw new IllegalArgumentException("argument of replace must be String.");
                else
                    ke.setResultMode(RESULT_MODE_REPLACE)
                    .setReplace(others[0].toString());
            }
            if(mode == RESULT_MODE_CALLBACK){
                if(!(others[0] instanceof CallBack))
                    throw new IllegalArgumentException("argument of callback must be CallBack.");
                else
                    ke.setResultMode(RESULT_MODE_CALLBACK)
                    .setCallBack((CallBack)others[0]);
            }
        } else if(mode == RESULT_MODE_REMOVE){
            ke.setResultMode(RESULT_MODE_REMOVE);
        }
        keys.add(ke);
    }
    
    private void getMaxKeyLength(){
        int skSize = 0;
        int ekSize = 0;
        for(KeyExpression ke : keys){
            int k1 = ke.getStartKey().length();
            if(k1 > skSize){
                skSize = k1;
            }
            if(ke.getEndKey() != null && ke.getEndKey().length() > ekSize){
                ekSize = ke.getEndKey().length();
            }
        }
        this.maxStartKeyLength = skSize;
        this.maxEndKeyLength = ekSize;
        logger.debug("maxStartKeyLength = " + maxStartKeyLength);
        logger.debug("maxEndKeyLength = " + maxEndKeyLength);
    }
    
    private char previousRead;
    
    private void readOneCharacter(char c, boolean isLastRead){
        /* 读到空格,而且上一个也是空格,忽略 */
        if(c == 32 && previousRead == 32) return;
        
        checkKeyExcludedChar(c);
        
//        String scaned = getScanedChars().trim();
        String scaned = getScanedChars();
//        System.out.println("已扫描内容 = " + scaned);
        
        doMatchedInScaned(scaned);
        
        if(!isLastRead){
            scanStack.addLast(c);
            previousRead = c;
        } else {
            // 读到最后一个字符了,如果栈中存在内容就输出
            this.totalResult.append(getScanedChars());
        }
    }
    
    /**
     * 栈中是否存在被排除的字符,如果存在返回<code> true </code>
     * @return
     */
    private boolean inExcludedCharStack(){
        return !keyExcludedCharStack.isEmpty();
    }
    
    /**
     * 检查输入的字符是否是排除的字符
     * @param c
     */
    private void checkKeyExcludedChar(char c){
        if(keyExcludedCharSet.contains(c)){
            Character existInStack = this.keyExcludedCharStack.peekLast();
            if(existInStack != null && c == existInStack){
//                logger.debug("读入的字符在排除字符栈中已经存在,配对成功, 可以清除了. char = " + c);
                keyExcludedCharStack.pollLast();
            } else {
//                logger.debug("读入的字符是要排除的字符,但栈顶并没有匹配的,直接加入栈顶");
                keyExcludedCharStack.offerLast(c);
            }
        }
    }
    
    /**
     * 在已经扫描的字符串中,是否存在匹配的关键词
     * @return
     */
    private boolean doMatchedInScaned(String scaned){
        String findStartKey = doMatchStartKey(scaned);
        if(findStartKey != null){
            /* 在清空扫描栈之前,需要保存到关键词对应变量中 */
            String savedData = null;
            String savedKey = null;
            if(!scaned.equals(findStartKey)) {
//                savedData = this.contentIncludeKey ? scaned : scaned.replaceAll(findStartKey, "");
                
                //2017-06-05 时克英修改,对于单符号关键词,必须用转义表示正则表达式
                if(findStartKey.equals("{") || findStartKey.equals("[")){
                    savedData = this.contentIncludeKey ? scaned : scaned.replaceAll("\\\\" + findStartKey, "");
                } else {
                    savedData = this.contentIncludeKey ? scaned : scaned.replaceAll(findStartKey, "");
                }
            }
            savedKey = existKeysStack.peekLast();
            if(savedKey != null){
                int index = storedKeyCounter.get(savedKey);
                List<StringBuilder> sbs = result2.get(new KeyExpression(savedKey
                        , startEndKeys.get(savedKey)));
                sbs.get(index).append(savedData == null ? "" : savedData);
//                result.get(new KeyExpression(savedKey
//                        , startEndKeys.get(savedKey))).append(savedData == null ? "" : savedData);
            }
            
            existKeysStack.offerLast(findStartKey);
            scanStack.clear();
            return true;
        }
        
        String existStartKey = existKeysStack.peekLast();
        String endKey = existStartKey == null ? null : startEndKeys.get(existStartKey);
        
        String matchedEndKey = doMatchEndKey(scaned, existStartKey, endKey);
        if(matchedEndKey != null){
            existKeysStack.pollLast();
            scanStack.clear();
            return true;
        }
        return false;
    }
    
    private String doMatchEndKey(String scaned, String startKey, String endKey){
        int _ss = scaned.length();
        int size = 0;
        String cbCall = null;
        for(KeyExpression ke : keys){
            if(ke.getEndKey() == null) continue;
            if(!ke.getStartKey().equalsIgnoreCase(startKey)) continue; // 必须是与开始匹配的结束,否则不比较
            size = ke.getEndKey().length();
//            if(_ss >= size && scaned.indexOf(ke.getEndKey()) >= 0){
            if(_ss >= size && scaned.endsWith(ke.getEndKey()) && !inExcludedCharStack()){
                if(ke.getResultMode() == RESULT_MODE_STORE){
                    writeResult2(scaned, ke);
                    
                } else if(ke.getResultMode() == RESULT_MODE_REMOVE){
                    if(this.contentIncludeKey)
                        totalResult.append(ke.getEndKey());
                    
                } else if(ke.getResultMode() == RESULT_MODE_REPLACE){
                    writeResult2(ke.getReplace(), ke);
                    
                } else if(ke.getResultMode() == RESULT_MODE_CALLBACK){
                    cbCall = ke.getCallBack().afterSegment(scaned, ke.getStartKey(), ke.getEndKey());
                    writeResult2(cbCall, ke);
                }
                return ke.getEndKey();
            }
        }
        return null;
    }
    
    private Map<String, Integer> storedKeyCounter = new HashMap<String, Integer>(2);
    
    private String doMatchStartKey(String scaned){
        int _ss = scaned.length();
        int size = 0;
        String startKey = null;
        String previousKey = null; // 栈中保存的开始关键词
        for(KeyExpression ke : keys){
            startKey = ke.getStartKey();
            size = ke.getStartKey().length();
            if(_ss >= size && scaned.endsWith(startKey)){
                /* 如果后续字符串中包含了开始关键词,我们并不处理,因为主要通过单词来区分而不是字符 */
                previousKey = existKeysStack.peekLast();
                if(previousKey != null && previousKey.equals(startKey))
                    continue;
                
                /* 删除关键词优先处理,如果发现栈里面已经存在关键词(开始) */
                /* 并且是一个删除类型,那么后面嵌套的关键词就不再处理。 */
                if(hasRemovedKeyInStack()) continue;
                
                /* 遇到内容中存在多处相同的关键词时,我们需要同时保留这些分析结果 */
                if(ke.getResultMode() != RESULT_MODE_REMOVE){
                    Integer i = storedKeyCounter.get(startKey);
                    if(i == null){
                        i = 0;
                        storedKeyCounter.put(startKey, i);
                        logger.debug("已存储的key '" + startKey + "' i=0.");
                    } else {
                        storedKeyCounter.put(startKey, ++i);
                        logger.debug("已存储的key '" + startKey + "' i=" + i);
                    }
                    List<StringBuilder> sbs = result2.get(new KeyExpression(startKey
                            , startEndKeys.get(startKey)));
                    sbs.add(i, new StringBuilder());
                    logger.debug("key '" + startKey + "' 创建了第" + i + "个StringBuilder.");
                }
                /*--------------------------------------------------*/
                
                if(this.contentIncludeKey){
                    totalResult.append(scaned);
                } else {
//                    totalResult.append(scaned.replaceAll(ke.getStartKey(), ""));
                    if(scaned.endsWith(ke.getStartKey())){
                        int indx = scaned.length() - ke.getStartKey().length();
                        totalResult.append(scaned.substring(0, indx));
                    } else
                        totalResult.append(scaned);
                }
                return ke.getStartKey();
            }
        }
        return null;
    }
    
    private void writeResult2(String scaned, KeyExpression ke){
        writeResult2(scaned, ke, true);
    }
    private void writeResult2(String scaned, KeyExpression ke, boolean includeTotalResult){
        int index = storedKeyCounter.get(ke.getStartKey());
        if(this.contentIncludeKey){
            result2.get(ke).get(index).append(scaned);
            if(includeTotalResult)
                totalResult.append(scaned);
        } else {
//            result2.get(ke).get(index).append(scaned.replaceAll(, ""));
//            totalResult.append(scaned.replaceAll(ke.getEndKey(), ""));
            int eIndx = scaned.length() - ke.getEndKey().length();
            result2.get(ke).get(index).append(scaned.substring(0, eIndx));
            if(includeTotalResult)
                totalResult.append(scaned.substring(0, eIndx));
        }
    }
    
    /**
     * 在关键词栈中已经存在了"删除类型的关键词"
     * @return 如果存在返回<code>true</code>
     */
    private boolean hasRemovedKeyInStack(){
        String _existKey = existKeysStack.peekLast();
        if(_existKey == null) return false;
        KeyExpression _k = new KeyExpression(_existKey, startEndKeys.get(_existKey));
        for(KeyExpression ke : keys){
            if(ke.equals(_k) && ke.getResultMode() == RESULT_MODE_REMOVE){
                return true;
            }
        }
        return false;
    }
    
    private String getScanedChars(){
        StringBuilder sb = new StringBuilder(8);
        for(char c : scanStack){
            sb.append(c);
        }
        return sb.toString();
    }
    
    /**
     * 定义关键词表达式对象,内部用来处理关键词的各种属性和逻辑</br>
     * 这是封装的一个好处,系统通过此对象来完成对关键词的各种操作。
     * @author shikeying
     *
     */
    private class KeyExpression {
        private String startKey;
        private String endKey;
        private int resultMode = RESULT_MODE_STORE;
        private CallBack callBack;
        private String replace;
        
        public KeyExpression(String startKey, String endKey){
            assert (startKey != null && !startKey.equals(""));
            if(!keyCaseSensitive){
                this.startKey = startKey.toLowerCase();
                this.endKey = (endKey == null ? null : endKey.toLowerCase());
            } else {
                this.startKey = startKey;
                this.endKey = endKey;
            }
        }
        
        public String getStartKey() {
            return startKey;
        }
 
        public String getEndKey() {
            return endKey;
        }
 
        public String getReplace() {
            return replace;
        }
 
        public CallBack getCallBack() {
            return callBack;
        }
 
        public int getResultMode() {
            return resultMode;
        }
 
        public KeyExpression setResultMode(int mode){
            this.resultMode = mode;
            return this;
        }
        
        public KeyExpression setReplace(String replace){
            this.replace = replace;
            return this;
        }
        public KeyExpression setCallBack(CallBack callback){
            this.callBack = callback;
            return this;
        }
        
        public int hashCode(){
            return (31 + 13*this.startKey.hashCode() 
                    + (this.endKey == null ? 0 : this.endKey.hashCode()*13));
        }
        
        public boolean equals(Object o){
            if(o == null) return false;
            if(o instanceof KeyExpression){
                KeyExpression ke = (KeyExpression)o;
                if(ke == this) return true;
                if(ke.startKey.equals(this.startKey)){
                    return (ke.endKey == null ? 
                            (this.endKey == null ? true : false) 
                            : (this.endKey != null && this.endKey.equals(ke.endKey) ? true : false));
                }
            }
            return false;
        }
        
        public String toString(){
            return new StringBuilder().append("{skey=").append(startKey)
                    .append(", ekey=").append(endKey)
                    .append(", mode=").append(resultMode)
                    .append(", replace=").append(replace)
                    .append(", callback=").append(callBack==null ? "" : callBack.getClass().getName())
                    .append("}").toString();
        }
    }
    
    /**
     * 分段语句处理回调函数,用户可以自定义实现系统分析后的结果
     * @author shikeying
     *
     */
    protected interface CallBack{
        String afterSegment(String segmentResult, String keyStart, String keyEnd);
    }
    
    /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     * test method
     * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
    void print(){
        System.out.println("previousRead = " + (int)previousRead);
        System.out.println("scanStack = " + scanStack.toArray());
        for(char c : scanStack)
            System.out.print(c);
    }
    
    public static void main(String[] args){
//        SegmentReader sr = new SegmentReader();
//        sr.addKey("boy", "girl");
//        sr.addKey("must ", null);
//        String result = sr.read("I am a boy, but my sister is     a good girl.");
//        sr.print();
//        System.out.println("========= result ======== \t");
//        System.out.println(result);
//        System.out.println("========= getKeys(boy,girl) ======== \t");
//        System.out.println(sr.getSolvedContent("boy", "girl"));
        
        StringBuilder test = new StringBuilder();
        test.append("OPEN out_cursor FOR\r\n");
        test.append("--查询电子钱包消费和月票消费\r\n");
        test.append("SELECT cardtype,\r\n");
        test.append("NVL (SUM (operno), 0) AS operno,\r\n");
        test.append("NVL (SUM (viceopermn), 0) AS viceopermn\r\n");
        test.append("to_char('sum',10,90) end");
        test.append("FROM (                              --电子钱包和月票钱包\r\n");
        test.append("select * FROM TABLE t WHERE opdt >= TO_DATE(prmsdate, 'YYYY-MM-DD HH24:MI;SS'));");
        test.append("ELSE \r\n");
        test.append("OPEN out_cursor FOR querysql;\r\n");
        System.out.println(test);
        System.out.println("--------- start... ---------");
        
        SegmentReader sr2 = new SegmentReader();
        sr2.setKeyCaseSensitive(true);
        sr2.addKey("OPEN out_cursor FOR", ";");
        sr2.addRemoveKey("--", "\r\n");
        sr2.addKey("to_char", "end");
        System.out.println(sr2.read(test.toString()));
        System.out.println("------------------");
        System.out.println(sr2.getSolvedList("OPEN out_cursor FOR", ";"));
        System.out.println("##########");
        System.out.println(sr2.getSolvedContent("to_char", "end"));
        
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        String srcSql = "select * from s_user_core where id = {uid}";
        SegmentReader sr = new SegmentReader();
//        sr.setKeyCaseSensitive(false);
        sr.addKey("{", "}");
        System.out.println(sr.read(srcSql));
        List<StringBuilder> paramList = sr.getSolvedList("{", "}");
        System.out.println(paramList);
    }
}