• R/O
  • HTTP
  • SSH
  • HTTPS

jaxcel: 提交


Commit MetaInfo

修訂74a70f97bed2a3f624d826d127651a146c9cc677 (tree)
時間2014-11-13 01:38:41
作者noboru saitoh <msnobosan@gmal...>
Commiternoboru saitoh

Log Message

#34479 対応中

Change Summary

差異

--- a/Jaxcel/.classpath
+++ b/Jaxcel/.classpath
@@ -24,6 +24,13 @@
2424 </classpathentry>
2525 <classpathentry kind="lib" path="lib/xmlbeans-2.6.0.jar"/>
2626 <classpathentry kind="lib" path="lib/commons-codec-1.9.jar"/>
27+ <classpathentry kind="lib" path="lib/commons-collections-3.2.1.jar"/>
28+ <classpathentry kind="lib" path="lib/dom4j-1.6.1.jar"/>
29+ <classpathentry kind="lib" path="lib/commons-logging-1.2.jar"/>
30+ <classpathentry kind="lib" path="lib/log4j-api-2.0.2.jar"/>
31+ <classpathentry kind="lib" path="lib/log4j-core-2.0.2.jar"/>
32+ <classpathentry kind="lib" path="lib/log4j-jcl-2.0.2.jar"/>
33+ <classpathentry kind="lib" path="lib/log4j-slf4j-impl-2.0.2.jar"/>
2734 <classpathentry kind="con" path="org.eclipse.jst.j2ee.internal.module.container"/>
2835 <classpathentry kind="con" path="org.eclipse.jst.server.core.container/org.eclipse.jst.server.tomcat.runtimeTarget/Apache Tomcat v7.0">
2936 <attributes>
Binary files /dev/null and b/Jaxcel/lib/commons-collections-3.2.1.jar differ
--- /dev/null
+++ b/Jaxcel/src/jaxcel.properties
@@ -0,0 +1,45 @@
1+#EL\u5f0f\u5b9a\u7fa9
2+el.class = org.hanei.jaxcel.parser.ELParser
3+el.directive = $
4+el.bracket.start = {
5+el.bracket.end = }
6+
7+#\u65e5\u4ed8\u30fb\u6642\u523b\u30d5\u30a9\u30fc\u30de\u30c3\u30c8\u5b9a\u7fa9
8+format.date = yyyy-MM-dd'T'HH:mm:ss.SSSXXX
9+format.date = yyyy-MM-dd'T'HH:mm:ss.SSXXX
10+format.date = yyyy-MM-dd'T'HH:mm:ss.SXXX
11+format.date = yyyy-MM-dd'T'HH:mm:ss.SSSXX
12+format.date = yyyy-MM-dd'T'HH:mm:ss.SSXX
13+format.date = yyyy-MM-dd'T'HH:mm:ss.SXX
14+format.date = yyyy-MM-dd'T'HH:mm:ss.SXXX
15+format.date = yyyy-MM-dd'T'HH:mm:ss.SXX
16+format.date = yyyy-MM-dd'T'HH:mm:ss.SX
17+format.date = yyyy-MM-dd'T'HH:mm:ssXXX
18+format.date = yyyy-MM-dd'T'HH:mm:ssXX
19+format.date = yyyy-MM-dd'T'HH:mm:ssX
20+format.date = yyyy-MM-dd'T'HH:mm:ss.S
21+format.date = yyyy-MM-dd'T'HH:mm:ss
22+format.date = yyyy-MM-dd'T'HH:mm
23+format.date = yyyy-MM-dd\ HH:mm:ss.S
24+format.date = yyyy-MM-dd\ HH:mm:ss
25+format.date = yyyy-MM-dd\ HH:mm
26+format.date = yyyy-MM-dd
27+format.date = yyyy/MM/dd\ HH:mm:ss.S
28+format.date = yyyy/MM/dd\ HH:mm:ss
29+format.date = yyyy/MM/dd\ HH:mm
30+format.date = yyyy/MM/dd
31+format.time = HH:mm:ss.S
32+format.time = HH:mm:ss
33+format.time = HH:mm
34+
35+#\u6307\u793a\u5b50\u5b9a\u7fa9
36+tl.directive = foreach
37+tl.directive = if
38+tl.bracket.start = (
39+tl.bracket.end = )
40+
41+#foreach
42+tl.foreach.class = org.hanei.jaxcel.parser.ForeachParser
43+
44+#if
45+tl.if.class = org.hanei.jaxcel.parser.IfParser
--- a/Jaxcel/src/org/hanei/jaxcel/example/MakeReportWithObject.java
+++ b/Jaxcel/src/org/hanei/jaxcel/example/MakeReportWithObject.java
@@ -33,7 +33,7 @@ import org.slf4j.LoggerFactory;
3333 /**
3434 * JavaオブジェクトからのExcel帳票出力サンプルクラス
3535 *
36- * @version 1.00.00
36+ * @since 1.00.00
3737 * @author Noboru Saito
3838 */
3939 public class MakeReportWithObject {
--- a/Jaxcel/src/org/hanei/jaxcel/exception/JaxcelInputException.java
+++ b/Jaxcel/src/org/hanei/jaxcel/exception/JaxcelInputException.java
@@ -19,10 +19,10 @@
1919 package org.hanei.jaxcel.exception;
2020
2121 /**
22- * Jaxcelインプット例外クラス<br>
22+ * Jaxcelインプット実行時例外クラス<br>
2323 * 引数チェックエラー、ファイルオープンエラー等発生時の例外
2424 *
25- * @version 1.00.00
25+ * @since 1.00.00
2626 * @author Noboru Saito
2727 */
2828 public class JaxcelInputException extends JaxcelRuntimeException {
--- a/Jaxcel/src/org/hanei/jaxcel/exception/JaxcelOutputException.java
+++ b/Jaxcel/src/org/hanei/jaxcel/exception/JaxcelOutputException.java
@@ -19,10 +19,10 @@
1919 package org.hanei.jaxcel.exception;
2020
2121 /**
22- * Jaxcelアウトプット例外クラス<br>
22+ * Jaxcelアウトプット実行時例外クラス<br>
2323 * ファイル出力、クローズエラー等発生時の例外
2424 *
25- * @version 1.00.00
25+ * @since 1.00.00
2626 * @author Noboru Saito
2727 */
2828 public class JaxcelOutputException extends JaxcelRuntimeException {
--- a/Jaxcel/src/org/hanei/jaxcel/exception/JaxcelRuntimeException.java
+++ b/Jaxcel/src/org/hanei/jaxcel/exception/JaxcelRuntimeException.java
@@ -19,9 +19,9 @@
1919 package org.hanei.jaxcel.exception;
2020
2121 /**
22- * Jaxcel例外基底クラス
22+ * Jaxcel実行時例外基底クラス
2323 *
24- * @version 1.00.00
24+ * @since 1.00.00
2525 * @author Noboru Saito
2626 */
2727 public class JaxcelRuntimeException extends RuntimeException {
--- /dev/null
+++ b/Jaxcel/src/org/hanei/jaxcel/parser/AbstractTLParser.java
@@ -0,0 +1,39 @@
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 org.hanei.jaxcel.report.JaxcelContext;
22+import org.slf4j.Logger;
23+import org.slf4j.LoggerFactory;
24+
25+/**
26+ * テンプレートパーサー抽象クラス<br>
27+ * テンプレートパーサーはこのクラスを継承する必要があります。
28+ *
29+ * @since 1.01.00
30+ * @author Noboru Saito
31+ */
32+public abstract class AbstractTLParser implements TLParser {
33+
34+ protected static final Logger log = LoggerFactory.getLogger(Thread.currentThread().getStackTrace()[1].getClassName());
35+
36+ @Override
37+ public abstract void parse(JaxcelContext context);
38+
39+}
--- /dev/null
+++ b/Jaxcel/src/org/hanei/jaxcel/parser/CellParser.java
@@ -0,0 +1,578 @@
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.IOException;
22+import java.io.InputStream;
23+import java.text.CharacterIterator;
24+import java.text.StringCharacterIterator;
25+import java.util.ArrayDeque;
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.apache.poi.ss.util.CellReference;
35+import org.hanei.jaxcel.exception.JaxcelInputException;
36+import org.hanei.jaxcel.report.JaxcelContext;
37+import org.hanei.jaxcel.util.ExcelUtil;
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+ // プロパティファイル キー定義
53+ private static final String DEFAULT_PROPS_NAME = "/jaxcel.properties";
54+ private static final String P_EL_CLASS = "el.class";
55+ private static final String P_EL_DIRECTIVE = "el.directive";
56+ private static final String P_EL_BRACKET_START = "el.bracket.start";
57+ private static final String P_EL_BRACKET_END = "el.bracket.end";
58+ private static final String P_TL_DIRECTIVE = "tl.directive";
59+ private static final String P_TL_PREFIX = "tl.";
60+ private static final String P_CLASS_SUFFIX = ".class";
61+ private static final String P_TL_BRACKET_START = "tl.bracket.start";
62+ private static final String P_TL_BRACKET_END = "tl.bracket.end";
63+ private static final String P_DATE_FORMAT = "format.date";
64+ private static final String P_TIME_FORMAT = "format.time";
65+
66+ // 文字・文字列定義
67+ private static final String TL_START_SYMBOL = "#";
68+ private static final char VALUE_BRACKET = '"';
69+ private static final char ATTR_DELIMITER = ':';
70+ private static final char ESCAPE_CHAR = '\\';
71+ private static final char EM_SPACE = ' ';
72+
73+ private static final int PARSE_MAX = 10;
74+
75+ private ExtendedProperties config = null;
76+ private Map<String, TLParser> parserMap = null;
77+ private JaxcelContext context = null;
78+ private Cell cell = null;
79+ private char elDirective;
80+
81+ private boolean reParseFlg; // 同一セル再パース要否フラグ
82+ private int parseCount; // 同一セルパース回数
83+ private char elStartBracket;
84+ private char elEndBracket;
85+ private char tlEndBracket;
86+ private String matchPattern = "";
87+
88+ private StringCharacterIterator valueIterator = new StringCharacterIterator("");
89+ private Pattern ptAll = null;
90+ private Matcher mtAll = null;
91+
92+ /**
93+ * コンストラクタ
94+ *
95+ * @param context Jaxlsコンテキストオブジェクト
96+ *
97+ * @throws JaxcelInputException デフォルトプロパティファイル入力例外発生時
98+ */
99+ public CellParser(JaxcelContext context) {
100+ // デフォルトプロパティロード
101+ try(InputStream inStream = getClass().getResourceAsStream(DEFAULT_PROPS_NAME)) {
102+ config = new ExtendedProperties();
103+ config.load(inStream);
104+ log.debug("default properties load");
105+ } catch (IOException e) {
106+ throw new JaxcelInputException("default properties load error");
107+ }
108+
109+ // コンテキスト取得
110+ this.context = context;
111+ if(this.context != null) {
112+ //TODO: 外部プロパティロードを予定。デフォルトプロパティ上書く
113+ }
114+
115+ // 初期処理
116+ init();
117+ }
118+
119+
120+ /**
121+ * 初期処理
122+ */
123+ private void init() {
124+ log.trace("init start");
125+
126+ // パーサ ロード
127+ parserMap = new HashMap<>();
128+ try {
129+ // 日付・時刻書式
130+ context.setDateFormat(config.getStringArray(P_DATE_FORMAT));
131+ context.setTimeFormat(config.getStringArray(P_TIME_FORMAT));
132+ // EL式パーサ
133+ Class<?> cls = Class.forName(config.getString(P_EL_CLASS));
134+ TLParser parser = (TLParser) cls.newInstance();
135+ elDirective = config.getString(P_EL_DIRECTIVE).charAt(0);
136+ elStartBracket = config.getString(P_EL_BRACKET_START).charAt(0);
137+ elEndBracket = config.getString(P_EL_BRACKET_END).charAt(0);
138+ parserMap.put(String.valueOf(elDirective) + String.valueOf(elStartBracket), parser);
139+ log.debug("ELParser load");
140+
141+ tlEndBracket = config.getString(P_TL_BRACKET_END).charAt(0);
142+ for(String tlDirective : config.getStringArray(P_TL_DIRECTIVE)) {
143+ cls = Class.forName(config.getString(P_TL_PREFIX + tlDirective + P_CLASS_SUFFIX));
144+ parser = (TLParser) cls.newInstance();
145+ parserMap.put(TL_START_SYMBOL + tlDirective + config.getString(P_TL_BRACKET_START), parser);
146+ log.debug("{} load", cls.getSimpleName());
147+ }
148+ }
149+ catch(Exception e) {
150+ log.error("TLParser load error. {}", e.getMessage(), e);
151+ }
152+
153+ // 正規表現Pattern生成
154+ for(String key : parserMap.keySet()) {
155+ matchPattern += (matchPattern.length() > 0 ? "|" : "") + Pattern.quote(key);
156+ }
157+ log.debug("matchPattern: {}", matchPattern);
158+ ptAll = Pattern.compile(matchPattern);
159+
160+ log.trace("init end");
161+ }
162+
163+ /**
164+ * セルのパース
165+ *
166+ * @param cell 対象セル
167+ *
168+ * @throws JaxcelInputException 入力例外発生時
169+ */
170+ public void parse(Cell cell) {
171+ log.trace("parse start");
172+
173+ // チェック
174+ if(cell == null) {
175+ // 再パースフラグクリア
176+ this.cell = null;
177+ reParseFlg = false;
178+ parseCount = 0;
179+ log.debug("cell is null");
180+ log.trace("parse end");
181+ return;
182+ }
183+
184+ // パース初回の場合セルを保持
185+ if(this.cell == null) {
186+ log.debug("start new cell parse");
187+ this.cell = cell;
188+ // 再パースフラグ・カウントクリア
189+ reParseFlg = false;
190+ parseCount = 0;
191+ }
192+ // 保持しているセルと同一セルのパースの場合
193+ else if(ExcelUtil.equalsCellAddress(this.cell, cell, true)) {
194+ // 再パースカウント加算
195+ parseCount++;
196+ // 再パース回数チェック
197+ if(parseCount >= PARSE_MAX) {
198+ log.warn("parse repeat count over");
199+ // 再パースフラグクリア
200+ reParseFlg = false;
201+ log.trace("parse end");
202+ return;
203+ }
204+ log.debug("start repeat cell parse");
205+ }
206+ // 保持しているセルと異なるセルのパースの場合
207+ else {
208+ log.debug("start new cell parse");
209+ // セルを保持
210+ this.cell = cell;
211+ // 再パースフラグ・カウントクリア
212+ reParseFlg = false;
213+ parseCount = 0;
214+ }
215+
216+ // 文字列・計算式の場合は値保持。以外は終了
217+ String cellValue = null;
218+ switch(cell.getCellType()){
219+ case Cell.CELL_TYPE_STRING:
220+ cellValue = cell.getStringCellValue();
221+ log.debug("cell[{}] cellType: string value: {}", (new CellReference(cell)).formatAsString(), cellValue);
222+ break;
223+ case Cell.CELL_TYPE_FORMULA:
224+ cellValue = cell.getCellFormula();
225+ log.debug("cell[{}] cellType: formula value: {}", (new CellReference(cell)).formatAsString(), cellValue);
226+ break;
227+ default:
228+ log.debug("cell type is not string or formula");
229+ // 再パースフラグクリア
230+ reParseFlg = false;
231+ log.trace("parse end");
232+ return;
233+ }
234+
235+ // 解析開始
236+ TLParser parser = null;
237+ Map<String, Object> attributeMap = new HashMap<>();
238+ String matchString = null;
239+ String mapKey = null;
240+ StringBuilder strBuf = new StringBuilder(1024);
241+ StringBuilder parseString = new StringBuilder(1024);
242+ Deque<Character> bracketStack = new ArrayDeque<>();
243+ boolean attrValueFlag, expressionFlag;
244+ boolean findFlag = false;
245+ boolean escapeFlag = false;
246+ boolean keyParseFlag = false;
247+
248+ // 指示子(TL)の検索
249+ mtAll = ptAll.matcher(cellValue);
250+ // 指示子検索ループ
251+ findLoop:
252+ while(mtAll.find()) {
253+ matchString = mtAll.group();
254+ log.debug("match: {}", matchString);
255+
256+ // パーサ取得
257+ parser = parserMap.get(matchString);
258+ if(parser == null) {
259+ log.warn("parser is null");
260+ continue;
261+ }
262+ log.debug("parser: {}", parser.getClass().getSimpleName());
263+
264+ // クリア
265+ parseString.setLength(0);
266+ parseString.append(matchString);
267+ strBuf.setLength(0);
268+ bracketStack.clear();
269+ attributeMap.clear();
270+ expressionFlag = false;
271+ attrValueFlag = false;
272+ keyParseFlag = false;
273+ escapeFlag = false;
274+
275+ // 指示子がEL式
276+ if(matchString.equals(String.valueOf(elDirective) + String.valueOf(elStartBracket))) {
277+ // elValueFlag ON
278+ expressionFlag = true;
279+ log.debug("expressionFlag: {}", expressionFlag);
280+ // 括弧スタックに開始括弧push
281+ bracketStack.offerFirst(elStartBracket);
282+ }
283+
284+ // 開始括弧以降読込み
285+ valueIterator.setText(cellValue);
286+ for(char c = valueIterator.setIndex(mtAll.start() + matchString.length()); c != CharacterIterator.DONE; c = valueIterator.next()) {
287+ // カーソル文字抜出し
288+ parseString.append(c);
289+ log.trace("current char: {}", c);
290+ // EL式解析中
291+ if(expressionFlag) {
292+ log.trace("parsing EL");
293+ // カーソル文字がEL式終端括弧であれば
294+ if(elEndBracket == c) {
295+ // 括弧スタックpop
296+ bracketStack.pollFirst();
297+ // 括弧スタック空ならEL式解析終了
298+ if(bracketStack.size() == 0) {
299+ // Mapに登録
300+ attributeMap.put(Constants.EXPRESSION, getTrimString(strBuf));
301+ log.debug("attributeMap : {}", attributeMap);
302+ // 指示子がEL式なら解析終了
303+ if(matchString.equals(String.valueOf(elDirective) + String.valueOf(elStartBracket))) {
304+ log.trace("parse end");
305+ findFlag = true;
306+ break findLoop;
307+ }
308+ // 指示子がEL式以外なら
309+ else {
310+ log.trace("EL parse end");
311+ // フラグクリア
312+ expressionFlag = false;
313+ // バッファクリア
314+ strBuf.setLength(0);
315+ }
316+ }
317+ // 括弧スタック空でないならバッファに保持
318+ else {
319+ strBuf.append(c);
320+ log.trace("parsing: {}", strBuf.toString());
321+ }
322+ }
323+ // EL式終端括弧以外
324+ else {
325+ // EL式開始括弧
326+ if(elStartBracket == c) {
327+ // 括弧スタックにpush
328+ bracketStack.offerFirst(c);
329+ log.trace("bracketStack: {}", bracketStack.size());
330+ }
331+ // バッファに保持
332+ strBuf.append(c);
333+ log.trace("parsing: {}", strBuf.toString());
334+ }
335+ }
336+ // 属性値解析中
337+ else if(attrValueFlag) {
338+ log.trace("parsing attrValue");
339+ // カーソル文字がダブルクォートであれば
340+ if(VALUE_BRACKET == c) {
341+ log.trace("escapeFlag : {}", escapeFlag);
342+ // エスケープ中なら
343+ if(escapeFlag) {
344+ // 括弧とはみなさずバッファに保持
345+ strBuf.append(c);
346+ // エスケープフラグクリア
347+ escapeFlag = false;
348+ log.trace("parsing: {}", strBuf.toString());
349+ }
350+ // エスケープ中でないなら
351+ else {
352+ // Mapに登録
353+ attributeMap.put(mapKey, strBuf.toString());
354+ log.debug("attributeMap : {}", attributeMap);
355+ // 属性値解析中フラグクリア
356+ attrValueFlag = false;
357+ // バッファクリア
358+ strBuf.setLength(0);
359+ mapKey = null;
360+ }
361+ }
362+ // カーソル文字がダブルクォートでないなら
363+ else {
364+ log.trace("escapeFlag : {}", escapeFlag);
365+ // エスケープ中なら
366+ if(escapeFlag) {
367+ // エスケープフラグクリア
368+ escapeFlag = false;
369+ }
370+ // エスケープ中でなくエスケープ文字(\)なら
371+ else if(ESCAPE_CHAR == c) {
372+ // エスケープフラグON
373+ escapeFlag = true;
374+ }
375+ // バッファに保持
376+ strBuf.append(c);
377+ log.trace("parsing: {}", strBuf.toString());
378+ }
379+ }
380+ // カーソル文字がEL式指示字であれば
381+ else if(elDirective == c) {
382+ log.trace("parsing elDirective");
383+ // 次の文字のチェック
384+ c = valueIterator.next();
385+ log.trace("next char: {}", c);
386+ // 次の文字がEL式開始括弧であれば
387+ if(elStartBracket == c) {
388+ parseString.append(c);
389+ // EL式解析中フラグセット
390+ expressionFlag = true;
391+ log.trace("expressionFlag : {}", expressionFlag);
392+ // バッファ空でなければ
393+ if(strBuf.length() > 0) {
394+ // Mapに登録(値はnull)
395+ attributeMap.put(strBuf.toString(), null);
396+ log.debug("attributeMap : {}", attributeMap);
397+ }
398+ // バッファクリア
399+ mapKey = null;
400+ strBuf.setLength(0);
401+ }
402+ // 次の文字がEL式開始括弧でなければ
403+ else {
404+ // カーソルを戻しバッファに保持
405+ c = valueIterator.previous();
406+ log.trace("current char previous: {}", c);
407+ // カーソルを戻しバッファに保持
408+ strBuf.append(c);
409+ log.trace("parsing: {}", strBuf.toString());
410+ }
411+ }
412+ // カーソル文字が属性・属性値結合子であれば
413+ else if(ATTR_DELIMITER == c) {
414+ log.trace("parsing attr delimiter");
415+ // 属性文字列空でないなら
416+ if(mapKey != null) {
417+ // 属性解析済みフラグセット
418+ keyParseFlag = true;
419+ log.trace("keyParseFlag: {}", keyParseFlag);
420+ }
421+ // 属性文字列空なら
422+ else {
423+ // バッファ空でなければ
424+ if(strBuf.length() > 0) {
425+ // 属性文字列保持
426+ mapKey = strBuf.toString();
427+ log.trace("mapKey: {}", mapKey);
428+ // Mapに登録(値はnull)
429+ attributeMap.put(mapKey, null);
430+ log.debug("attributeMap : {}", attributeMap);
431+ // バッファクリア
432+ strBuf.setLength(0);
433+ // 属性解析済みフラグセット
434+ keyParseFlag = true;
435+ log.trace("keyParseFlag: {}", keyParseFlag);
436+ }
437+ // 上記以外
438+ else {
439+ // 解析異常終了
440+ log.warn("parse end");
441+ break;
442+ }
443+ }
444+ }
445+ // カーソル文字がダブルクォートであれば
446+ else if(VALUE_BRACKET == c) {
447+ log.trace("parsing attr value bracket");
448+ // 属性解析済みなら
449+ if(keyParseFlag == true && mapKey != null) {
450+ // 属性値解析中フラグセット
451+ attrValueFlag = true;
452+ keyParseFlag = false;
453+ log.trace("attrValueFlag: {}", attrValueFlag);
454+ log.trace("keyParseFlag: {}", keyParseFlag);
455+ // バッファクリア
456+ strBuf.setLength(0);
457+ }
458+ // 上記以外
459+ else {
460+ // 解析異常終了
461+ log.warn("parse end");
462+ break;
463+ }
464+ }
465+ // 空白文字
466+ else if(Character.isWhitespace(c) || EM_SPACE == c) {
467+ log.trace("parsing white space");
468+ // バッファ空でなければ
469+ if(strBuf.length() > 0) {
470+ // Mapに登録(値はnull)
471+ mapKey = strBuf.toString();
472+ log.trace("mapKey: {}", mapKey);
473+ attributeMap.put(mapKey, null);
474+ log.debug("attributeMap : {}", attributeMap);
475+ // バッファクリア
476+ strBuf.setLength(0);
477+ }
478+ }
479+ // 指示子閉じ括弧
480+ else if(tlEndBracket == c) {
481+ log.trace("parsing tlEndBracket");
482+ // バッファ空でなければ
483+ if(strBuf.length() > 0) {
484+ // Mapに登録(値はnull)
485+ attributeMap.put(strBuf.toString(), null);
486+ log.debug("attributeMap : {}", attributeMap);
487+ // バッファクリア
488+ mapKey = null;
489+ strBuf.setLength(0);
490+ }
491+ log.trace("parse end");
492+ findFlag = true;
493+ break findLoop;
494+ }
495+ // 上記以外
496+ else {
497+ // バッファに保持
498+ strBuf.append(c);
499+ log.trace("parsing: {}", strBuf.toString());
500+ }
501+ }
502+ }
503+
504+ log.trace("findFlag: {}", findFlag);
505+ // 指示子検索ヒット
506+ if(findFlag) {
507+ // コンテキスト設定
508+ context.setCurrentCell(this.cell);
509+ context.setParseString(parseString.toString());
510+ context.setAttributeMap(attributeMap);
511+ if(log.isDebugEnabled()) {
512+ log.debug("parseString: {}", parseString);
513+ log.debug("attributeMap: {}", attributeMap);
514+ }
515+ try {
516+ // パーサでのパース
517+ parser.parse(context);
518+
519+ // パース後のセルタイプチェック
520+ switch(this.cell.getCellType()){
521+ case Cell.CELL_TYPE_STRING:
522+ case Cell.CELL_TYPE_FORMULA:
523+ // セルタイプが文字列、数式の場合は再パースフラグ設定
524+ reParseFlg = true;
525+ break;
526+ default:
527+ // 上記以外は再パースフラグクリア
528+ reParseFlg = false;
529+ }
530+ }
531+ catch(Exception e) {
532+ log.error("parser.parse error. {}", e.getMessage(), e);
533+ // 再パースフラグクリア
534+ reParseFlg = false;
535+ }
536+ }
537+ // 検索ヒットせず
538+ else {
539+ log.debug("not found TL");
540+ // 再パースフラグクリア
541+ reParseFlg = false;
542+ }
543+
544+ log.trace("parse end: reParseFlg: {} parseCount: {}", reParseFlg, parseCount);
545+ }
546+
547+ /**
548+ * 同一セルの再パース要否を返却
549+ *
550+ * @return 再パース必要であればはtrue。不要であればfalse
551+ */
552+ public boolean isReParseCell() {
553+ return reParseFlg;
554+ }
555+
556+ /**
557+ * StringBuilderで保持している文字列のトリム
558+ *
559+ * @param string 文字列
560+ * @return トリム結果
561+ */
562+ private String getTrimString(StringBuilder string) {
563+ if(string == null) return "";
564+
565+ int st = 0;
566+ String str = string.toString();
567+ char[] val = str.toCharArray();
568+ int len = val.length;
569+
570+ while ((st < len) && (val[st] <= ' ' || val[st] == ' ')) {
571+ st++;
572+ }
573+ while ((st < len) && (val[len - 1] <= ' ' || val[len - 1] == ' ')) {
574+ len--;
575+ }
576+ return ((st > 0) || (len < val.length)) ? str.substring(st, len) : str;
577+ }
578+}
--- /dev/null
+++ b/Jaxcel/src/org/hanei/jaxcel/parser/Constants.java
@@ -0,0 +1,32 @@
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+/**
22+ * テンプレートパーサー用定数クラス<br>
23+ *
24+ * @since 1.01.00
25+ * @author Noboru Saito
26+ */
27+public class Constants {
28+ public static final short FORMAT_GENERAL = 0;
29+ public static final short FORMAT_TEXT = 49;
30+ public static final String EXPRESSION = "_expression";
31+
32+}
--- /dev/null
+++ b/Jaxcel/src/org/hanei/jaxcel/parser/ELParser.java
@@ -0,0 +1,247 @@
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.text.ParseException;
22+import java.util.Date;
23+import java.util.Locale;
24+import java.util.TimeZone;
25+import org.apache.commons.lang3.BooleanUtils;
26+import org.apache.commons.lang3.StringUtils;
27+import org.apache.commons.lang3.math.NumberUtils;
28+import org.apache.commons.lang3.time.DateUtils;
29+import org.apache.poi.ss.formula.FormulaParseException;
30+import org.apache.poi.ss.usermodel.Cell;
31+import org.hanei.jaxcel.report.ELManager;
32+import org.hanei.jaxcel.report.JaxcelContext;
33+import org.hanei.jaxcel.util.ExcelUtil;
34+
35+/**
36+ * EL式のパーサー実装<br>
37+ *
38+ * @since 1.01.00
39+ * @author Noboru Saito
40+ */
41+public class ELParser extends AbstractTLParser {
42+
43+ @Override
44+ public void parse(JaxcelContext context) {
45+ log.trace("parse start");
46+
47+ Object elResult; // パース結果保持用
48+ String newValue;
49+
50+ Cell cell = context.getCurrentCell();
51+ ELManager elMgr = context.getElManager();
52+
53+ // evaluate
54+ elResult = elMgr.evaluate((String) context.getAttributeMap().get(Constants.EXPRESSION));
55+ if(elResult == null) {
56+ log.debug("evaluate result is null");
57+ }
58+ else {
59+ log.debug("evaluate result: {}", elResult.toString());
60+ }
61+
62+ // マッチ1件目をevaluate結果で置換
63+ newValue = ExcelUtil.replaceFirstCellValue(cell, context.getParseString(), (elResult == null ? "" : elResult.toString()));
64+ log.debug("replace value: {}", newValue);
65+
66+ // newValueがnullの場合パース終了
67+ if(newValue == null) {
68+ log.trace("parse end");
69+ return;
70+ }
71+
72+ // newCellValが空白
73+ if(newValue.length() == 0) {
74+ cell.setCellType(Cell.CELL_TYPE_BLANK);
75+ }
76+ // newValueが空白でないなら
77+ else {
78+ // 置換後の値の形式チェック
79+ // 置換後の値が数値型
80+ if(NumberUtils.isNumber(newValue)) {
81+ // もともと文字列セルなら数値セルに変更し値をセット
82+ if(cell.getCellType() == Cell.CELL_TYPE_STRING) {
83+ cell.setCellType(Cell.CELL_TYPE_BLANK);
84+ cell.setCellType(Cell.CELL_TYPE_NUMERIC);
85+ // まずは整数変換
86+ try {
87+ cell.setCellValue(Integer.parseInt(newValue));
88+ log.debug("set value type: Integer");
89+ }
90+ // エラー発生で実数変換
91+ catch(NumberFormatException e) {
92+ cell.setCellValue(NumberUtils.toDouble(newValue));
93+ log.debug("set value type: Double");
94+ }
95+ }
96+ // もともと計算式セルの場合は計算式として値をセット
97+ else {
98+ // まずはそのまま
99+ try {
100+ cell.setCellFormula(newValue);
101+ log.debug("set value type: Formula");
102+ }
103+ // エラー発生で数値としてセット
104+ catch(FormulaParseException e) {
105+ log.warn("formula set error: {}", e.getMessage(), e);
106+ cell.setCellType(Cell.CELL_TYPE_BLANK);
107+ cell.setCellType(Cell.CELL_TYPE_NUMERIC);
108+ // まずは整数変換
109+ try {
110+ cell.setCellValue(Integer.parseInt(newValue));
111+ log.debug("set value type: Integer");
112+ }
113+ // エラー発生で実数変換
114+ catch(NumberFormatException e2) {
115+ cell.setCellValue(NumberUtils.toDouble(newValue));
116+ log.debug("set value type: Double");
117+ }
118+ }
119+ }
120+ }
121+ // 置換後の値がBool型
122+ else if(StringUtils.equalsIgnoreCase(newValue, "true") || StringUtils.equalsIgnoreCase(newValue, "false")) {
123+ // もともと文字列セルならBoolセルに変更し値をセット
124+ if(cell.getCellType() == Cell.CELL_TYPE_STRING) {
125+ cell.setCellType(Cell.CELL_TYPE_BLANK);
126+ cell.setCellType(Cell.CELL_TYPE_BOOLEAN);
127+ cell.setCellValue(BooleanUtils.toBoolean(newValue));
128+ log.debug("set value type: Boolean");
129+ }
130+ // もともと計算式セルの場合は計算式として値をセット
131+ else {
132+ // まずはそのまま
133+ try {
134+ cell.setCellFormula(newValue);
135+ log.debug("set value type: Formula");
136+ }
137+ // エラー発生でBoolとしてセット
138+ catch(FormulaParseException e) {
139+ log.warn("formula set error: {}", e.getMessage(), e);
140+ cell.setCellType(Cell.CELL_TYPE_BLANK);
141+ cell.setCellType(Cell.CELL_TYPE_BOOLEAN);
142+ cell.setCellValue(BooleanUtils.toBoolean(newValue));
143+ log.debug("set value type: Boolean");
144+ }
145+ }
146+ }
147+ // 置換後の値が上記以外
148+ else {
149+ Date tmpDate = null;
150+ int valType = -1;
151+ for(int i = 0; i < 3; i++) {
152+ switch(i) {
153+ case 0:
154+ try {
155+ // 置換後の値が日付・日付時刻型
156+ tmpDate = DateUtils.parseDateStrictly(newValue, Locale.getDefault(), context.getDateFormat());
157+ valType = i;
158+ } catch (ParseException e1) {}
159+ break;
160+ case 1:
161+ try {
162+ // 置換後の値が時刻型
163+ tmpDate = DateUtils.parseDateStrictly(newValue, context.getTimeFormat());
164+ valType = i;
165+ } catch (ParseException e1) {}
166+ break;
167+ default:
168+ // 置換後の値が上記以外(文字列)
169+ valType = i;
170+ break;
171+ }
172+ if(valType >= 0) break;
173+ }
174+
175+ switch(valType) {
176+ case 0:
177+ case 1:
178+ // 置換後の値が日付・日付時刻・時刻型
179+ // もともと文字列セルなら数値セルに変更し値をセット
180+ if(cell.getCellType() == Cell.CELL_TYPE_STRING) {
181+ cell.setCellType(Cell.CELL_TYPE_BLANK);
182+ cell.setCellType(Cell.CELL_TYPE_NUMERIC);
183+ // 書式文字列なら文字列のまま挿入
184+ if(Constants.FORMAT_GENERAL == cell.getCellStyle().getDataFormat() || Constants.FORMAT_TEXT == cell.getCellStyle().getDataFormat()) {
185+ cell.setCellValue(newValue);
186+ log.debug("set value type: String");
187+ }
188+ else {
189+ switch(valType) {
190+ case 0:
191+ // 置換後の値が日付・日付時刻型ならDateでセット
192+ cell.setCellValue(tmpDate);
193+ log.debug("set value type: Date");
194+ break;
195+ case 1:
196+ // 置換後の値が時刻型ならDoubleでセット(デフォルトタイムゾーン分加算)
197+ cell.setCellValue((tmpDate.getTime() + TimeZone.getDefault().getRawOffset()) / (1000.0 * 60 * 60 * 24));
198+ log.debug("set value type: double");
199+ break;
200+ }
201+ }
202+ }
203+ // もともと計算式セルの場合は計算式として値をセット
204+ else {
205+ // まずはそのまま
206+ try {
207+ cell.setCellFormula(newValue);
208+ log.debug("set value type: Formula");
209+ }
210+ // エラー発生で数値としてセット
211+ catch(FormulaParseException e) {
212+ log.warn("formula set error: {}", e.getMessage(), e);
213+ cell.setCellType(Cell.CELL_TYPE_BLANK);
214+ cell.setCellType(Cell.CELL_TYPE_NUMERIC);
215+ cell.setCellValue(tmpDate);
216+ log.debug("set value type: Date");
217+ }
218+ }
219+ break;
220+ default:
221+ // 置換後の値が日付・日付時刻・時刻型以外(文字列と判断)
222+ // 元のセルタイプのまま値をセット
223+ if(cell.getCellType() == Cell.CELL_TYPE_STRING) {
224+ cell.setCellValue(newValue);
225+ log.debug("set value type: String");
226+ }
227+ else {
228+ // 式
229+ try {
230+ cell.setCellFormula(newValue);
231+ log.debug("set value type: Formula");
232+ }
233+ catch(FormulaParseException e2) {
234+ log.warn("formula set error: {}", e2.getMessage(), e2);
235+ cell.setCellType(Cell.CELL_TYPE_BLANK);
236+ cell.setCellType(Cell.CELL_TYPE_STRING);
237+ cell.setCellValue(newValue);
238+ log.debug("set value type: String");
239+ }
240+ }
241+ }
242+ }
243+ }
244+
245+ log.trace("parse end");
246+ }
247+}
--- /dev/null
+++ b/Jaxcel/src/org/hanei/jaxcel/parser/ForeachParser.java
@@ -0,0 +1,635 @@
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.util.ArrayList;
22+import java.util.Map;
23+import java.util.regex.Matcher;
24+import java.util.regex.Pattern;
25+
26+import org.apache.commons.lang3.BooleanUtils;
27+import org.apache.commons.lang3.math.NumberUtils;
28+import org.apache.poi.POIXMLDocumentPart;
29+import org.apache.poi.hssf.record.cf.CellRangeUtil;
30+import org.apache.poi.ss.usermodel.Cell;
31+import org.apache.poi.ss.usermodel.Row;
32+import org.apache.poi.ss.usermodel.Sheet;
33+import org.apache.poi.ss.util.CellRangeAddress;
34+import org.apache.poi.ss.util.CellReference;
35+import org.apache.poi.xssf.usermodel.XSSFDrawing;
36+import org.apache.poi.xssf.usermodel.XSSFSheet;
37+import org.hanei.jaxcel.report.ELManager;
38+import org.hanei.jaxcel.report.JaxcelContext;
39+import org.hanei.jaxcel.util.ExcelUtil;
40+import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTDrawing;
41+import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTMarker;
42+import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTTwoCellAnchor;
43+
44+/**
45+ * foreach句のパーサー実装<br>
46+ *
47+ * @since 1.01.00
48+ * @author Noboru Saito
49+ */
50+public class ForeachParser extends AbstractTLParser {
51+
52+ private static final String ROWS = "rows";
53+ private static final String COLS = "cols";
54+ private static final String DIRECTION = "direction";
55+ private static final String STYLE = "style";
56+ private static final String SHIFT = "shift";
57+ private static final String BLOCK = "block";
58+ private static final String START = "start";
59+ private static final String END = "end";
60+
61+ private static final String IN_PHRASE = " in ";
62+
63+ private static final String ROW = "row";
64+ private static final String COL = "col";
65+ private static final String COPY = "copy";
66+ private static final int IDX_BASE = 1;
67+ private static final int SPAN_DEF = 1;
68+ @Override
69+ public void parse(JaxcelContext context) {
70+ log.trace("parse start");
71+
72+ Sheet sheet = context.getCurrentSheet();
73+ Cell cell = context.getCurrentCell();
74+ ELManager elMgr = context.getElManager();
75+ Map<String, Object> attributeMap = context.getAttributeMap();
76+ String expression = null;
77+ String object = null;
78+ String list = null;
79+ String direction = ROW;
80+ boolean shift = false;
81+ boolean block = true;
82+ String style = COPY;
83+ String start = null; // start
84+ String end = null; // end
85+ int startIdx;
86+ int endIdx;
87+ int rowSpan = SPAN_DEF; // row
88+ int colSpan = SPAN_DEF; // column
89+
90+ Row fromRow, toRow;
91+ Cell fromCell, toCell;
92+ String fromCellValue;
93+ Pattern pattern = null;
94+ Matcher matcher = null;
95+ Object evalObject;
96+ Object listObject;
97+ Object[] mapKeys = null;
98+ int listSize;
99+ ArrayList<CellRangeAddress> rangeList = new ArrayList<>();
100+ CellRangeAddress fromRange, toRange, tmpBfRange, tmpAfRange;
101+
102+ // セルタイプのチェック、指示子をクリア
103+ switch(cell.getCellType()) {
104+ case Cell.CELL_TYPE_STRING:
105+ cell.setCellValue(ExcelUtil.replaceFirstCellValue(cell, context.getParseString(), ""));
106+ break;
107+ default:
108+ // セルタイプが文字列でない場合パース終了
109+ log.error("cell type is not strng");
110+ log.trace("parse end");
111+ return;
112+ }
113+
114+ // EL式
115+ if(attributeMap.containsKey(Constants.EXPRESSION)) {
116+ expression = (String) attributeMap.get(Constants.EXPRESSION);
117+ log.debug("expression: {}", expression);
118+ String[] tmp = expression.split(IN_PHRASE, 2);
119+ if(tmp != null) {
120+ object = tmp[0].trim();
121+ list = tmp[1].trim();
122+ }
123+ else {
124+ log.error("format error: ex) object in list");
125+ log.trace("parse end");
126+ return;
127+ }
128+ }
129+ else {
130+ // EL式が存在しな場合は終了
131+ log.error("expression is null");
132+ log.trace("parse end");
133+ return;
134+ }
135+ // リスト
136+ listObject = elMgr.evaluate(list);
137+ if(listObject == null) {
138+ log.error("list is null eval response.");
139+ log.trace("parse end");
140+ return;
141+ }
142+ log.debug("list class: {}", listObject.getClass().getName());
143+
144+ // listObject Mapならキー取得
145+ if(listObject instanceof Map) {
146+ mapKeys = ((Map<?, ?>)listObject).keySet().toArray();
147+ log.debug("mapKeys: {}", mapKeys);
148+ }
149+
150+ // listのサイズ取得
151+ evalObject = elMgr.evaluate("size(" + list + ")");
152+ // evalした結果がintでなければリスト不可なオブジェクトと判断
153+ if(evalObject == null) {
154+ log.error("list size unknown");
155+ log.trace("parse end");
156+ return;
157+ }
158+ else if(!(evalObject instanceof Integer)) {
159+ log.error("list size is not Integer instance");
160+ log.trace("parse end");
161+ return;
162+ }
163+ listSize = (int) evalObject;
164+ log.debug("list size: {}", listSize);
165+
166+
167+ // direction:"row|col" 繰返し方向 デフォルト row
168+ if(attributeMap.containsKey(DIRECTION)) {
169+ direction = (String) attributeMap.get(DIRECTION);
170+ if(!ROW.equalsIgnoreCase(direction) && !COL.equalsIgnoreCase(direction)) {
171+ log.warn("{} is illegal argument. set default: {}", DIRECTION, ROW);
172+ direction = ROW;
173+ }
174+ }
175+ // shift:"true|false" foreachレンジより後のセルに対するシフト指定 デフォルト false
176+ if(attributeMap.containsKey(SHIFT)) {
177+ shift = BooleanUtils.toBoolean((String) attributeMap.get(SHIFT));
178+ }
179+ // blick:"true|false" foreachレンジをブロック範囲で繰り返すかの指定 デフォルト true
180+ if(attributeMap.containsKey(BLOCK)) {
181+ block = BooleanUtils.toBoolean((String) attributeMap.get(BLOCK));
182+ }
183+ // style:"copy|^copy" foreachレンジのセルスタイルのコピー指定 デフォルト copy
184+ if(attributeMap.containsKey(STYLE)) {
185+ style = (String) attributeMap.get(STYLE);
186+ }
187+ // start:"numberExpression"
188+ if(attributeMap.containsKey(START)) {
189+ start = (String) attributeMap.get(START);
190+ }
191+ // end:"numberExpression"
192+ if(attributeMap.containsKey(END)) {
193+ end = (String) attributeMap.get(END);
194+ }
195+ // rows:"number" 範囲 デフォルト 1
196+ if(attributeMap.containsKey(ROWS)) {
197+ rowSpan = NumberUtils.toInt((String) attributeMap.get(ROWS));
198+ if(rowSpan <= 0) {
199+ log.warn("{} is illegal argument. set default: {}", ROWS, SPAN_DEF);
200+ rowSpan = SPAN_DEF;
201+ }
202+ }
203+ // cols:"number" 範囲 デフォルト 1
204+ if(attributeMap.containsKey(COLS)) {
205+ colSpan = NumberUtils.toInt((String) attributeMap.get(COLS));
206+ if(colSpan <= 0) {
207+ log.warn("{} is illegal argument. set default: {}", COLS, SPAN_DEF);
208+ colSpan = SPAN_DEF;
209+ }
210+ }
211+
212+ // start インデックス
213+ if(start != null) {
214+ // size(list) 等 式の可能性もあるのでevalする
215+ evalObject = elMgr.evaluate(start);
216+ if(evalObject != null) {
217+ // evalした結果がintでなければデフォルト
218+ if(!(evalObject instanceof Integer)) {
219+ log.warn("start is not Integer instance. start set default: {}", IDX_BASE);
220+ startIdx = IDX_BASE;
221+ }
222+ else {
223+ startIdx = (int) evalObject;
224+ // startIdxの補正
225+ if(startIdx == 0) {
226+ // startIdxが0の場合、1に補正
227+ startIdx = IDX_BASE;
228+ }
229+ else if(startIdx < 0) {
230+ // startIdxがマイナス値の場合、listのサイズからの逆順とする
231+ // -1 の場合リストのサイズ
232+ startIdx = listSize + startIdx + IDX_BASE;
233+ }
234+ else if(startIdx > listSize) {
235+ // startIdxがリストのサイズより大きい場合、リストのサイズに切り詰める
236+ startIdx = listSize;
237+ }
238+ }
239+ }
240+ // evalした結果がnullの場合はデフォルト
241+ else {
242+ log.warn("start is null eval response. start set default: {}", IDX_BASE);
243+ startIdx = IDX_BASE;
244+ }
245+ }
246+ else {
247+ log.debug("start is null. start set default: {}", IDX_BASE);
248+ startIdx = IDX_BASE;
249+ }
250+
251+ // end インデックス
252+ if(end != null) {
253+ // size(list) 等 式の可能性もあるのでevalする
254+ evalObject = elMgr.evaluate(end);
255+ if(evalObject != null) {
256+ // evalした結果がintでなければリストサイズ
257+ if(!(evalObject instanceof Integer)) {
258+ log.warn("end is not Integer instance. end set list size: {}", listSize);
259+ endIdx = listSize;
260+ }
261+ else {
262+ endIdx = (int) evalObject;
263+ // endIdxの補正
264+ if(endIdx == 0) {
265+ // endIdxが0の場合、リストサイズ
266+ endIdx = listSize;
267+ }
268+ else if(endIdx < 0) {
269+ // endIdxがマイナス値の場合、listのサイズからの逆順とする
270+ // -1 の場合リストのサイズ
271+ endIdx = listSize + endIdx + IDX_BASE;
272+ }
273+ else if(endIdx > listSize) {
274+ // endIdxがリストのサイズより大きい場合、リストのサイズに切り詰める
275+ endIdx = listSize;
276+ }
277+ }
278+ }
279+ // evalした結果がnullの場合はリストサイズ
280+ else {
281+ log.warn("end is null eval response. end set list size: {}", listSize);
282+ endIdx = listSize;
283+ }
284+ }
285+ else {
286+ log.debug("end is null. end set list size: {}", listSize);
287+ endIdx = listSize;
288+ }
289+
290+ if(log.isDebugEnabled()) {
291+ log.debug("object: {}", object);
292+ log.debug("list: {}", list);
293+ log.debug("start: {}", startIdx);
294+ log.debug("end: {}", endIdx);
295+ log.debug("rows: {}", rowSpan);
296+ log.debug("cols: {}", colSpan);
297+ log.debug("direction: {}", direction);
298+ log.debug("shift: {}", shift);
299+ log.debug("block: {}", block);
300+ log.debug("style: {}", style);
301+ }
302+
303+ // コピー元範囲 block:falseの場合、行・列全体に範囲を広げる
304+ if(!block) {
305+ // 繰返し方向横(列)(行がデフォルト)
306+ if(COL.equalsIgnoreCase(direction)) {
307+ fromRange = new CellRangeAddress(sheet.getFirstRowNum(), sheet.getLastRowNum(), cell.getColumnIndex(), cell.getColumnIndex() + colSpan - 1);
308+ }
309+ // 繰返し方向縦(行)の場合
310+ else {
311+ fromRange = new CellRangeAddress(cell.getRowIndex(), cell.getRowIndex() + rowSpan - 1, 0, ExcelUtil.getLastColNum(sheet));
312+ }
313+ }
314+ else {
315+ fromRange = new CellRangeAddress(cell.getRowIndex(), cell.getRowIndex() + rowSpan - 1, cell.getColumnIndex(), cell.getColumnIndex() + colSpan - 1);
316+ }
317+ // シートに収まる範囲にリサイズ
318+ fromRange = ExcelUtil.getIntersectRange(sheet, fromRange);
319+
320+ // 繰返しコピー回数1以上なら、コピー先範囲シフト・クリア等実施
321+ if(startIdx != endIdx) {
322+ // コピー先範囲の範囲特定
323+ int copyCount = startIdx < endIdx ? endIdx - startIdx : startIdx - endIdx;
324+ // 繰返し方向横(列)(行がデフォルト)
325+ if(COL.equalsIgnoreCase(direction)) {
326+ toRange = new CellRangeAddress(
327+ fromRange.getFirstRow(),
328+ fromRange.getLastRow(),
329+ fromRange.getLastColumn() + 1,
330+ fromRange.getLastColumn() + (fromRange.getLastColumn() - fromRange.getFirstColumn() + 1) * copyCount);
331+ }
332+ // 繰返し方向縦(行)の場合
333+ else {
334+ toRange = new CellRangeAddress(
335+ fromRange.getLastRow() + 1,
336+ fromRange.getLastRow() + (fromRange.getLastRow() - fromRange.getFirstRow() + 1) * copyCount,
337+ fromRange.getFirstColumn(),
338+ fromRange.getLastColumn());
339+ }
340+
341+ // shift:trueの場合、コピー先範囲の後続範囲をシフト
342+ if(shift) {
343+ // 移動量
344+ int distance = (startIdx > endIdx ? startIdx - endIdx : endIdx - startIdx);
345+
346+ // 繰返し方向横(列)(行がデフォルト)
347+ if(COL.equalsIgnoreCase(direction)) {
348+ distance *= colSpan;
349+ // 繰返し範囲以降をずらす
350+ ExcelUtil.shift(sheet, new CellRangeAddress(fromRange.getFirstRow(), fromRange.getLastRow(), fromRange.getLastColumn() + 1, fromRange.getLastColumn() + 1), COL, distance, block);
351+ }
352+ // 繰返し方向縦(行)の場合
353+ else {
354+ distance *= rowSpan;
355+ // 繰返し範囲以降をずらす
356+ ExcelUtil.shift(sheet, new CellRangeAddress(fromRange.getLastRow() + 1, fromRange.getLastRow() + 1, fromRange.getFirstColumn(), fromRange.getLastColumn()), ROW, distance, block);
357+ }
358+ }
359+ // shift:falseの場合、コピー先範囲をクリア
360+ else {
361+ ExcelUtil.clearRange(sheet, toRange, COPY.equalsIgnoreCase(style), COPY.equalsIgnoreCase(style));
362+ }
363+ // style:copyの場合、コピー元範囲の結合保持・解除
364+ if(COPY.equalsIgnoreCase(style)) {
365+ // コピー元範囲の結合状態チェック・保持。コピー先範囲の結合解除
366+ CellRangeAddress tmpRange;
367+ for(int i = 0; i < sheet.getNumMergedRegions(); i++) {
368+ tmpRange = sheet.getMergedRegion(i);
369+ // コピー元範囲の結合状態保持・解除
370+ switch(CellRangeUtil.intersect(fromRange, tmpRange)) {
371+ case CellRangeUtil.INSIDE:
372+ rangeList.add(tmpRange);
373+ log.debug("from range inside mergedRegion. save: {}", tmpRange.formatAsString());
374+ break;
375+ case CellRangeUtil.ENCLOSES:
376+ log.warn("from range encloses mergedRegion. remove: {}", tmpRange.formatAsString());
377+ sheet.removeMergedRegion(i);
378+ break;
379+ case CellRangeUtil.OVERLAP:
380+ log.warn("from range overlap mergedRegion. remove: {}", tmpRange.formatAsString());
381+ sheet.removeMergedRegion(i);
382+ break;
383+ }
384+ }
385+ }
386+ }
387+
388+ // 範囲行でループ
389+ for(int r = fromRange.getFirstRow(); r <= fromRange.getLastRow(); r++) {
390+ // コピー元行
391+ fromRow = sheet.getRow(r);
392+ // コピー行
393+ toRow = fromRow;
394+ // 範囲列でループ
395+ colLoop:
396+ for(int c = fromRange.getFirstColumn(); c <= fromRange.getLastColumn(); c++) {
397+ // コピー元セル
398+ fromCell = fromRow == null ? null : fromRow.getCell(c);
399+ // コピー元セルが数式か文字列の場合バリュー保持
400+ fromCellValue = null;
401+ if(fromCell != null && (fromCell.getCellType() == Cell.CELL_TYPE_FORMULA || fromCell.getCellType() == Cell.CELL_TYPE_STRING)) {
402+ switch(fromCell.getCellType()) {
403+ case Cell.CELL_TYPE_FORMULA:
404+ fromCellValue = fromCell.getCellFormula();
405+ break;
406+ case Cell.CELL_TYPE_STRING:
407+ fromCellValue = fromCell.getStringCellValue();
408+ }
409+ // 正規表現オブジェクト
410+ pattern = Pattern.compile("(" + Pattern.quote("${") + "[^\\$\\{\\}]*?\\b+)(" + Pattern.quote(object) + ")([^a-zA-Z_0-9\\$]+?)");
411+ matcher = pattern.matcher(fromCellValue);
412+ }
413+
414+ // 繰返し回数でループしコピー
415+ for(int i = startIdx, cpCount = 0; (startIdx <= endIdx && i <= endIdx || startIdx > endIdx && i >= endIdx); i = (startIdx <= endIdx ? i + 1 : i - 1), cpCount++) {
416+ // 繰返し方向横(列)(行がデフォルト)
417+ if(COL.equalsIgnoreCase(direction)) {
418+ // コピー先の範囲チェック
419+ if(c + colSpan * cpCount > ExcelUtil.getMaxColumnIndex(sheet)) {
420+ // コピー先列がシート範囲外
421+ log.debug("break col loop. to cell [{}] is outside sheet", c + colSpan * cpCount + 1);
422+ break colLoop;
423+ }
424+ // コピー元行がnullならコピー先行もnull
425+ if(fromRow == null) {
426+ log.debug("break col loop. from row [{}] and to row [{}] is null", (r + 1), (r + 1));
427+ break colLoop;
428+ }
429+ }
430+ // 繰返し方向縦(行)の場合
431+ else {
432+ // コピー先の範囲チェック
433+ if(r + rowSpan * cpCount > ExcelUtil.getMaxRowIndex(sheet)) {
434+ // コピー先行がシート範囲外
435+ log.debug("break copy loop. to row [{}] is outside sheet", r + rowSpan * cpCount + 1);
436+ break;
437+ }
438+ // コピー行
439+ toRow = sheet.getRow(r + rowSpan * cpCount);
440+ // コピー元行がnullならコピー先行もnull。continue
441+ if(fromRow == null) {
442+ if(toRow != null) {
443+ log.debug("continue. from row [{}] is null. to row [{}] remove", (r + 1), (toRow.getRowNum() + 1));
444+ sheet.removeRow(toRow);
445+ }
446+ else {
447+ log.debug("continue. from row [{}] and to row [{}] is null", (r + 1), ((ROW.equalsIgnoreCase(direction) ? (r + rowSpan * cpCount) : r) + 1));
448+ }
449+ continue;
450+ }
451+ }
452+
453+ // コピー先行がnullなら生成
454+ if(toRow == null) toRow = sheet.createRow(r + rowSpan * cpCount);
455+
456+ // コピー元行・コピー先行 行番号が異なり、block false、繰返し方向縦(行)なら行情報(高さ)もコピー(範囲列先頭のみ)
457+ if(cpCount > 0 && !block && c == fromRange.getFirstColumn() && ROW.equalsIgnoreCase(direction))
458+ toRow.setHeight(fromRow.getHeight());
459+
460+ // コピー先セル
461+ toCell = toRow.getCell(COL.equalsIgnoreCase(direction) ? c + colSpan * cpCount : c);
462+ // コピー元セルがnullならコピー先セルもnull
463+ if(fromCell == null) {
464+ if(toCell != null) {
465+ log.debug("continue. from cell [{}] is null. to cell [{}] remove", (new CellReference(fromRow.getRowNum(), c)).formatAsString(), (new CellReference(toCell.getRowIndex(), toCell.getColumnIndex())).formatAsString());
466+ toRow.removeCell(toCell);
467+ }
468+ else {
469+ log.debug("continue. from cell [{}] and to cell [{}] is null", (new CellReference(fromRow.getRowNum(), c)).formatAsString(), (new CellReference(toRow.getRowNum(), COL.equalsIgnoreCase(direction) ? c + colSpan * cpCount : c)).formatAsString());
470+ }
471+ continue;
472+ }
473+ // コピー先セルがnullなら生成
474+ if(toCell == null) toCell = toRow.createCell(COL.equalsIgnoreCase(direction) ? c + colSpan * cpCount : c);
475+
476+ // コピー元列・コピー先列 列番号が異なり、block false、繰返し方向横(列)なら列情報(幅)もコピー(範囲行先頭のみ)
477+ if(cpCount > 0 && !block && r == fromRange.getFirstRow() && COL.equalsIgnoreCase(direction))
478+ sheet.setColumnWidth(toCell.getColumnIndex(), sheet.getColumnWidth(fromCell.getColumnIndex()));
479+
480+ // スタイルコピーなら
481+ if(cpCount > 0 && COPY.equalsIgnoreCase(style)) {
482+ // スタイルのコピー
483+ toCell.setCellStyle(fromCell.getCellStyle());
484+ }
485+ // 値のコピー
486+ switch(fromCell.getCellType()) {
487+ case Cell.CELL_TYPE_BLANK:
488+ toCell.setCellType(Cell.CELL_TYPE_BLANK);
489+ break;
490+ case Cell.CELL_TYPE_BOOLEAN:
491+ toCell.setCellType(Cell.CELL_TYPE_BOOLEAN);
492+ toCell.setCellValue(fromCell.getBooleanCellValue());
493+ break;
494+ case Cell.CELL_TYPE_ERROR:
495+ toCell.setCellType(Cell.CELL_TYPE_ERROR);
496+ toCell.setCellErrorValue(fromCell.getErrorCellValue());
497+ break;
498+ case Cell.CELL_TYPE_NUMERIC:
499+ toCell.setCellType(Cell.CELL_TYPE_NUMERIC);
500+ toCell.setCellValue(fromCell.getNumericCellValue());
501+ break;
502+ case Cell.CELL_TYPE_FORMULA:
503+ toCell.setCellType(Cell.CELL_TYPE_FORMULA);
504+ // 数式のパース・移動先に合わせる
505+ toCell.setCellFormula(
506+ ExcelUtil.getMoveFormula(
507+ sheet,
508+ matcher.replaceAll("$1" + Matcher.quoteReplacement("(" + list + ")[" + (mapKeys == null ? i - 1 : "'" + mapKeys[i - 1] + "'") + "]") + "$3"),
509+ toCell.getRowIndex() - fromCell.getRowIndex(),
510+ toCell.getColumnIndex() - fromCell.getColumnIndex()
511+ )
512+ );
513+ log.debug("to cell Formula: {}", toCell.getCellFormula());
514+ break;
515+ case Cell.CELL_TYPE_STRING:
516+ toCell.setCellType(Cell.CELL_TYPE_STRING);
517+// log.debug("fromCell : {}", fromCell.getStringCellValue());
518+// log.debug("toCell : {}", toCell.getStringCellValue());
519+// log.debug("i : {}", i);
520+// log.debug("cpCount : {}", cpCount);
521+// log.debug("regex : {}", "(" + Pattern.quote("${") + "[^\\$\\{\\}]*?\\b+)(" + Pattern.quote(object) + ")([^a-zA-Z_0-9\\$]+?)");
522+// log.debug("replacement: {}", "$1" + Matcher.quoteReplacement("(" + list + ")[" + (mapKeys == null ? i - 1 : "'" + mapKeys[i - 1] + "'") + "]") + "$3");
523+// log.debug("replaceAll: {}", matcher.replaceAll("$1" + Matcher.quoteReplacement("(" + list + ")[" + (mapKeys == null ? i - 1 : "'" + mapKeys[i - 1] + "'") + "]") + "$3"));
524+ toCell.setCellValue(matcher.replaceAll("$1" + Matcher.quoteReplacement("(" + list + ")[" + (mapKeys == null ? i - 1 : "'" + mapKeys[i - 1] + "'") + "]") + "$3"));
525+ log.debug("to cell string: {}", toCell.getStringCellValue());
526+ break;
527+ }
528+ }
529+ }
530+ }
531+
532+ // スタイルコピーなら
533+ if(COPY.equalsIgnoreCase(style)) {
534+ // 行列加算用
535+ int addRow = 0;
536+ int addCol = 0;
537+ if(COL.equalsIgnoreCase(direction)) {
538+ addCol = colSpan;
539+ }
540+ else {
541+ addRow = rowSpan;
542+ }
543+
544+ // コピー元範囲の結合保持なら
545+ if(!rangeList.isEmpty()) {
546+ // 結合をコピー
547+ // 結合保持数でループ
548+ for(int c = 0; c < rangeList.size(); c++) {
549+ tmpBfRange = rangeList.get(c);
550+ // 繰返し回数でループ
551+ for(int i = startIdx, cpCount = 0; (startIdx <= endIdx && i <= endIdx || startIdx > endIdx && i >= endIdx); i = (startIdx <= endIdx ? i + 1 : i - 1), cpCount++) {
552+ // 初回はコピーしない(コピー元だから)
553+ if(i == startIdx) continue;
554+ // コピー先レンジ
555+ tmpAfRange = new CellRangeAddress(
556+ tmpBfRange.getFirstRow() + (addRow * cpCount),
557+ tmpBfRange.getLastRow() + (addRow * cpCount),
558+ tmpBfRange.getFirstColumn() + (addCol * cpCount),
559+ tmpBfRange.getLastColumn() + (addCol * cpCount));
560+ if(ExcelUtil.validateRange(sheet, tmpAfRange)) {
561+ sheet.addMergedRegion(tmpAfRange);
562+ log.debug("mergedRegion copy. from: [{}] to: [{}]", tmpBfRange.formatAsString(), tmpAfRange.formatAsString());
563+ }
564+ }
565+ }
566+ }
567+
568+ // オブジェクトのコピー Excel2007以降 ooxml形式のみ対応
569+ if(sheet instanceof XSSFSheet) {
570+ int r1, c1, r2, c2;
571+ CTDrawing ctDrawing;
572+ CTTwoCellAnchor fAnchor, cpAnchor;
573+ CTMarker from, to;
574+ try {
575+ // DocumentPartでループ
576+ for(POIXMLDocumentPart part : ((XSSFSheet)sheet).getRelations()) {
577+ if(part == null) continue;
578+ log.debug("DocumentPart class: {}", part.getClass().getName());
579+ // DocumentPartがDrawingオブジェクトの場合
580+ if(part instanceof XSSFDrawing) {
581+ ctDrawing = ((XSSFDrawing) part).getCTDrawing();
582+ if(ctDrawing != null) {
583+ // アンカーでループ
584+ int alSize = ctDrawing.getTwoCellAnchorList().size();
585+ for (int i = 0; i < alSize; i++) {
586+ fAnchor = ctDrawing.getTwoCellAnchorList().get(i);
587+ // GraphicFrameをもつグラフ、スマートアートは現状非対応
588+ if(fAnchor.isSetGraphicFrame()) continue;
589+
590+ // アンカーの位置情報
591+ from = fAnchor.getFrom();
592+ r1 = from.getRow();
593+ c1 = from.getCol();
594+ to = fAnchor.getTo();
595+ r2 = to.getRow();
596+ c2 = to.getCol();
597+ tmpBfRange = new CellRangeAddress(r1, r2, c1, c2);
598+
599+ // コピー元レンジに含まれている、掛かっているならコピー
600+ switch(CellRangeUtil.intersect(fromRange, tmpBfRange)) {
601+ case CellRangeUtil.INSIDE:
602+ case CellRangeUtil.OVERLAP:
603+ // 繰返し回数でループ
604+ for(int j = startIdx, cpCount = 0; (startIdx <= endIdx && j <= endIdx || startIdx > endIdx && j >= endIdx); j = (startIdx <= endIdx ? j + 1 : j - 1), cpCount++) {
605+ // 初回はコピーしない(コピー元だから)
606+ if(j == startIdx) continue;
607+ // オブジェクト(アンカー)のコピー
608+ cpAnchor = ctDrawing.addNewTwoCellAnchor();
609+ cpAnchor.set(fAnchor.copy());
610+ from = cpAnchor.getFrom();
611+ from.setRow(from.getRow() + (addRow * cpCount));
612+ from.setCol(from.getCol() + (addCol * cpCount));
613+ to = cpAnchor.getTo();
614+ to.setRow(to.getRow() + (addRow * cpCount));
615+ to.setCol(to.getCol() + (addCol * cpCount));
616+ if(log.isDebugEnabled()) {
617+ tmpAfRange = new CellRangeAddress(from.getRow(), to.getRow(), from.getCol(), to.getCol());
618+ log.debug("object copy from: [{}] to: [{}]", tmpBfRange.formatAsString(), tmpAfRange.formatAsString());
619+ }
620+ }
621+ }
622+ }
623+ }
624+ }
625+ }
626+ }
627+ catch(Exception e) {
628+ log.error("object copy error: " + e.getMessage(), e);
629+ }
630+ }
631+ }
632+
633+ log.trace("parse end");
634+ }
635+}
--- /dev/null
+++ b/Jaxcel/src/org/hanei/jaxcel/parser/IfParser.java
@@ -0,0 +1,170 @@
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.util.ArrayList;
22+import java.util.Map;
23+import org.apache.commons.lang3.BooleanUtils;
24+import org.apache.commons.lang3.math.NumberUtils;
25+import org.apache.poi.ss.usermodel.Cell;
26+import org.apache.poi.ss.usermodel.Sheet;
27+import org.apache.poi.ss.util.CellRangeAddress;
28+import org.hanei.jaxcel.report.ELManager;
29+import org.hanei.jaxcel.report.JaxcelContext;
30+import org.hanei.jaxcel.util.ExcelUtil;
31+
32+/**
33+ * if句のパーサー実装<br>
34+ *
35+ * @since 1.01.00
36+ * @author Noboru Saito
37+ */
38+public class IfParser extends AbstractTLParser {
39+
40+ private static final String LEFT = "left";
41+ private static final String UP = "up";
42+ private static final String CLEAR = "clear";
43+ private static final String ROWS = "rows";
44+ private static final String COLS = "cols";
45+ private static final String DELETE = "delete";
46+ private static final String BLOCK = "block";
47+ private static final String ROW = "row";
48+ private static final String COL = "col";
49+ private static final int SPAN_DEF = 1;
50+
51+ @Override
52+ public void parse(JaxcelContext context) {
53+ log.trace("parse start");
54+
55+ Sheet sheet = context.getCurrentSheet();
56+ Cell cell = context.getCurrentCell();
57+ ELManager elMgr = context.getElManager();
58+ Map<String, Object> attributeMap = context.getAttributeMap();
59+ String expression = null;
60+ String delete = null;
61+ boolean block = true;
62+ int rowSpan = SPAN_DEF; // row
63+ int colSpan = SPAN_DEF; // column
64+
65+ new ArrayList<>();
66+ // セルタイプのチェック、指示子をクリア
67+ switch(cell.getCellType()) {
68+ case Cell.CELL_TYPE_STRING:
69+ cell.setCellValue(ExcelUtil.replaceFirstCellValue(cell, context.getParseString(), ""));
70+ break;
71+ default:
72+ // セルタイプが文字列でない場合パース終了
73+ log.error("cell type is not strng");
74+ log.trace("parse end");
75+ return;
76+ }
77+
78+ // EL式
79+ if(attributeMap.containsKey(Constants.EXPRESSION)) {
80+ expression = (String) attributeMap.get(Constants.EXPRESSION);
81+ log.debug("expression: {}", expression);
82+ }
83+ else {
84+ // EL式が存在しな場合は終了
85+ log.error("expression is null");
86+ log.trace("parse end");
87+ return;
88+ }
89+
90+ // blick:"true|false" foreachレンジをブロック範囲で繰り返すかの指定 デフォルト true
91+ if(attributeMap.containsKey(BLOCK)) {
92+ block = BooleanUtils.toBoolean((String) attributeMap.get(BLOCK));
93+ }
94+ // delete:"left|up|clear" 判定式がflse判定の場合の制御範囲に対する操作を指定 デフォルト left
95+ if(attributeMap.containsKey(DELETE)) {
96+ delete = (String) attributeMap.get(DELETE);
97+ }
98+ // rows:"number" 範囲 デフォルト 1
99+ if(attributeMap.containsKey(ROWS)) {
100+ rowSpan = NumberUtils.toInt((String) attributeMap.get(ROWS));
101+ if(rowSpan <= 0) {
102+ log.warn("{} is illegal argument. set default: {}", ROWS, SPAN_DEF);
103+ rowSpan = SPAN_DEF;
104+ }
105+ }
106+ // cols:"number" 範囲 デフォルト 1
107+ if(attributeMap.containsKey(COLS)) {
108+ colSpan = NumberUtils.toInt((String) attributeMap.get(COLS));
109+ if(colSpan <= 0) {
110+ log.warn("{} is illegal argument. set default: {}", COLS, SPAN_DEF);
111+ colSpan = SPAN_DEF;
112+ }
113+ }
114+
115+ if(log.isDebugEnabled()) {
116+ log.debug("rows: {}", rowSpan);
117+ log.debug("cols: {}", colSpan);
118+ log.debug("delete: {}", delete);
119+ log.debug("block: {}", block);
120+ }
121+
122+ // 判定
123+ boolean result;
124+ Object elResult = elMgr.evaluate(expression);
125+ if(elResult == null) {
126+ log.debug("evaluate result is null");
127+ result = false;
128+ }
129+ else if(elResult instanceof Boolean) {
130+ result = (boolean) elResult;
131+ }
132+ else {
133+ result = true;
134+ }
135+ log.debug("evaluate result: {}", result);
136+
137+ // 判定falseの場合、範囲の操作
138+ if(!result) {
139+ if(delete == null) {
140+ log.debug("{} is null set default: {}", DELETE, LEFT);
141+ delete = LEFT;
142+ }
143+ else if(!LEFT.equalsIgnoreCase(delete) && !UP.equalsIgnoreCase(delete) && !CLEAR.equalsIgnoreCase(delete)) {
144+ log.warn("{} is illegal argument. set default: {}", DELETE, LEFT);
145+ delete = LEFT;
146+ }
147+ else {
148+ log.debug("delete: {}", delete);
149+ delete = delete.toLowerCase().trim();
150+ }
151+
152+ switch(delete) {
153+ // クリア
154+ case CLEAR:
155+ ExcelUtil.clearRange(sheet, (new CellRangeAddress(cell.getRowIndex(), cell.getRowIndex() + rowSpan - 1, cell.getColumnIndex(), cell.getColumnIndex() + colSpan - 1)), false, false);
156+ break;
157+ // 左詰め
158+ case LEFT:
159+ // IF範囲以降をずらす
160+ ExcelUtil.shift(sheet, new CellRangeAddress(cell.getRowIndex(), cell.getRowIndex() + rowSpan - 1, cell.getColumnIndex() + colSpan, cell.getColumnIndex() + colSpan), COL, -colSpan, block);
161+ break;
162+ // 上詰め
163+ case UP:
164+ // IF範囲以降をずらす
165+ ExcelUtil.shift(sheet, new CellRangeAddress(cell.getRowIndex() + rowSpan, cell.getRowIndex() + rowSpan, cell.getColumnIndex(), cell.getColumnIndex() + colSpan - 1), ROW, -rowSpan, block);
166+ }
167+ }
168+ log.trace("parse end");
169+ }
170+}
--- /dev/null
+++ b/Jaxcel/src/org/hanei/jaxcel/parser/TLParser.java
@@ -0,0 +1,38 @@
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 org.hanei.jaxcel.report.JaxcelContext;
22+
23+/**
24+ * テンプレートパーサーのインターフェイス<br>
25+ *
26+ * @since 1.01.00
27+ * @author Noboru Saito
28+ */
29+public interface TLParser {
30+
31+ /**
32+ * 対象セルのテンプレート指示子をパースする
33+ *
34+ * @param context
35+ */
36+ public void parse(JaxcelContext context);
37+
38+}
--- a/Jaxcel/src/org/hanei/jaxcel/report/ELManager.java
+++ b/Jaxcel/src/org/hanei/jaxcel/report/ELManager.java
@@ -35,7 +35,7 @@ import org.slf4j.LoggerFactory;
3535 * EL式の解析、解析で使用するパラメータオブジェクトの保持を行う。<br>
3636 * 式言語エンジンにはJEXLを使用。
3737 *
38- * @version 1.00.00
38+ * @since 1.00.00
3939 * @author noboru saito
4040 *
4141 */
--- a/Jaxcel/src/org/hanei/jaxcel/report/JaxcelContext.java
+++ b/Jaxcel/src/org/hanei/jaxcel/report/JaxcelContext.java
@@ -18,17 +18,25 @@
1818 */
1919 package org.hanei.jaxcel.report;
2020
21+import java.util.Map;
22+
23+import org.apache.poi.ss.usermodel.Cell;
2124 import org.apache.poi.ss.usermodel.Sheet;
2225
2326 /**
2427 * Jaxcelコンテキスト
2528 *
26- * @version 1.00.00
29+ * @since 1.00.00
2730 * @author noboru saito
2831 */
2932 public class JaxcelContext {
3033
3134 private Sheet currentSheet = null;
35+ private Cell currentCell = null;
36+ private String parseString = null;
37+ private Map<String, Object> attributeMap =null;
38+ private String[] dateFormat = null;
39+ private String[] timeFormat = null;
3240 private ELManager elManager = null;
3341
3442 /**
@@ -55,6 +63,96 @@ public class JaxcelContext {
5563 }
5664
5765 /**
66+ * 参照中のCellオブジェクトを返却
67+ *
68+ * @return 参照中のCellオブジェクト
69+ */
70+ public Cell getCurrentCell() {
71+ return currentCell;
72+ }
73+
74+ /**
75+ * 参照中とするCellオブジェクトを設定
76+ *
77+ * @param currentCell Cellオブジェクト
78+ */
79+ public void setCurrentCell(Cell currentCell) {
80+ this.currentCell = currentCell;
81+ }
82+
83+ /**
84+ * パース対象の指示子文字列を返却
85+ *
86+ * @return パース対象の文字列
87+ */
88+ public String getParseString() {
89+ return parseString;
90+ }
91+
92+ /**
93+ * パース対象とする指示子文字列を設定
94+ *
95+ * @param parseString パース対象の文字列
96+ */
97+ public void setParseString(String parseString) {
98+ this.parseString = parseString;
99+ }
100+
101+ /**
102+ * パース対象の指示子属性情報Mapオブジェクトを返却
103+ *
104+ * @return Mapオブジェクト
105+ */
106+ public Map<String, Object> getAttributeMap() {
107+ return attributeMap;
108+ }
109+
110+ /**
111+ * パース対象の指示子属性情報Mapオブジェクトを設定
112+ *
113+ * @param attributeMap Mapオブジェクト
114+ */
115+ public void setAttributeMap(Map<String, Object> attributeMap) {
116+ this.attributeMap = attributeMap;
117+ }
118+
119+ /**
120+ * 日付・日付時刻フォーマット文字列配列を返却
121+ *
122+ * @return 日付・日付時刻フォーマット文字列配列
123+ */
124+ public String[] getDateFormat() {
125+ return dateFormat;
126+ }
127+
128+ /**
129+ * 日付・日付時刻フォーマット文字列配列を設定
130+ *
131+ * @param dateFormat 日付・日付時刻フォーマット文字列配列
132+ */
133+ public void setDateFormat(String[] dateFormat) {
134+ this.dateFormat = dateFormat;
135+ }
136+
137+ /**
138+ * 時刻フォーマット文字列配列を返却
139+ *
140+ * @return 時刻フォーマット文字列配列
141+ */
142+ public String[] getTimeFormat() {
143+ return timeFormat;
144+ }
145+
146+ /**
147+ * 時刻フォーマット文字列配列を設定
148+ *
149+ * @param dateFormat 時刻フォーマット文字列配列
150+ */
151+ public void setTimeFormat(String[] timeFormat) {
152+ this.timeFormat = timeFormat;
153+ }
154+
155+ /**
58156 * ELManagerオブジェクトを返却
59157 *
60158 * @return ELManagerオブジェクト
@@ -71,5 +169,5 @@ public class JaxcelContext {
71169 public void setElManager(ELManager elManager) {
72170 this.elManager = elManager;
73171 }
74-
172+
75173 }
--- a/Jaxcel/src/org/hanei/jaxcel/report/ReportMaker.java
+++ b/Jaxcel/src/org/hanei/jaxcel/report/ReportMaker.java
@@ -44,6 +44,7 @@ import org.apache.poi.xssf.usermodel.XSSFDialogsheet;
4444 import org.apache.poi.xssf.usermodel.XSSFWorkbook;
4545 import org.hanei.jaxcel.exception.JaxcelInputException;
4646 import org.hanei.jaxcel.exception.JaxcelOutputException;
47+import org.hanei.jaxcel.parser.CellParser;
4748 import org.slf4j.LoggerFactory;
4849 import org.slf4j.Logger;
4950
@@ -159,7 +160,7 @@ import org.slf4j.Logger;
159160 * </tr>
160161 * </table>
161162 *
162- * @version 1.00.00
163+ * @since 1.00.00
163164 * @author Noboru Saito
164165 */
165166 public class ReportMaker {
@@ -187,6 +188,11 @@ public class ReportMaker {
187188 * コンテキスト
188189 */
189190 private JaxcelContext context = null;
191+
192+ /**
193+ * セルパーサ
194+ */
195+ private CellParser cellParser = null;
190196
191197 /**
192198 * テンプレート一時ファイル
@@ -596,6 +602,9 @@ public class ReportMaker {
596602 private void makeBook(Workbook book) {
597603 log.trace("makeBook start");
598604
605+ // セルパーサ生成
606+ cellParser = new CellParser(context);
607+
599608 // シートでループ
600609 log.debug("sheet count: {}", book.getNumberOfSheets());
601610 for(int i = 0; i < book.getNumberOfSheets(); i++) {
@@ -635,11 +644,9 @@ public class ReportMaker {
635644
636645 Row row; // 行オブジェクト
637646 Cell cell; // セルブジェクト
638- TLParser tlParser; // パーサ
639647
640648 // カレントシート設定、パーサ生成
641649 context.setCurrentSheet(sheet);
642- tlParser = new TLParser(context);
643650
644651 //最大行数
645652 int lastRowNum = sheet.getLastRowNum();
@@ -682,9 +689,9 @@ public class ReportMaker {
682689 case Cell.CELL_TYPE_STRING:
683690 case Cell.CELL_TYPE_FORMULA:
684691 // パース
685- tlParser.parse(cell);
692+ cellParser.parse(cell);
686693 // 再パースフラグONなら
687- if(tlParser.isReParseCell()) {
694+ if(cellParser.isReParseCell()) {
688695 // もう一度そのセルからループする
689696 cellIdx--;
690697 }
--- a/Jaxcel/src/org/hanei/jaxcel/report/TLParser.java
+++ b/Jaxcel/src/org/hanei/jaxcel/report/TLParser.java
@@ -53,8 +53,9 @@ import org.slf4j.Logger;
5353 /**
5454 * Excelテンプレートシートの指示子(Template Language)、EL式(Expression Language)の検索、パースを行う
5555 *
56+ * @since 1.00.00
5657 * @author Noboru Saito
57- *
58+ * @deprecated
5859 */
5960 public class TLParser {
6061
@@ -1444,7 +1445,7 @@ public class TLParser {
14441445 * @return 置換後の文字列
14451446 */
14461447 private String replaceFirst(String replacement) {
1447- return mtAll.replaceFirst(replacement == null ? "" : replacement);
1448+ return mtAll.replaceFirst(replacement == null ? "" : replacement.replace("\\", "\\\\").replace("$", "\\$"));
14481449 }
14491450
14501451 /**
--- a/Jaxcel/src/org/hanei/jaxcel/util/ExcelUtil.java
+++ b/Jaxcel/src/org/hanei/jaxcel/util/ExcelUtil.java
@@ -21,6 +21,9 @@ package org.hanei.jaxcel.util;
2121 import java.lang.reflect.Field;
2222 import java.util.ArrayList;
2323 import java.util.List;
24+import java.util.regex.Matcher;
25+import java.util.regex.Pattern;
26+import java.util.regex.PatternSyntaxException;
2427
2528
2629 import org.apache.poi.POIXMLDocumentPart;
@@ -62,7 +65,7 @@ import org.slf4j.Logger;
6265 /**
6366 * Excelユーティリティクラス
6467 *
65- * @version 1.00.00
68+ * @since 1.00.00
6669 * @author Noboru Saito
6770 */
6871 public class ExcelUtil {
@@ -655,8 +658,9 @@ public class ExcelUtil {
655658 if(!validateRange(sheet, range)) {
656659 _range = getIntersectRange(sheet, range);
657660 if(_range == null) {
658- log.error("range is illegal: [{}]", range.formatAsString());
659- throw new JaxcelInputException("range is illegal");
661+ log.warn("range is illegal: [{}]", range.formatAsString());
662+ log.trace("clearRange end");
663+ return;
660664 }
661665 else {
662666 log.info("resize range: [{}]", _range.formatAsString());
@@ -1430,4 +1434,98 @@ public class ExcelUtil {
14301434 }
14311435 }
14321436 }
1437+
1438+ /**
1439+ * セルのアドレス比較<br>
1440+ *
1441+ * @param cellA 対象セルA
1442+ * @param cellB 対象セルB
1443+ * @param isCheckSheet trueの場合、シート名を含め比較する
1444+ *
1445+ * @return 判定結果<br>
1446+ * 対象セルのいずれかでもnullの場合はfalse
1447+ *
1448+ * @since 1.01.00
1449+ */
1450+ public static boolean equalsCellAddress(Cell cellA, Cell cellB, boolean isCheckSheet) {
1451+
1452+ if(cellA == null || cellB == null) return false;
1453+
1454+ if(isCheckSheet) {
1455+ if(!(cellA.getSheet().getSheetName().equals(cellB.getSheet().getSheetName()))) {
1456+ return false;
1457+ }
1458+ }
1459+ return cellA.getColumnIndex() == cellB.getColumnIndex() &&
1460+ cellA.getRowIndex() == cellB.getRowIndex();
1461+
1462+ }
1463+
1464+ /**
1465+ * セルの文字列・数式から指定された正規表現の最初の部分を指定された文字列に置換した結果を返却<br>
1466+ *
1467+ * replacement内でのバックスラッシュ (\) とドル記号 ($) は、String.replaceFirstと同様に作用します。
1468+ *
1469+ * @param cell 対象セル
1470+ * @param target 置換される文字列
1471+ * @param replacement 置き換える文字列
1472+ * @param regex trueの場合、targetを正規表現として扱う
1473+ *
1474+ * @return 置換後のセル文字列・計算式<br>
1475+ * 対象セルがnullの場合はnull<br>
1476+ * 対象セルのセルタイプが文字列・数式以外の場合はnull
1477+ *
1478+ * @since 1.01.00
1479+ */
1480+ public static String replaceFirstCellValue(Cell cell, String target, String replacement, boolean regex) {
1481+ // チェック
1482+ if(cell == null) return null;
1483+ String _replacement = replacement == null ? "" : replacement;
1484+ String _return = null;
1485+ // セルタイプ毎処理
1486+ switch(cell.getCellType()) {
1487+ case Cell.CELL_TYPE_STRING:
1488+ if(target == null) _return = cell.getStringCellValue();
1489+ try {
1490+ _return = cell.getStringCellValue().replaceFirst(regex ? target : Pattern.quote(target), _replacement);
1491+ }
1492+ catch(PatternSyntaxException e) {
1493+ log.warn("replaceFirst error. {}", e.getMessage());
1494+ }
1495+ break;
1496+ case Cell.CELL_TYPE_FORMULA:
1497+ if(target == null) _return = cell.getCellFormula();
1498+ try {
1499+ _return = cell.getCellFormula().replaceFirst(regex ? target : Pattern.quote(target), _replacement);
1500+ }
1501+ catch(PatternSyntaxException e) {
1502+ log.warn("replaceFirst error. {}", e.getMessage());
1503+ }
1504+ break;
1505+ default:
1506+ // セルタイプが上記以外の場合はnull返却
1507+ log.warn("cell type is not strng or formula");
1508+ return null;
1509+ }
1510+ if(_return == null) _return = "";
1511+ return _return;
1512+ }
1513+
1514+ /**
1515+ * セルの文字列・数式から指定された文字列の最初の部分を指定された文字列に置換した結果を返却<br>
1516+ * 引数のtarget, replacementともに正規表現として扱いません。
1517+ *
1518+ * @param cell 対象セル
1519+ * @param target 置換される文字列
1520+ * @param replacement 置き換える文字列
1521+ *
1522+ * @return 置換後のセル文字列・計算式<br>
1523+ * 対象セルがnullの場合はnull<br>
1524+ * 対象セルのセルタイプが文字列・数式以外の場合はnull
1525+ *
1526+ * @since 1.01.00
1527+ */
1528+ public static String replaceFirstCellValue(Cell cell, String target, String replacement) {
1529+ return replaceFirstCellValue(cell, target, Matcher.quoteReplacement(replacement), false);
1530+ }
14331531 }
--- a/Jaxcel/src/org/hanei/jaxcel/util/MakeReportTool.java
+++ b/Jaxcel/src/org/hanei/jaxcel/util/MakeReportTool.java
@@ -30,7 +30,7 @@ import org.slf4j.LoggerFactory;
3030 /**
3131 * Excel帳票出力コマンドラインツールクラス
3232 *
33- * @version 1.00.00
33+ * @since 1.00.00
3434 * @author Noboru Saito
3535 */
3636 public class MakeReportTool {
Show on old repository browser