OSDN Git Service

edafa42782524bd7847d2d3a00d06f9b4a999982
[jaxcel/jaxcel.git] / Jaxcel / src / org / hanei / jaxcel / parser / CellParser.java
1 /**
2  * Copyright 2014 Hanei Management Co.,Ltd. 
3  * 
4  * This file is part of Jaxcel
5  * 
6  *  Jaxcel is free software: you can redistribute it and/or modify
7  *  it under the terms of the GNU Lesser General Public License as published by
8  *  the Free Software Foundation, either version 3 of the License, or
9  *  (at your option) any later version.
10  *
11  *  Jaxcel is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU Lesser General Public License for more details.
15  *
16  *  You should have received a copy of the GNU Lesser General Public License
17  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 package org.hanei.jaxcel.parser;
20
21 import java.io.InputStream;
22 import java.text.CharacterIterator;
23 import java.text.StringCharacterIterator;
24 import java.util.ArrayDeque;
25 import java.util.Arrays;
26 import java.util.Deque;
27 import java.util.HashMap;
28 import java.util.Map;
29 import java.util.regex.Matcher;
30 import java.util.regex.Pattern;
31
32 import org.apache.commons.collections.ExtendedProperties;
33 import org.apache.poi.ss.usermodel.Cell;
34 import org.hanei.jaxcel.exception.JaxcelInputException;
35 import org.hanei.jaxcel.report.JaxcelContext;
36 import org.hanei.jaxcel.util.ExcelUtil;
37 import static org.hanei.jaxcel.parser.Constants.*;
38 import org.slf4j.LoggerFactory;
39 import org.slf4j.Logger;
40
41 /**
42  * Excelテンプレートシートのセルに記述された指示子(Template Language)、EL式(Expression Language)の検索、パースを行う
43  * 
44  * @since 1.01.00
45  * @author Noboru Saito
46  *
47  */
48 public class CellParser {
49
50         private static final Logger log = LoggerFactory.getLogger(CellParser.class);
51         
52         private static final int PARSE_MAX = 10;        // 同一セルパース最大回数
53
54         private ExtendedProperties config = new ExtendedProperties();
55         private Map<String, TLParser> parserMap = null;
56         private JaxcelContext context = null;
57         private Cell cell = null;
58
59         private boolean needReParse;    // 同一セル再パース要否フラグ
60         private int parseCount;                 // 同一セルパース回数
61
62         private char elDirective;
63         private char elStartBracket;
64         private char elEndBracket;
65         private char tlEndBracket;
66         private char tlSymbol;
67
68         private StringCharacterIterator valueIterator = new StringCharacterIterator("");
69         private Pattern parserPattern = null;
70         private Matcher parserMatches = null;
71
72         /**
73          * コンストラクタ
74          * 
75          * @param context       Jaxlsコンテキストオブジェクト
76          * 
77          * @throws JaxcelInputException デフォルトプロパティファイル入力例外発生時
78          */
79         public CellParser(JaxcelContext context) {
80
81                 // コンテキスト設定
82                 this.context = context;
83                 
84                 // デフォルトプロパティロード
85                 try(InputStream inStream = getClass().getResourceAsStream(DEFAULT_PROPS_NAME)) {
86                         config.load(inStream);
87                         log.debug("default properties load");
88                 } catch (Exception e) {
89                         throw new JaxcelInputException("default properties load error", e);
90                 }
91                 // 外部プロパティロード
92                 if(this.context != null) {
93                         ExtendedProperties customProperties = this.context.getCustomProperties();
94                         if(customProperties != null) {
95                                 // 日付・時刻書式
96                                 if(customProperties.containsKey(P_DATE_FORMAT))
97                                         config.addProperty(P_DATE_FORMAT, Arrays.asList(customProperties.getStringArray(P_DATE_FORMAT)));
98                                 if(customProperties.containsKey(P_TIME_FORMAT))
99                                         config.addProperty(P_TIME_FORMAT, Arrays.asList(customProperties.getStringArray(P_TIME_FORMAT)));
100                                 // テンプレート指示子
101                                 if(customProperties.containsKey(P_TL_DIRECTIVE)) {
102                                         for(String tlDirective : customProperties.getStringArray(P_TL_DIRECTIVE)) {
103                                                 config.addProperty(P_TL_DIRECTIVE, Arrays.asList(customProperties.getStringArray(P_TL_DIRECTIVE)));
104                                                 config.addProperty(String.format(P_TL_CLASS, tlDirective), customProperties.getString(String.format(P_TL_CLASS, tlDirective)));
105                                         }
106                                 }
107                         }
108                 }
109                 
110                 // パーサ ロード
111                 parserMap = new HashMap<>();
112                 try {
113                         // 日付・時刻書式
114                         context.setDateFormat(config.getStringArray(P_DATE_FORMAT));
115                         context.setTimeFormat(config.getStringArray(P_TIME_FORMAT));
116                         // EL式パーサ
117                         Class<?> cls = Class.forName(config.getString(P_EL_CLASS));
118                         TLParser parser = (TLParser) cls.newInstance();
119                         elDirective = config.getString(P_EL_DIRECTIVE).charAt(0);
120                         elStartBracket = config.getString(P_EL_BRACKET_START).charAt(0);
121                         elEndBracket   = config.getString(P_EL_BRACKET_END).charAt(0);
122                         parserMap.put(String.valueOf(elDirective) + String.valueOf(elStartBracket), parser);
123                         log.debug("parser load: {} ", cls.getSimpleName());
124
125                         // テンプレート指示子パーサ
126                         tlSymbol = config.getString(P_TL_SYMBOL).charAt(0);
127                         tlEndBracket = config.getString(P_TL_BRACKET_END).charAt(0);
128                         for(String tlDirective : config.getStringArray(P_TL_DIRECTIVE)) {
129                                 cls = Class.forName(config.getString(String.format(P_TL_CLASS, tlDirective)));
130                                 parser = (TLParser) cls.newInstance();
131                                 parserMap.put(tlSymbol + tlDirective + config.getString(P_TL_BRACKET_START), parser);
132                                 log.debug("parser load: {} ", cls.getSimpleName());
133                         }
134                 }
135                 catch(Exception e) {
136                         log.error("TLParser load error. {}", e.getMessage(), e);
137                 }
138
139                 // 正規表現Pattern生成
140                 String matchPattern = "";
141                 for(String key : parserMap.keySet()) {
142                         matchPattern += (matchPattern.length() > 0 ? "|" : "") + Pattern.quote(key);
143                 }
144                 log.debug("parser matchPattern: {}", matchPattern);
145                 parserPattern = Pattern.compile(matchPattern);
146         }
147         
148         /**
149          * セルのパース
150          * 
151          * @param cell  対象セル
152          * 
153          * @throws JaxcelInputException 入力例外発生時
154          */
155         public void parse(Cell cell) {
156                 log.trace("parse start");
157
158                 // チェック
159                 if(cell == null) {
160                         // 再パースフラグクリア
161                         this.cell = null;
162                         needReParse = false;            
163                         parseCount = 0;
164                         log.debug("cell is null");
165                         log.trace("parse end");
166                         return;
167                 }
168                 
169                 // パース初回の場合セルを保持
170                 if(this.cell == null) {
171                         log.debug("start new cell parse");
172                         this.cell = cell;
173                         // 再パースフラグ・カウントクリア
174                         needReParse = false;
175                         parseCount = 0;
176                 }
177                 // 保持しているセルと同一セルのパースの場合
178                 else if(ExcelUtil.equalsCellAddress(this.cell, cell, true)) {
179                         // 再パース回数チェック
180                         if(++parseCount >= PARSE_MAX) {
181                                 log.warn("parse repeat count over");
182                                 // 再パースフラグクリア
183                                 needReParse = false;
184                                 log.trace("parse end");
185                                 return;
186                         }
187                         log.debug("start repeat cell parse");
188                 }
189                 // 保持しているセルと異なるセルのパースの場合
190                 else {
191                         log.debug("start new cell parse");
192                         // セルを保持
193                         this.cell = cell;
194                         // 再パースフラグ・カウントクリア
195                         needReParse = false;
196                         parseCount = 0;
197                 }
198                                 
199                 // 文字列・計算式の場合は値保持。以外は終了
200                 String cellValue = null;
201                 switch(cell.getCellType()){
202                 case Cell.CELL_TYPE_STRING:
203                         cellValue = cell.getStringCellValue();
204                         log.debug("value: {}", cellValue);
205                         break;
206                 case Cell.CELL_TYPE_FORMULA:
207                         cellValue = cell.getCellFormula();
208                         log.debug("formula: {}", cellValue);
209                         break;
210                 default:
211                         log.debug("cell type is not string or )formula");
212                         // 再パースフラグクリア
213                         needReParse = false;
214                         log.trace("parse end");
215                         return;
216                 }
217
218                 // 解析開始
219                 TLParser parser = null;
220                 Map<String, Object> attributeMap = new HashMap<>();
221                 String matchParser = null;
222                 String attrKey = null;
223                 StringBuilder strBuf = new StringBuilder(1024);
224                 StringBuilder templateString = new StringBuilder(1024);
225                 Deque<Character> bracketStack = new ArrayDeque<>();
226                 boolean parserExist = false;
227                 boolean attrValueParsing;
228                 boolean expressionParsing;
229                 boolean attrKeyParsed;
230                 boolean escaping;
231                 
232                 // 指示子(TL)の検索
233                 parserMatches = parserPattern.matcher(cellValue);
234                 // 指示子検索ループ
235                 findLoop: while(parserMatches.find()) {
236                         matchParser = parserMatches.group();
237                         log.debug("match: {}", matchParser);
238                         
239                         // パーサ取得
240                         parser = parserMap.get(matchParser);
241                         if(parser == null) {
242                                 log.warn("parser is null");
243                                 continue;
244                         }
245                         log.debug("parser: {}", parser.getClass().getSimpleName());
246                         
247                         // クリア
248                         templateString.setLength(0);
249                         templateString.append(matchParser);
250                         strBuf.setLength(0);
251                         bracketStack.clear();
252                         attributeMap.clear();
253                         attrValueParsing  = false;
254                         expressionParsing = false;
255                         attrKeyParsed = false;
256                         escaping = false;
257
258                         // 指示子がEL式
259                         if(matchParser.equals(String.valueOf(elDirective) + String.valueOf(elStartBracket))) {
260                                 // elValueFlag ON
261                                 expressionParsing = true;
262                                 log.debug("expressionParsing: {}", expressionParsing);
263                                 // 括弧スタックに開始括弧push
264                                 bracketStack.offerFirst(elStartBracket);
265                         }
266                         
267                         // 開始括弧以降読込み
268                         valueIterator.setText(cellValue);
269                         for(char c = valueIterator.setIndex(parserMatches.start() + matchParser.length()); c != CharacterIterator.DONE; c = valueIterator.next()) {
270                                 // カーソル文字抜出し
271                                 templateString.append(c);
272                                 log.trace("current char: {}", c);
273                                 // EL式解析中
274                                 if(expressionParsing) {
275                                         log.trace("parsing EL");
276                                         // カーソル文字がEL式終端括弧であれば
277                                         if(elEndBracket == c) {
278                                                 // 括弧スタックpop
279                                                 bracketStack.pollFirst();
280                                                 // 括弧スタック空ならEL式解析終了
281                                                 if(bracketStack.size() == 0) {
282                                                         // Mapに登録
283                                                         attributeMap.put(EXPRESSION, getTrimString(strBuf));
284                                                         log.debug("attributeMap: {}", attributeMap);
285                                                         // 指示子がEL式なら解析終了
286                                                         if(matchParser.equals(String.valueOf(elDirective) + String.valueOf(elStartBracket))) {
287                                                                 log.trace("parse end");
288                                                                 parserExist = true;
289                                                                 break findLoop;
290                                                         }
291                                                         // 指示子がEL式以外なら
292                                                         else {
293                                                                 log.trace("EL parse end");
294                                                                 // フラグクリア
295                                                                 expressionParsing = false;
296                                                                 // バッファクリア
297                                                                 strBuf.setLength(0);
298                                                         }
299                                                 }
300                                                 // 括弧スタック空でないならバッファに保持
301                                                 else {
302                                                         strBuf.append(c);
303                                                         log.trace("parsing: {}", strBuf.toString());
304                                                 }
305                                         }
306                                         // EL式終端括弧以外
307                                         else {
308                                                 // EL式開始括弧
309                                                 if(elStartBracket == c) {
310                                                         // 括弧スタックにpush
311                                                         bracketStack.offerFirst(c);
312                                                         log.trace("bracketStack: {}", bracketStack.size());
313                                                 }
314                                                 // バッファに保持
315                                                 strBuf.append(c);
316                                                 log.trace("parsing: {}", strBuf.toString());
317                                         }
318                                 }
319                                 // 属性値解析中
320                                 else if(attrValueParsing) {
321                                         log.trace("parsing attrValue");
322                                         // カーソル文字がダブルクォートであれば
323                                         if(VALUE_BRACKET == c) {
324                                                 log.trace("escaping : {}", escaping);
325                                                 // エスケープ中なら
326                                                 if(escaping) {
327                                                         // 括弧とはみなさずバッファに保持
328                                                         strBuf.append(c);
329                                                         // エスケープフラグクリア
330                                                         escaping = false;
331                                                         log.trace("parsing: {}", strBuf.toString());
332                                                 }
333                                                 // エスケープ中でないなら
334                                                 else {
335                                                         // Mapに登録
336                                                         attributeMap.put(attrKey, strBuf.toString());
337                                                         log.trace("attributeMap: {}", attributeMap);
338                                                         // 属性値解析中フラグクリア
339                                                         attrValueParsing = false;
340                                                         // バッファクリア
341                                                         strBuf.setLength(0);
342                                                         attrKey = null;
343                                                 }
344                                         }
345                                         // カーソル文字がダブルクォートでないなら
346                                         else {
347                                                 log.trace("escaping : {}", escaping);
348                                                 // エスケープ中なら
349                                                 if(escaping) {
350                                                         // エスケープフラグクリア
351                                                         escaping = false;
352                                                 }
353                                                 // エスケープ中でなくエスケープ文字(\)なら
354                                                 else if(ESCAPE_CHAR == c) {
355                                                         // エスケープフラグON
356                                                         escaping = true;
357                                                 }
358                                                 // バッファに保持
359                                                 strBuf.append(c);
360                                                 log.trace("parsing: {}", strBuf.toString());
361                                         }
362                                 }
363                                 // カーソル文字がEL式指示字であれば
364                                 else if(elDirective == c) {
365                                         log.trace("parsing elDirective");
366                                         // 次の文字のチェック
367                                         c = valueIterator.next();
368                                         log.trace("next char: {}", c);
369                                         // 次の文字がEL式開始括弧であれば
370                                         if(elStartBracket == c) {
371                                                 templateString.append(c);
372                                                 // EL式解析中フラグセット
373                                                 expressionParsing = true;
374                                                 log.trace("expressionParsing : {}", expressionParsing);
375                                                 // バッファ空でなければ
376                                                 if(strBuf.length() > 0) {
377                                                         // Mapに登録(値はnull)
378                                                         attributeMap.put(strBuf.toString(), null);
379                                                         log.trace("attributeMap: {}", attributeMap);
380                                                 }
381                                                 // バッファクリア
382                                                 attrKey = null;
383                                                 strBuf.setLength(0);
384                                         }
385                                         // 次の文字がEL式開始括弧でなければ
386                                         else {
387                                                 // カーソルを戻しバッファに保持
388                                                 c = valueIterator.previous();
389                                                 log.trace("current char previous: {}", c);
390                                                 // カーソルを戻しバッファに保持
391                                                 strBuf.append(c);
392                                                 log.trace("parsing: {}", strBuf.toString());
393                                         }
394                                 }
395                                 // カーソル文字が属性・属性値結合子であれば
396                                 else if(ATTR_DELIMITER == c) {
397                                         log.trace("parsing attr delimiter");
398                                         // 属性文字列空でないなら
399                                         if(attrKey != null) {
400                                                 // 属性解析済みフラグセット
401                                                 attrKeyParsed = true;
402                                                 log.trace("attrKeyParsed: {}", attrKeyParsed);
403                                         }
404                                         // 属性文字列空なら
405                                         else {
406                                                 // バッファ空でなければ
407                                                 if(strBuf.length() > 0) {
408                                                         // 属性文字列保持
409                                                         attrKey = strBuf.toString();
410                                                         log.trace("mapKey: {}", attrKey);
411                                                         // Mapに登録(値はnull)
412                                                         attributeMap.put(attrKey, null);
413                                                         log.trace("attributeMap: {}", attributeMap);
414                                                         // バッファクリア
415                                                         strBuf.setLength(0);
416                                                         // 属性解析済みフラグセット
417                                                         attrKeyParsed = true;
418                                                         log.trace("attrKeyParsed: {}", attrKeyParsed);
419                                                 }
420                                                 // 上記以外
421                                                 else {
422                                                         // 解析異常終了
423                                                         log.warn("parse end");
424                                                         break;
425                                                 }
426                                         }
427                                 }
428                                 // カーソル文字がダブルクォートであれば
429                                 else if(VALUE_BRACKET == c) {
430                                         log.trace("parsing attr value bracket");
431                                         // 属性解析済みなら
432                                         if(attrKeyParsed == true && attrKey != null) {
433                                                 // 属性値解析中フラグセット
434                                                 attrValueParsing = true;
435                                                 attrKeyParsed = false;
436                                                 log.trace("attrValueParsing: {}", attrValueParsing);
437                                                 log.trace("attrKeyParsed: {}", attrKeyParsed);
438                                                 // バッファクリア
439                                                 strBuf.setLength(0);
440                                         }
441                                         // 上記以外
442                                         else {
443                                                 // 解析異常終了
444                                                 log.warn("parse end");
445                                                 break;
446                                         }
447                                 }
448                                 // 空白文字
449                                 else if(Character.isWhitespace(c) || EM_SPACE == c) {
450                                         log.trace("parsing white space");
451                                         // バッファ空でなければ
452                                         if(strBuf.length() > 0) {
453                                                 // Mapに登録(値はnull)
454                                                 attrKey = strBuf.toString();
455                                                 log.trace("mapKey: {}", attrKey);
456                                                 attributeMap.put(attrKey, null);
457                                                 log.trace("attributeMap: {}", attributeMap);
458                                                 // バッファクリア
459                                                 strBuf.setLength(0);
460                                         }
461                                 }
462                                 // 指示子閉じ括弧
463                                 else if(tlEndBracket == c) {
464                                         log.trace("parsing tlEndBracket");
465                                         // バッファ空でなければ
466                                         if(strBuf.length() > 0) {
467                                                 // Mapに登録(値はnull)
468                                                 attributeMap.put(strBuf.toString(), null);
469                                                 log.trace("attributeMap: {}", attributeMap);
470                                                 // バッファクリア
471                                                 attrKey = null;
472                                                 strBuf.setLength(0);
473                                         }
474                                         log.trace("parse end");
475                                         parserExist = true;
476                                         break findLoop;
477                                 }
478                                 // 上記以外
479                                 else {
480                                         // バッファに保持
481                                         strBuf.append(c);
482                                         log.trace("parsing: {}", strBuf.toString());
483                                 }
484                         }
485                 }
486
487                 log.trace("parser exist: {}", parserExist);
488                 // 指示子検索ヒット
489                 if(parserExist) {
490                         // コンテキスト設定
491                         context.setCurrentCell(this.cell);
492                         context.setTemplateString(templateString.toString());
493                         context.setAttributeMap(attributeMap);
494                         if(log.isDebugEnabled()) {
495                                 log.debug("templateString: {}", templateString.toString());
496                                 log.debug("attributeMap: {}", attributeMap);
497                         }
498                         try {
499                                 // パーサでのパース
500                                 parser.parse(context);
501
502                                 // パース後のセルタイプチェック
503                                 switch(this.cell.getCellType()){
504                                 case Cell.CELL_TYPE_STRING:
505                                 case Cell.CELL_TYPE_FORMULA:
506                                         // セルタイプが文字列、数式の場合は再パースフラグ設定
507                                         needReParse = true;
508                                         break;
509                                 default:
510                                         // 上記以外は再パースフラグクリア
511                                         needReParse = false;
512                                 }
513                         }
514                         catch(Exception e) {
515                                 log.error("parser.parse error. {}", e.getMessage(), e);
516                                 // 再パースフラグクリア
517                                 needReParse = false;
518                         }
519                 }
520                 // 検索ヒットせず
521                 else {
522                         log.debug("not found TL");
523                         // 再パースフラグクリア
524                         needReParse = false;
525                 }
526                 
527                 log.trace("parse end: needReParse: {} parseCount: {}", needReParse, parseCount);
528         }
529         
530         /**
531          * 同一セルの再パース要否を返却
532          * 
533          * @return 再パース必要であればはtrue。不要であればfalse
534          */
535         public boolean isReParseCell() {
536                 return needReParse;
537         }
538         
539         /**
540          * StringBuilderで保持している文字列のトリム
541          * 
542          * @param string 文字列
543          * @return トリム結果
544          */
545         private String getTrimString(StringBuilder string) {
546                 if(string == null) return "";
547
548                 int st = 0;
549                 String str = string.toString();
550                 char[] val = str.toCharArray();
551                 int len = val.length;
552                 
553                 while ((st < len) && (val[st] <= ' ' || val[st] == ' ')) {
554                     st++;
555                 }
556                 while ((st < len) && (val[len - 1] <= ' ' || val[len - 1] == ' ')) {
557                     len--;
558                 }
559                 return ((st > 0) || (len <  val.length)) ? str.substring(st, len) : str;
560         }
561 }