OSDN Git Service

#34479 対応中
authornoboru saitoh <msnobosan@gmal.com>
Wed, 12 Nov 2014 16:38:41 +0000 (01:38 +0900)
committernoboru saitoh <msnobosan@gmal.com>
Wed, 12 Nov 2014 16:38:41 +0000 (01:38 +0900)
20 files changed:
Jaxcel/.classpath
Jaxcel/lib/commons-collections-3.2.1.jar [new file with mode: 0644]
Jaxcel/src/jaxcel.properties [new file with mode: 0644]
Jaxcel/src/org/hanei/jaxcel/example/MakeReportWithObject.java
Jaxcel/src/org/hanei/jaxcel/exception/JaxcelInputException.java
Jaxcel/src/org/hanei/jaxcel/exception/JaxcelOutputException.java
Jaxcel/src/org/hanei/jaxcel/exception/JaxcelRuntimeException.java
Jaxcel/src/org/hanei/jaxcel/parser/AbstractTLParser.java [new file with mode: 0644]
Jaxcel/src/org/hanei/jaxcel/parser/CellParser.java [new file with mode: 0644]
Jaxcel/src/org/hanei/jaxcel/parser/Constants.java [new file with mode: 0644]
Jaxcel/src/org/hanei/jaxcel/parser/ELParser.java [new file with mode: 0644]
Jaxcel/src/org/hanei/jaxcel/parser/ForeachParser.java [new file with mode: 0644]
Jaxcel/src/org/hanei/jaxcel/parser/IfParser.java [new file with mode: 0644]
Jaxcel/src/org/hanei/jaxcel/parser/TLParser.java [new file with mode: 0644]
Jaxcel/src/org/hanei/jaxcel/report/ELManager.java
Jaxcel/src/org/hanei/jaxcel/report/JaxcelContext.java
Jaxcel/src/org/hanei/jaxcel/report/ReportMaker.java
Jaxcel/src/org/hanei/jaxcel/report/TLParser.java
Jaxcel/src/org/hanei/jaxcel/util/ExcelUtil.java
Jaxcel/src/org/hanei/jaxcel/util/MakeReportTool.java

index 4cd2b4e..a26ba6f 100644 (file)
        </classpathentry>
        <classpathentry kind="lib" path="lib/xmlbeans-2.6.0.jar"/>
        <classpathentry kind="lib" path="lib/commons-codec-1.9.jar"/>
+       <classpathentry kind="lib" path="lib/commons-collections-3.2.1.jar"/>
+       <classpathentry kind="lib" path="lib/dom4j-1.6.1.jar"/>
+       <classpathentry kind="lib" path="lib/commons-logging-1.2.jar"/>
+       <classpathentry kind="lib" path="lib/log4j-api-2.0.2.jar"/>
+       <classpathentry kind="lib" path="lib/log4j-core-2.0.2.jar"/>
+       <classpathentry kind="lib" path="lib/log4j-jcl-2.0.2.jar"/>
+       <classpathentry kind="lib" path="lib/log4j-slf4j-impl-2.0.2.jar"/>
        <classpathentry kind="con" path="org.eclipse.jst.j2ee.internal.module.container"/>
        <classpathentry kind="con" path="org.eclipse.jst.server.core.container/org.eclipse.jst.server.tomcat.runtimeTarget/Apache Tomcat v7.0">
                <attributes>
diff --git a/Jaxcel/lib/commons-collections-3.2.1.jar b/Jaxcel/lib/commons-collections-3.2.1.jar
new file mode 100644 (file)
index 0000000..c35fa1f
Binary files /dev/null and b/Jaxcel/lib/commons-collections-3.2.1.jar differ
diff --git a/Jaxcel/src/jaxcel.properties b/Jaxcel/src/jaxcel.properties
new file mode 100644 (file)
index 0000000..2e80136
--- /dev/null
@@ -0,0 +1,45 @@
+#EL\u5f0f\u5b9a\u7fa9
+el.class = org.hanei.jaxcel.parser.ELParser
+el.directive = $
+el.bracket.start = {
+el.bracket.end = }
+
+#\u65e5\u4ed8\u30fb\u6642\u523b\u30d5\u30a9\u30fc\u30de\u30c3\u30c8\u5b9a\u7fa9
+format.date = yyyy-MM-dd'T'HH:mm:ss.SSSXXX
+format.date = yyyy-MM-dd'T'HH:mm:ss.SSXXX
+format.date = yyyy-MM-dd'T'HH:mm:ss.SXXX
+format.date = yyyy-MM-dd'T'HH:mm:ss.SSSXX
+format.date = yyyy-MM-dd'T'HH:mm:ss.SSXX
+format.date = yyyy-MM-dd'T'HH:mm:ss.SXX
+format.date = yyyy-MM-dd'T'HH:mm:ss.SXXX
+format.date = yyyy-MM-dd'T'HH:mm:ss.SXX
+format.date = yyyy-MM-dd'T'HH:mm:ss.SX
+format.date = yyyy-MM-dd'T'HH:mm:ssXXX
+format.date = yyyy-MM-dd'T'HH:mm:ssXX
+format.date = yyyy-MM-dd'T'HH:mm:ssX
+format.date = yyyy-MM-dd'T'HH:mm:ss.S
+format.date = yyyy-MM-dd'T'HH:mm:ss
+format.date = yyyy-MM-dd'T'HH:mm
+format.date = yyyy-MM-dd\ HH:mm:ss.S
+format.date = yyyy-MM-dd\ HH:mm:ss
+format.date = yyyy-MM-dd\ HH:mm
+format.date = yyyy-MM-dd
+format.date = yyyy/MM/dd\ HH:mm:ss.S
+format.date = yyyy/MM/dd\ HH:mm:ss
+format.date = yyyy/MM/dd\ HH:mm
+format.date = yyyy/MM/dd
+format.time = HH:mm:ss.S
+format.time = HH:mm:ss
+format.time = HH:mm
+       
+#\u6307\u793a\u5b50\u5b9a\u7fa9
+tl.directive = foreach
+tl.directive = if
+tl.bracket.start = (
+tl.bracket.end = )
+
+#foreach
+tl.foreach.class = org.hanei.jaxcel.parser.ForeachParser
+
+#if
+tl.if.class = org.hanei.jaxcel.parser.IfParser
index d7c39c2..6ea88e3 100644 (file)
@@ -33,7 +33,7 @@ import org.slf4j.LoggerFactory;
 /**
  * JavaオブジェクトからのExcel帳票出力サンプルクラス
  * 
- * @version 1.00.00
+ * @since 1.00.00
  * @author Noboru Saito
  */
 public class MakeReportWithObject {
index 401d718..97ead85 100644 (file)
 package org.hanei.jaxcel.exception;
 
 /**
- * Jaxcelインプット例外クラス<br>
+ * Jaxcelインプット実行時例外クラス<br>
  * 引数チェックエラー、ファイルオープンエラー等発生時の例外
  * 
- * @version 1.00.00
+ * @since 1.00.00
  * @author Noboru Saito
  */
 public class JaxcelInputException extends JaxcelRuntimeException {
index 6208ee3..8186686 100644 (file)
 package org.hanei.jaxcel.exception;
 
 /**
- * Jaxcelアウトプット例外クラス<br>
+ * Jaxcelアウトプット実行時例外クラス<br>
  * ファイル出力、クローズエラー等発生時の例外
  * 
- * @version 1.00.00
+ * @since 1.00.00
  * @author Noboru Saito
  */
 public class JaxcelOutputException extends JaxcelRuntimeException {
index a3d70b7..e5c9ec2 100644 (file)
@@ -19,9 +19,9 @@
 package org.hanei.jaxcel.exception;
 
 /**
- * Jaxcel例外基底クラス
+ * Jaxcel実行時例外基底クラス
  * 
- * @version 1.00.00
+ * @since 1.00.00
  * @author Noboru Saito
  */
 public class JaxcelRuntimeException extends RuntimeException {
diff --git a/Jaxcel/src/org/hanei/jaxcel/parser/AbstractTLParser.java b/Jaxcel/src/org/hanei/jaxcel/parser/AbstractTLParser.java
new file mode 100644 (file)
index 0000000..735f02a
--- /dev/null
@@ -0,0 +1,39 @@
+/**
+ * Copyright 2014 Hanei Management Co.,Ltd. 
+ * 
+ * This file is part of Jaxcel
+ * 
+ *  Jaxcel is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU Lesser General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  Jaxcel is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package org.hanei.jaxcel.parser;
+
+import org.hanei.jaxcel.report.JaxcelContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * テンプレートパーサー抽象クラス<br>
+ * テンプレートパーサーはこのクラスを継承する必要があります。
+ * 
+ * @since 1.01.00
+ * @author Noboru Saito
+ */
+public abstract class AbstractTLParser implements TLParser {
+
+       protected static final Logger log = LoggerFactory.getLogger(Thread.currentThread().getStackTrace()[1].getClassName());
+
+       @Override
+       public abstract void parse(JaxcelContext context);
+
+}
diff --git a/Jaxcel/src/org/hanei/jaxcel/parser/CellParser.java b/Jaxcel/src/org/hanei/jaxcel/parser/CellParser.java
new file mode 100644 (file)
index 0000000..effb048
--- /dev/null
@@ -0,0 +1,578 @@
+/**
+ * Copyright 2014 Hanei Management Co.,Ltd. 
+ * 
+ * This file is part of Jaxcel
+ * 
+ *  Jaxcel is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU Lesser General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  Jaxcel is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package org.hanei.jaxcel.parser;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.text.CharacterIterator;
+import java.text.StringCharacterIterator;
+import java.util.ArrayDeque;
+import java.util.Deque;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.commons.collections.ExtendedProperties;
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.util.CellReference;
+import org.hanei.jaxcel.exception.JaxcelInputException;
+import org.hanei.jaxcel.report.JaxcelContext;
+import org.hanei.jaxcel.util.ExcelUtil;
+import org.slf4j.LoggerFactory;
+import org.slf4j.Logger;
+
+/**
+ * Excelテンプレートシートの指示子(Template Language)、EL式(Expression Language)の検索、パースを行う
+ * 
+ * @since 1.01.00
+ * @author Noboru Saito
+ *
+ */
+public class CellParser {
+
+       private static final Logger log = LoggerFactory.getLogger(CellParser.class);
+
+       // プロパティファイル キー定義
+       private static final String DEFAULT_PROPS_NAME = "/jaxcel.properties";
+       private static final String P_EL_CLASS = "el.class";
+       private static final String P_EL_DIRECTIVE = "el.directive";
+       private static final String P_EL_BRACKET_START = "el.bracket.start";
+       private static final String P_EL_BRACKET_END = "el.bracket.end";
+       private static final String P_TL_DIRECTIVE = "tl.directive";
+       private static final String P_TL_PREFIX = "tl.";
+       private static final String P_CLASS_SUFFIX = ".class";
+       private static final String P_TL_BRACKET_START = "tl.bracket.start";
+       private static final String P_TL_BRACKET_END = "tl.bracket.end";
+       private static final String P_DATE_FORMAT = "format.date";
+       private static final String P_TIME_FORMAT = "format.time";
+
+       // 文字・文字列定義
+       private static final String TL_START_SYMBOL = "#";
+       private static final char VALUE_BRACKET = '"';
+       private static final char ATTR_DELIMITER = ':';
+       private static final char ESCAPE_CHAR = '\\';
+       private static final char EM_SPACE = ' ';
+
+       private static final int PARSE_MAX = 10;
+
+       private ExtendedProperties config = null;
+       private Map<String, TLParser> parserMap = null;
+       private JaxcelContext context = null;
+       private Cell cell = null;
+       private char elDirective;
+
+       private boolean reParseFlg;     // 同一セル再パース要否フラグ
+       private int parseCount;         // 同一セルパース回数
+       private char elStartBracket;
+       private char elEndBracket;
+       private char tlEndBracket;
+       private String matchPattern = "";
+
+       private StringCharacterIterator valueIterator = new StringCharacterIterator("");
+       private Pattern ptAll = null;
+       private Matcher mtAll = null;
+
+       /**
+        * コンストラクタ
+        * 
+        * @param context       Jaxlsコンテキストオブジェクト
+        * 
+        * @throws JaxcelInputException デフォルトプロパティファイル入力例外発生時
+        */
+       public CellParser(JaxcelContext context) {
+               // デフォルトプロパティロード
+               try(InputStream inStream = getClass().getResourceAsStream(DEFAULT_PROPS_NAME)) {
+                       config = new ExtendedProperties();
+                       config.load(inStream);
+                       log.debug("default properties load");
+               } catch (IOException e) {
+                       throw new JaxcelInputException("default properties load error");
+               }
+               
+               // コンテキスト取得
+               this.context = context;
+               if(this.context != null) {
+                       //TODO: 外部プロパティロードを予定。デフォルトプロパティ上書く
+               }
+               
+               // 初期処理
+               init();
+       }
+       
+       
+       /**
+        * 初期処理
+        */
+       private void init() {
+               log.trace("init start");
+               
+               // パーサ ロード
+               parserMap = new HashMap<>();
+               try {
+                       // 日付・時刻書式
+                       context.setDateFormat(config.getStringArray(P_DATE_FORMAT));
+                       context.setTimeFormat(config.getStringArray(P_TIME_FORMAT));
+                       // EL式パーサ
+                       Class<?> cls = Class.forName(config.getString(P_EL_CLASS));
+                       TLParser parser = (TLParser) cls.newInstance();
+                       elDirective = config.getString(P_EL_DIRECTIVE).charAt(0);
+                       elStartBracket = config.getString(P_EL_BRACKET_START).charAt(0);
+                       elEndBracket   = config.getString(P_EL_BRACKET_END).charAt(0);
+                       parserMap.put(String.valueOf(elDirective) + String.valueOf(elStartBracket), parser);
+                       log.debug("ELParser load");
+
+                       tlEndBracket = config.getString(P_TL_BRACKET_END).charAt(0);
+                       for(String tlDirective : config.getStringArray(P_TL_DIRECTIVE)) {
+                               cls = Class.forName(config.getString(P_TL_PREFIX + tlDirective + P_CLASS_SUFFIX));
+                               parser = (TLParser) cls.newInstance();
+                               parserMap.put(TL_START_SYMBOL + tlDirective + config.getString(P_TL_BRACKET_START), parser);
+                               log.debug("{} load", cls.getSimpleName());
+                       }
+               }
+               catch(Exception e) {
+                       log.error("TLParser load error. {}", e.getMessage(), e);
+               }
+
+               // 正規表現Pattern生成
+               for(String key : parserMap.keySet()) {
+                       matchPattern += (matchPattern.length() > 0 ? "|" : "") + Pattern.quote(key);
+               }
+               log.debug("matchPattern: {}", matchPattern);
+               ptAll = Pattern.compile(matchPattern);
+
+               log.trace("init end");
+       }
+       
+       /**
+        * セルのパース
+        * 
+        * @param cell  対象セル
+        * 
+        * @throws JaxcelInputException 入力例外発生時
+        */
+       public void parse(Cell cell) {
+               log.trace("parse start");
+
+               // チェック
+               if(cell == null) {
+                       // 再パースフラグクリア
+                       this.cell = null;
+                       reParseFlg = false;             
+                       parseCount = 0;
+                       log.debug("cell is null");
+                       log.trace("parse end");
+                       return;
+               }
+               
+               // パース初回の場合セルを保持
+               if(this.cell == null) {
+                       log.debug("start new cell parse");
+                       this.cell = cell;
+                       // 再パースフラグ・カウントクリア
+                       reParseFlg = false;
+                       parseCount = 0;
+               }
+               // 保持しているセルと同一セルのパースの場合
+               else if(ExcelUtil.equalsCellAddress(this.cell, cell, true)) {
+                       // 再パースカウント加算
+                       parseCount++;
+                       // 再パース回数チェック
+                       if(parseCount >= PARSE_MAX) {
+                               log.warn("parse repeat count over");
+                               // 再パースフラグクリア
+                               reParseFlg = false;
+                               log.trace("parse end");
+                               return;
+                       }
+                       log.debug("start repeat cell parse");
+               }
+               // 保持しているセルと異なるセルのパースの場合
+               else {
+                       log.debug("start new cell parse");
+                       // セルを保持
+                       this.cell = cell;
+                       // 再パースフラグ・カウントクリア
+                       reParseFlg = false;
+                       parseCount = 0;
+               }
+                               
+               // 文字列・計算式の場合は値保持。以外は終了
+               String cellValue = null;
+               switch(cell.getCellType()){
+               case Cell.CELL_TYPE_STRING:
+                       cellValue = cell.getStringCellValue();
+                       log.debug("cell[{}] cellType: string  value: {}", (new CellReference(cell)).formatAsString(), cellValue);
+                       break;
+               case Cell.CELL_TYPE_FORMULA:
+                       cellValue = cell.getCellFormula();
+                       log.debug("cell[{}] cellType: formula  value: {}", (new CellReference(cell)).formatAsString(), cellValue);
+                       break;
+               default:
+                       log.debug("cell type is not string or formula");
+                       // 再パースフラグクリア
+                       reParseFlg = false;
+                       log.trace("parse end");
+                       return;
+               }
+
+               // 解析開始
+               TLParser parser = null;
+               Map<String, Object> attributeMap = new HashMap<>();
+               String matchString = null;
+               String mapKey = null;
+               StringBuilder strBuf = new StringBuilder(1024);
+               StringBuilder parseString = new StringBuilder(1024);
+               Deque<Character> bracketStack = new ArrayDeque<>();
+               boolean attrValueFlag, expressionFlag;
+               boolean findFlag = false;
+               boolean escapeFlag = false;
+               boolean keyParseFlag = false;
+               
+               // 指示子(TL)の検索
+               mtAll = ptAll.matcher(cellValue);
+               // 指示子検索ループ
+               findLoop:
+               while(mtAll.find()) {
+                       matchString = mtAll.group();
+                       log.debug("match: {}", matchString);
+                       
+                       // パーサ取得
+                       parser = parserMap.get(matchString);
+                       if(parser == null) {
+                               log.warn("parser is null");
+                               continue;
+                       }
+                       log.debug("parser: {}", parser.getClass().getSimpleName());
+                       
+                       // クリア
+                       parseString.setLength(0);
+                       parseString.append(matchString);
+                       strBuf.setLength(0);
+                       bracketStack.clear();
+                       attributeMap.clear();
+                       expressionFlag = false;
+                       attrValueFlag  = false;
+                       keyParseFlag = false;
+                       escapeFlag = false;
+
+                       // 指示子がEL式
+                       if(matchString.equals(String.valueOf(elDirective) + String.valueOf(elStartBracket))) {
+                               // elValueFlag ON
+                               expressionFlag = true;
+                               log.debug("expressionFlag: {}", expressionFlag);
+                               // 括弧スタックに開始括弧push
+                               bracketStack.offerFirst(elStartBracket);
+                       }
+                       
+                       // 開始括弧以降読込み
+                       valueIterator.setText(cellValue);
+                       for(char c = valueIterator.setIndex(mtAll.start() + matchString.length()); c != CharacterIterator.DONE; c = valueIterator.next()) {
+                               // カーソル文字抜出し
+                               parseString.append(c);
+                               log.trace("current char: {}", c);
+                               // EL式解析中
+                               if(expressionFlag) {
+                                       log.trace("parsing EL");
+                                       // カーソル文字がEL式終端括弧であれば
+                                       if(elEndBracket == c) {
+                                               // 括弧スタックpop
+                                               bracketStack.pollFirst();
+                                               // 括弧スタック空ならEL式解析終了
+                                               if(bracketStack.size() == 0) {
+                                                       // Mapに登録
+                                                       attributeMap.put(Constants.EXPRESSION, getTrimString(strBuf));
+                                                       log.debug("attributeMap : {}", attributeMap);                           
+                                                       // 指示子がEL式なら解析終了
+                                                       if(matchString.equals(String.valueOf(elDirective) + String.valueOf(elStartBracket))) {
+                                                               log.trace("parse end");
+                                                               findFlag = true;
+                                                               break findLoop;
+                                                       }
+                                                       // 指示子がEL式以外なら
+                                                       else {
+                                                               log.trace("EL parse end");
+                                                               // フラグクリア
+                                                               expressionFlag = false;
+                                                               // バッファクリア
+                                                               strBuf.setLength(0);
+                                                       }
+                                               }
+                                               // 括弧スタック空でないならバッファに保持
+                                               else {
+                                                       strBuf.append(c);
+                                                       log.trace("parsing: {}", strBuf.toString());
+                                               }
+                                       }
+                                       // EL式終端括弧以外
+                                       else {
+                                               // EL式開始括弧
+                                               if(elStartBracket == c) {
+                                                       // 括弧スタックにpush
+                                                       bracketStack.offerFirst(c);
+                                                       log.trace("bracketStack: {}", bracketStack.size());
+                                               }
+                                               // バッファに保持
+                                               strBuf.append(c);
+                                               log.trace("parsing: {}", strBuf.toString());
+                                       }
+                               }
+                               // 属性値解析中
+                               else if(attrValueFlag) {
+                                       log.trace("parsing attrValue");
+                                       // カーソル文字がダブルクォートであれば
+                                       if(VALUE_BRACKET == c) {
+                                               log.trace("escapeFlag : {}", escapeFlag);                               
+                                               // エスケープ中なら
+                                               if(escapeFlag) {
+                                                       // 括弧とはみなさずバッファに保持
+                                                       strBuf.append(c);
+                                                       // エスケープフラグクリア
+                                                       escapeFlag = false;
+                                                       log.trace("parsing: {}", strBuf.toString());
+                                               }
+                                               // エスケープ中でないなら
+                                               else {
+                                                       // Mapに登録
+                                                       attributeMap.put(mapKey, strBuf.toString());
+                                                       log.debug("attributeMap : {}", attributeMap);
+                                                       // 属性値解析中フラグクリア
+                                                       attrValueFlag = false;
+                                                       // バッファクリア
+                                                       strBuf.setLength(0);
+                                                       mapKey = null;
+                                               }
+                                       }
+                                       // カーソル文字がダブルクォートでないなら
+                                       else {
+                                               log.trace("escapeFlag : {}", escapeFlag);
+                                               // エスケープ中なら
+                                               if(escapeFlag) {
+                                                       // エスケープフラグクリア
+                                                       escapeFlag = false;
+                                               }
+                                               // エスケープ中でなくエスケープ文字(\)なら
+                                               else if(ESCAPE_CHAR == c) {
+                                                       // エスケープフラグON
+                                                       escapeFlag = true;
+                                               }
+                                               // バッファに保持
+                                               strBuf.append(c);
+                                               log.trace("parsing: {}", strBuf.toString());
+                                       }
+                               }
+                               // カーソル文字がEL式指示字であれば
+                               else if(elDirective == c) {
+                                       log.trace("parsing elDirective");
+                                       // 次の文字のチェック
+                                       c = valueIterator.next();
+                                       log.trace("next char: {}", c);
+                                       // 次の文字がEL式開始括弧であれば
+                                       if(elStartBracket == c) {
+                                               parseString.append(c);
+                                               // EL式解析中フラグセット
+                                               expressionFlag = true;
+                                               log.trace("expressionFlag : {}", expressionFlag);
+                                               // バッファ空でなければ
+                                               if(strBuf.length() > 0) {
+                                                       // Mapに登録(値はnull)
+                                                       attributeMap.put(strBuf.toString(), null);
+                                                       log.debug("attributeMap : {}", attributeMap);
+                                               }
+                                               // バッファクリア
+                                               mapKey = null;
+                                               strBuf.setLength(0);
+                                       }
+                                       // 次の文字がEL式開始括弧でなければ
+                                       else {
+                                               // カーソルを戻しバッファに保持
+                                               c = valueIterator.previous();
+                                               log.trace("current char previous: {}", c);
+                                               // カーソルを戻しバッファに保持
+                                               strBuf.append(c);
+                                               log.trace("parsing: {}", strBuf.toString());
+                                       }
+                               }
+                               // カーソル文字が属性・属性値結合子であれば
+                               else if(ATTR_DELIMITER == c) {
+                                       log.trace("parsing attr delimiter");
+                                       // 属性文字列空でないなら
+                                       if(mapKey != null) {
+                                               // 属性解析済みフラグセット
+                                               keyParseFlag = true;
+                                               log.trace("keyParseFlag: {}", keyParseFlag);
+                                       }
+                                       // 属性文字列空なら
+                                       else {
+                                               // バッファ空でなければ
+                                               if(strBuf.length() > 0) {
+                                                       // 属性文字列保持
+                                                       mapKey = strBuf.toString();
+                                                       log.trace("mapKey: {}", mapKey);
+                                                       // Mapに登録(値はnull)
+                                                       attributeMap.put(mapKey, null);
+                                                       log.debug("attributeMap : {}", attributeMap);
+                                                       // バッファクリア
+                                                       strBuf.setLength(0);
+                                                       // 属性解析済みフラグセット
+                                                       keyParseFlag = true;
+                                                       log.trace("keyParseFlag: {}", keyParseFlag);
+                                               }
+                                               // 上記以外
+                                               else {
+                                                       // 解析異常終了
+                                                       log.warn("parse end");
+                                                       break;
+                                               }
+                                       }
+                               }
+                               // カーソル文字がダブルクォートであれば
+                               else if(VALUE_BRACKET == c) {
+                                       log.trace("parsing attr value bracket");
+                                       // 属性解析済みなら
+                                       if(keyParseFlag == true && mapKey != null) {
+                                               // 属性値解析中フラグセット
+                                               attrValueFlag = true;
+                                               keyParseFlag = false;
+                                               log.trace("attrValueFlag: {}", attrValueFlag);
+                                               log.trace("keyParseFlag: {}", keyParseFlag);
+                                               // バッファクリア
+                                               strBuf.setLength(0);
+                                       }
+                                       // 上記以外
+                                       else {
+                                               // 解析異常終了
+                                               log.warn("parse end");
+                                               break;
+                                       }
+                               }
+                               // 空白文字
+                               else if(Character.isWhitespace(c) || EM_SPACE == c) {
+                                       log.trace("parsing white space");
+                                       // バッファ空でなければ
+                                       if(strBuf.length() > 0) {
+                                               // Mapに登録(値はnull)
+                                               mapKey = strBuf.toString();
+                                               log.trace("mapKey: {}", mapKey);
+                                               attributeMap.put(mapKey, null);
+                                               log.debug("attributeMap : {}", attributeMap);
+                                               // バッファクリア
+                                               strBuf.setLength(0);
+                                       }
+                               }
+                               // 指示子閉じ括弧
+                               else if(tlEndBracket == c) {
+                                       log.trace("parsing tlEndBracket");
+                                       // バッファ空でなければ
+                                       if(strBuf.length() > 0) {
+                                               // Mapに登録(値はnull)
+                                               attributeMap.put(strBuf.toString(), null);
+                                               log.debug("attributeMap : {}", attributeMap);
+                                               // バッファクリア
+                                               mapKey = null;
+                                               strBuf.setLength(0);
+                                       }
+                                       log.trace("parse end");
+                                       findFlag = true;
+                                       break findLoop;
+                               }
+                               // 上記以外
+                               else {
+                                       // バッファに保持
+                                       strBuf.append(c);
+                                       log.trace("parsing: {}", strBuf.toString());
+                               }
+                       }
+               }
+
+               log.trace("findFlag: {}", findFlag);
+               // 指示子検索ヒット
+               if(findFlag) {
+                       // コンテキスト設定
+                       context.setCurrentCell(this.cell);
+                       context.setParseString(parseString.toString());
+                       context.setAttributeMap(attributeMap);
+                       if(log.isDebugEnabled()) {
+                               log.debug("parseString: {}", parseString);
+                               log.debug("attributeMap: {}", attributeMap);
+                       }
+                       try {
+                               // パーサでのパース
+                               parser.parse(context);
+
+                               // パース後のセルタイプチェック
+                               switch(this.cell.getCellType()){
+                               case Cell.CELL_TYPE_STRING:
+                               case Cell.CELL_TYPE_FORMULA:
+                                       // セルタイプが文字列、数式の場合は再パースフラグ設定
+                                       reParseFlg = true;
+                                       break;
+                               default:
+                                       // 上記以外は再パースフラグクリア
+                                       reParseFlg = false;
+                               }
+                       }
+                       catch(Exception e) {
+                               log.error("parser.parse error. {}", e.getMessage(), e);
+                               // 再パースフラグクリア
+                               reParseFlg = false;
+                       }
+               }
+               // 検索ヒットせず
+               else {
+                       log.debug("not found TL");
+                       // 再パースフラグクリア
+                       reParseFlg = false;
+               }
+               
+               log.trace("parse end: reParseFlg: {} parseCount: {}", reParseFlg, parseCount);
+       }
+       
+       /**
+        * 同一セルの再パース要否を返却
+        * 
+        * @return 再パース必要であればはtrue。不要であればfalse
+        */
+       public boolean isReParseCell() {
+               return reParseFlg;
+       }
+       
+       /**
+        * StringBuilderで保持している文字列のトリム
+        * 
+        * @param string 文字列
+        * @return トリム結果
+        */
+       private String getTrimString(StringBuilder string) {
+               if(string == null) return "";
+
+               int st = 0;
+               String str = string.toString();
+               char[] val = str.toCharArray();
+               int len = val.length;
+               
+               while ((st < len) && (val[st] <= ' ' || val[st] == ' ')) {
+                   st++;
+               }
+               while ((st < len) && (val[len - 1] <= ' ' || val[len - 1] == ' ')) {
+                   len--;
+               }
+               return ((st > 0) || (len <  val.length)) ? str.substring(st, len) : str;
+       }
+}
diff --git a/Jaxcel/src/org/hanei/jaxcel/parser/Constants.java b/Jaxcel/src/org/hanei/jaxcel/parser/Constants.java
new file mode 100644 (file)
index 0000000..c695c8d
--- /dev/null
@@ -0,0 +1,32 @@
+/**
+ * Copyright 2014 Hanei Management Co.,Ltd. 
+ * 
+ * This file is part of Jaxcel
+ * 
+ *  Jaxcel is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU Lesser General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  Jaxcel is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package org.hanei.jaxcel.parser;
+
+/**
+ * テンプレートパーサー用定数クラス<br>
+ * 
+ * @since 1.01.00
+ * @author Noboru Saito
+ */
+public class Constants {
+       public static final short FORMAT_GENERAL = 0;
+       public static final short FORMAT_TEXT = 49;
+       public static final String EXPRESSION = "_expression";
+
+}
diff --git a/Jaxcel/src/org/hanei/jaxcel/parser/ELParser.java b/Jaxcel/src/org/hanei/jaxcel/parser/ELParser.java
new file mode 100644 (file)
index 0000000..c6e393d
--- /dev/null
@@ -0,0 +1,247 @@
+/**
+ * Copyright 2014 Hanei Management Co.,Ltd. 
+ * 
+ * This file is part of Jaxcel
+ * 
+ *  Jaxcel is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU Lesser General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  Jaxcel is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package org.hanei.jaxcel.parser;
+
+import java.text.ParseException;
+import java.util.Date;
+import java.util.Locale;
+import java.util.TimeZone;
+import org.apache.commons.lang3.BooleanUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.math.NumberUtils;
+import org.apache.commons.lang3.time.DateUtils;
+import org.apache.poi.ss.formula.FormulaParseException;
+import org.apache.poi.ss.usermodel.Cell;
+import org.hanei.jaxcel.report.ELManager;
+import org.hanei.jaxcel.report.JaxcelContext;
+import org.hanei.jaxcel.util.ExcelUtil;
+
+/**
+ * EL式のパーサー実装<br>
+ * 
+ * @since 1.01.00
+ * @author Noboru Saito
+ */
+public class ELParser extends AbstractTLParser {
+
+       @Override
+       public void parse(JaxcelContext context) {
+               log.trace("parse start");
+
+               Object elResult;                                // パース結果保持用
+               String newValue;
+
+               Cell cell = context.getCurrentCell();
+               ELManager elMgr = context.getElManager();
+               
+               // evaluate
+               elResult = elMgr.evaluate((String) context.getAttributeMap().get(Constants.EXPRESSION));
+               if(elResult == null) {
+                       log.debug("evaluate result is null");
+               }
+               else {
+                       log.debug("evaluate result: {}", elResult.toString());
+               }
+
+               // マッチ1件目をevaluate結果で置換
+               newValue = ExcelUtil.replaceFirstCellValue(cell, context.getParseString(), (elResult == null ? "" : elResult.toString()));
+               log.debug("replace value: {}", newValue);
+
+               // newValueがnullの場合パース終了
+               if(newValue == null) {
+                       log.trace("parse end");
+                       return;
+               }
+               
+               // newCellValが空白
+               if(newValue.length() == 0) {
+                       cell.setCellType(Cell.CELL_TYPE_BLANK);
+               }
+               // newValueが空白でないなら
+               else {
+                       // 置換後の値の形式チェック
+                       // 置換後の値が数値型
+                       if(NumberUtils.isNumber(newValue)) {
+                               // もともと文字列セルなら数値セルに変更し値をセット
+                               if(cell.getCellType() == Cell.CELL_TYPE_STRING) {
+                                       cell.setCellType(Cell.CELL_TYPE_BLANK);
+                                       cell.setCellType(Cell.CELL_TYPE_NUMERIC);
+                                       // まずは整数変換
+                                       try {
+                                               cell.setCellValue(Integer.parseInt(newValue));
+                                               log.debug("set value type: Integer");
+                                       }
+                                       // エラー発生で実数変換
+                                       catch(NumberFormatException e) {
+                                               cell.setCellValue(NumberUtils.toDouble(newValue));
+                                               log.debug("set value type: Double");
+                                       }
+                               }
+                               // もともと計算式セルの場合は計算式として値をセット
+                               else {
+                                       // まずはそのまま
+                                       try {
+                                               cell.setCellFormula(newValue);
+                                               log.debug("set value type: Formula");
+                                       }
+                                       // エラー発生で数値としてセット
+                                       catch(FormulaParseException e) {
+                                               log.warn("formula set error: {}", e.getMessage(), e);
+                                               cell.setCellType(Cell.CELL_TYPE_BLANK);
+                                               cell.setCellType(Cell.CELL_TYPE_NUMERIC);
+                                               // まずは整数変換
+                                               try {
+                                                       cell.setCellValue(Integer.parseInt(newValue));
+                                                       log.debug("set value type: Integer");
+                                               }
+                                               // エラー発生で実数変換
+                                               catch(NumberFormatException e2) {
+                                                       cell.setCellValue(NumberUtils.toDouble(newValue));
+                                                       log.debug("set value type: Double");
+                                               }
+                                       }
+                               }
+                       }
+                       // 置換後の値がBool型
+                       else if(StringUtils.equalsIgnoreCase(newValue, "true") || StringUtils.equalsIgnoreCase(newValue, "false")) {
+                               // もともと文字列セルならBoolセルに変更し値をセット
+                               if(cell.getCellType() == Cell.CELL_TYPE_STRING) {
+                                       cell.setCellType(Cell.CELL_TYPE_BLANK);
+                                       cell.setCellType(Cell.CELL_TYPE_BOOLEAN);
+                                       cell.setCellValue(BooleanUtils.toBoolean(newValue));
+                                       log.debug("set value type: Boolean");
+                               }
+                               // もともと計算式セルの場合は計算式として値をセット
+                               else {
+                                       // まずはそのまま
+                                       try {
+                                               cell.setCellFormula(newValue);
+                                               log.debug("set value type: Formula");
+                                       }
+                                       // エラー発生でBoolとしてセット
+                                       catch(FormulaParseException e) {
+                                               log.warn("formula set error: {}", e.getMessage(), e);
+                                               cell.setCellType(Cell.CELL_TYPE_BLANK);
+                                               cell.setCellType(Cell.CELL_TYPE_BOOLEAN);
+                                               cell.setCellValue(BooleanUtils.toBoolean(newValue));
+                                               log.debug("set value type: Boolean");
+                                       }
+                               }
+                       }
+                       // 置換後の値が上記以外
+                       else {
+                               Date tmpDate = null;
+                               int valType = -1;
+                               for(int i = 0; i < 3; i++) {
+                                       switch(i) {
+                                       case 0:
+                                               try {
+                                                       // 置換後の値が日付・日付時刻型
+                                                       tmpDate = DateUtils.parseDateStrictly(newValue, Locale.getDefault(), context.getDateFormat());
+                                                       valType = i;
+                                               } catch (ParseException e1) {}
+                                               break;
+                                       case 1:
+                                               try {
+                                                       // 置換後の値が時刻型
+                                                       tmpDate = DateUtils.parseDateStrictly(newValue, context.getTimeFormat());
+                                                       valType = i;
+                                               } catch (ParseException e1) {}
+                                               break;
+                                       default:
+                                               // 置換後の値が上記以外(文字列)
+                                               valType = i;
+                                               break;
+                                       }
+                                       if(valType >= 0) break;
+                               }
+                               
+                               switch(valType) {
+                               case 0:
+                               case 1:
+                                       // 置換後の値が日付・日付時刻・時刻型
+                                       // もともと文字列セルなら数値セルに変更し値をセット
+                                       if(cell.getCellType() == Cell.CELL_TYPE_STRING) {
+                                               cell.setCellType(Cell.CELL_TYPE_BLANK);
+                                               cell.setCellType(Cell.CELL_TYPE_NUMERIC);
+                                               // 書式文字列なら文字列のまま挿入
+                                               if(Constants.FORMAT_GENERAL == cell.getCellStyle().getDataFormat() || Constants.FORMAT_TEXT == cell.getCellStyle().getDataFormat()) {
+                                                       cell.setCellValue(newValue);
+                                                       log.debug("set value type: String");
+                                               }
+                                               else {
+                                                       switch(valType) {
+                                                       case 0:
+                                                               // 置換後の値が日付・日付時刻型ならDateでセット
+                                                               cell.setCellValue(tmpDate);
+                                                               log.debug("set value type: Date");
+                                                               break;
+                                                       case 1:
+                                                               // 置換後の値が時刻型ならDoubleでセット(デフォルトタイムゾーン分加算)
+                                                               cell.setCellValue((tmpDate.getTime() + TimeZone.getDefault().getRawOffset()) / (1000.0 * 60 * 60 * 24));
+                                                               log.debug("set value type: double");
+                                                               break;
+                                                       }
+                                               }
+                                       }
+                                       // もともと計算式セルの場合は計算式として値をセット
+                                       else {
+                                               // まずはそのまま
+                                               try {
+                                                       cell.setCellFormula(newValue);
+                                                       log.debug("set value type: Formula");
+                                               }
+                                               // エラー発生で数値としてセット
+                                               catch(FormulaParseException e) {
+                                                       log.warn("formula set error: {}", e.getMessage(), e);
+                                                       cell.setCellType(Cell.CELL_TYPE_BLANK);
+                                                       cell.setCellType(Cell.CELL_TYPE_NUMERIC);
+                                                       cell.setCellValue(tmpDate);
+                                                       log.debug("set value type: Date");
+                                               }
+                                       }
+                                       break;
+                               default:
+                                       // 置換後の値が日付・日付時刻・時刻型以外(文字列と判断)
+                                       // 元のセルタイプのまま値をセット
+                                       if(cell.getCellType() == Cell.CELL_TYPE_STRING) {
+                                               cell.setCellValue(newValue);
+                                               log.debug("set value type: String");
+                                       } 
+                                       else {
+                                               // 式
+                                               try {
+                                                       cell.setCellFormula(newValue);
+                                                       log.debug("set value type: Formula");
+                                               }
+                                               catch(FormulaParseException e2) {
+                                                       log.warn("formula set error: {}", e2.getMessage(), e2);
+                                                       cell.setCellType(Cell.CELL_TYPE_BLANK);
+                                                       cell.setCellType(Cell.CELL_TYPE_STRING);
+                                                       cell.setCellValue(newValue);
+                                                       log.debug("set value type: String");
+                                               }
+                                       }
+                               }
+                       }
+               }
+
+               log.trace("parse end");
+       }
+}
diff --git a/Jaxcel/src/org/hanei/jaxcel/parser/ForeachParser.java b/Jaxcel/src/org/hanei/jaxcel/parser/ForeachParser.java
new file mode 100644 (file)
index 0000000..a79b105
--- /dev/null
@@ -0,0 +1,635 @@
+/**
+ * Copyright 2014 Hanei Management Co.,Ltd. 
+ * 
+ * This file is part of Jaxcel
+ * 
+ *  Jaxcel is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU Lesser General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  Jaxcel is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package org.hanei.jaxcel.parser;
+
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.commons.lang3.BooleanUtils;
+import org.apache.commons.lang3.math.NumberUtils;
+import org.apache.poi.POIXMLDocumentPart;
+import org.apache.poi.hssf.record.cf.CellRangeUtil;
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.util.CellRangeAddress;
+import org.apache.poi.ss.util.CellReference;
+import org.apache.poi.xssf.usermodel.XSSFDrawing;
+import org.apache.poi.xssf.usermodel.XSSFSheet;
+import org.hanei.jaxcel.report.ELManager;
+import org.hanei.jaxcel.report.JaxcelContext;
+import org.hanei.jaxcel.util.ExcelUtil;
+import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTDrawing;
+import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTMarker;
+import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTTwoCellAnchor;
+
+/**
+ * foreach句のパーサー実装<br>
+ * 
+ * @since 1.01.00
+ * @author Noboru Saito
+ */
+public class ForeachParser extends AbstractTLParser {
+
+       private static final String ROWS = "rows";
+       private static final String COLS = "cols";
+       private static final String DIRECTION = "direction";
+       private static final String STYLE = "style";
+       private static final String SHIFT = "shift";
+       private static final String BLOCK = "block";
+       private static final String START = "start";
+       private static final String END = "end";
+
+       private static final String IN_PHRASE = " in ";
+
+       private static final String ROW = "row";
+       private static final String COL = "col";
+       private static final String COPY = "copy";
+       private static final int IDX_BASE = 1;
+       private static final int SPAN_DEF = 1;
+       @Override
+       public void parse(JaxcelContext context) {
+               log.trace("parse start");
+
+               Sheet sheet = context.getCurrentSheet();
+               Cell cell = context.getCurrentCell();
+               ELManager elMgr = context.getElManager();
+               Map<String, Object> attributeMap = context.getAttributeMap();
+               String expression = null;
+               String object = null;
+               String list = null;
+               String direction = ROW;
+               boolean shift = false;
+               boolean block = true;
+               String style = COPY;
+               String start = null;            // start
+               String end = null;                      // end
+               int startIdx;
+               int endIdx;
+               int rowSpan = SPAN_DEF;         // row
+               int colSpan = SPAN_DEF;         // column
+
+               Row fromRow, toRow;
+               Cell fromCell, toCell;
+               String fromCellValue;
+               Pattern pattern = null;
+               Matcher matcher = null;
+               Object evalObject;
+               Object listObject;
+               Object[] mapKeys = null;
+               int listSize;
+               ArrayList<CellRangeAddress> rangeList = new ArrayList<>(); 
+               CellRangeAddress fromRange, toRange, tmpBfRange, tmpAfRange;
+
+               // セルタイプのチェック、指示子をクリア
+               switch(cell.getCellType()) {
+               case Cell.CELL_TYPE_STRING:
+                       cell.setCellValue(ExcelUtil.replaceFirstCellValue(cell, context.getParseString(), ""));
+                       break;
+               default:
+                       // セルタイプが文字列でない場合パース終了
+                       log.error("cell type is not strng");
+                       log.trace("parse end");
+                       return;
+               }
+
+               // EL式
+               if(attributeMap.containsKey(Constants.EXPRESSION)) {
+                       expression = (String) attributeMap.get(Constants.EXPRESSION);
+                       log.debug("expression: {}", expression);
+                       String[] tmp = expression.split(IN_PHRASE, 2);
+                       if(tmp != null) {
+                               object = tmp[0].trim();
+                               list = tmp[1].trim();
+                       }
+                       else {
+                               log.error("format error: ex) object in list");
+                               log.trace("parse end");
+                               return;
+                       }
+               }
+               else {
+                       // EL式が存在しな場合は終了
+                       log.error("expression is null");
+                       log.trace("parse end");
+                       return;
+               }
+               // リスト
+               listObject = elMgr.evaluate(list);
+               if(listObject == null) {
+                       log.error("list is null eval response.");
+                       log.trace("parse end");
+                       return; 
+               }
+               log.debug("list class: {}", listObject.getClass().getName());
+
+               // listObject Mapならキー取得
+               if(listObject instanceof Map) {
+                       mapKeys = ((Map<?, ?>)listObject).keySet().toArray();
+                       log.debug("mapKeys: {}", mapKeys);
+               }
+
+               // listのサイズ取得
+               evalObject = elMgr.evaluate("size(" + list + ")");
+               // evalした結果がintでなければリスト不可なオブジェクトと判断
+               if(evalObject == null) {
+                       log.error("list size unknown");
+                       log.trace("parse end");
+                       return; 
+               }
+               else if(!(evalObject instanceof Integer)) {
+                       log.error("list size is not Integer instance");
+                       log.trace("parse end");
+                       return; 
+               }
+               listSize = (int) evalObject;
+               log.debug("list size: {}", listSize);
+
+               
+               // direction:"row|col"  繰返し方向 デフォルト row
+               if(attributeMap.containsKey(DIRECTION)) {
+                       direction = (String) attributeMap.get(DIRECTION);
+                       if(!ROW.equalsIgnoreCase(direction) && !COL.equalsIgnoreCase(direction)) {
+                               log.warn("{} is illegal argument. set default: {}", DIRECTION, ROW);
+                               direction = ROW;
+                       }
+               }
+               // shift:"true|false"  foreachレンジより後のセルに対するシフト指定 デフォルト false
+               if(attributeMap.containsKey(SHIFT)) {
+                       shift = BooleanUtils.toBoolean((String) attributeMap.get(SHIFT));
+               }
+               // blick:"true|false"  foreachレンジをブロック範囲で繰り返すかの指定 デフォルト true
+               if(attributeMap.containsKey(BLOCK)) {
+                       block = BooleanUtils.toBoolean((String) attributeMap.get(BLOCK));
+               }
+               // style:"copy|^copy"  foreachレンジのセルスタイルのコピー指定 デフォルト copy
+               if(attributeMap.containsKey(STYLE)) {
+                       style = (String) attributeMap.get(STYLE);
+               }
+           // start:"numberExpression"
+               if(attributeMap.containsKey(START)) {
+                       start = (String) attributeMap.get(START);
+               }
+           // end:"numberExpression"
+               if(attributeMap.containsKey(END)) {
+                       end = (String) attributeMap.get(END);
+               }
+               // rows:"number"  範囲 デフォルト 1
+               if(attributeMap.containsKey(ROWS)) {
+                       rowSpan = NumberUtils.toInt((String) attributeMap.get(ROWS));
+                       if(rowSpan <= 0) {
+                               log.warn("{} is illegal argument. set default: {}", ROWS, SPAN_DEF);
+                               rowSpan = SPAN_DEF;
+                       }
+               }
+               // cols:"number"  範囲 デフォルト 1
+               if(attributeMap.containsKey(COLS)) {
+                       colSpan = NumberUtils.toInt((String) attributeMap.get(COLS));
+                       if(colSpan <= 0) {
+                               log.warn("{} is illegal argument. set default: {}", COLS, SPAN_DEF);
+                               colSpan = SPAN_DEF;
+                       }
+               }
+
+               // start インデックス
+               if(start != null) {
+                       // size(list) 等 式の可能性もあるのでevalする
+                       evalObject = elMgr.evaluate(start);
+                       if(evalObject != null) {
+                               // evalした結果がintでなければデフォルト
+                               if(!(evalObject instanceof Integer)) {
+                                       log.warn("start is not Integer instance. start set default: {}", IDX_BASE);
+                                       startIdx = IDX_BASE;
+                               }
+                               else {
+                                       startIdx = (int) evalObject;
+                                       // startIdxの補正
+                                       if(startIdx == 0) {
+                                               // startIdxが0の場合、1に補正
+                                               startIdx = IDX_BASE;
+                                       }
+                                       else if(startIdx < 0) {
+                                               // startIdxがマイナス値の場合、listのサイズからの逆順とする
+                                               // -1 の場合リストのサイズ
+                                               startIdx = listSize + startIdx + IDX_BASE;
+                                       }
+                                       else if(startIdx > listSize) {
+                                               // startIdxがリストのサイズより大きい場合、リストのサイズに切り詰める
+                                               startIdx = listSize;
+                                       }
+                               }
+                       }
+                       // evalした結果がnullの場合はデフォルト
+                       else {
+                               log.warn("start is null eval response. start set default: {}", IDX_BASE);
+                               startIdx = IDX_BASE;
+                       }
+               }
+               else {
+                       log.debug("start is null. start set default: {}", IDX_BASE);
+                       startIdx = IDX_BASE;
+               }
+
+               // end インデックス
+               if(end  != null) {
+                       // size(list) 等 式の可能性もあるのでevalする
+                       evalObject = elMgr.evaluate(end);
+                       if(evalObject != null) {
+                               // evalした結果がintでなければリストサイズ
+                               if(!(evalObject instanceof Integer)) {
+                                       log.warn("end is not Integer instance. end set list size: {}", listSize);
+                                       endIdx = listSize;
+                               }
+                               else {
+                                       endIdx = (int) evalObject;
+                                       // endIdxの補正
+                                       if(endIdx == 0) {
+                                               // endIdxが0の場合、リストサイズ
+                                               endIdx = listSize;
+                                       }
+                                       else if(endIdx < 0) {
+                                               // endIdxがマイナス値の場合、listのサイズからの逆順とする
+                                               // -1 の場合リストのサイズ
+                                               endIdx = listSize + endIdx + IDX_BASE;
+                                       }
+                                       else if(endIdx > listSize) {
+                                               // endIdxがリストのサイズより大きい場合、リストのサイズに切り詰める
+                                               endIdx = listSize;
+                                       }
+                               }
+                       }
+                       // evalした結果がnullの場合はリストサイズ
+                       else {
+                               log.warn("end is null eval response. end set list size: {}", listSize);
+                               endIdx = listSize;
+                       }
+               }
+               else {
+                       log.debug("end is null. end set list size: {}", listSize);
+                       endIdx = listSize;
+               }
+               
+               if(log.isDebugEnabled()) {
+                       log.debug("object: {}", object);
+                       log.debug("list: {}", list);
+                       log.debug("start: {}", startIdx);
+                       log.debug("end: {}", endIdx);
+                       log.debug("rows: {}", rowSpan);
+                       log.debug("cols: {}", colSpan);
+                       log.debug("direction: {}", direction);
+                       log.debug("shift: {}", shift);
+                       log.debug("block: {}", block);
+                       log.debug("style: {}", style);
+               }
+               
+               // コピー元範囲 block:falseの場合、行・列全体に範囲を広げる
+               if(!block) {
+                       // 繰返し方向横(列)(行がデフォルト)
+                       if(COL.equalsIgnoreCase(direction)) {
+                               fromRange = new CellRangeAddress(sheet.getFirstRowNum(), sheet.getLastRowNum(), cell.getColumnIndex(), cell.getColumnIndex() + colSpan - 1);
+                       }
+                       // 繰返し方向縦(行)の場合
+                       else {
+                               fromRange = new CellRangeAddress(cell.getRowIndex(), cell.getRowIndex() + rowSpan - 1, 0, ExcelUtil.getLastColNum(sheet));
+                       }
+               }
+               else {
+                       fromRange = new CellRangeAddress(cell.getRowIndex(), cell.getRowIndex() + rowSpan - 1, cell.getColumnIndex(), cell.getColumnIndex() + colSpan - 1);
+               }
+               // シートに収まる範囲にリサイズ
+               fromRange = ExcelUtil.getIntersectRange(sheet, fromRange);
+
+               // 繰返しコピー回数1以上なら、コピー先範囲シフト・クリア等実施
+               if(startIdx != endIdx) {
+                       // コピー先範囲の範囲特定
+                       int copyCount = startIdx < endIdx ? endIdx - startIdx : startIdx - endIdx;
+                       // 繰返し方向横(列)(行がデフォルト)
+                       if(COL.equalsIgnoreCase(direction)) {
+                               toRange = new CellRangeAddress(
+                                               fromRange.getFirstRow(), 
+                                               fromRange.getLastRow(), 
+                                               fromRange.getLastColumn() + 1, 
+                                               fromRange.getLastColumn() + (fromRange.getLastColumn() - fromRange.getFirstColumn() + 1) * copyCount);
+                       }
+                       // 繰返し方向縦(行)の場合
+                       else {
+                               toRange = new CellRangeAddress(
+                                               fromRange.getLastRow() + 1, 
+                                               fromRange.getLastRow() + (fromRange.getLastRow() - fromRange.getFirstRow() + 1) * copyCount, 
+                                               fromRange.getFirstColumn(), 
+                                               fromRange.getLastColumn());
+                       }
+
+                       // shift:trueの場合、コピー先範囲の後続範囲をシフト
+                       if(shift) {
+                               // 移動量
+                               int distance = (startIdx > endIdx ? startIdx - endIdx : endIdx - startIdx);
+
+                               // 繰返し方向横(列)(行がデフォルト)
+                               if(COL.equalsIgnoreCase(direction)) {
+                                       distance *= colSpan;
+                                       // 繰返し範囲以降をずらす
+                                       ExcelUtil.shift(sheet, new CellRangeAddress(fromRange.getFirstRow(), fromRange.getLastRow(), fromRange.getLastColumn() + 1, fromRange.getLastColumn() + 1), COL, distance, block);
+                               }
+                               // 繰返し方向縦(行)の場合
+                               else {
+                                       distance *= rowSpan;
+                                       // 繰返し範囲以降をずらす
+                                       ExcelUtil.shift(sheet, new CellRangeAddress(fromRange.getLastRow() + 1, fromRange.getLastRow() + 1, fromRange.getFirstColumn(), fromRange.getLastColumn()), ROW, distance, block);
+                               }
+                       }
+                       // shift:falseの場合、コピー先範囲をクリア
+                       else {
+                               ExcelUtil.clearRange(sheet, toRange, COPY.equalsIgnoreCase(style), COPY.equalsIgnoreCase(style));
+                       }
+                       // style:copyの場合、コピー元範囲の結合保持・解除
+                       if(COPY.equalsIgnoreCase(style)) {
+                               // コピー元範囲の結合状態チェック・保持。コピー先範囲の結合解除
+                               CellRangeAddress tmpRange;
+                               for(int i = 0; i < sheet.getNumMergedRegions(); i++) {
+                                       tmpRange = sheet.getMergedRegion(i);
+                                       // コピー元範囲の結合状態保持・解除
+                                       switch(CellRangeUtil.intersect(fromRange, tmpRange)) {
+                                       case CellRangeUtil.INSIDE:
+                                               rangeList.add(tmpRange);
+                                               log.debug("from range inside mergedRegion. save: {}", tmpRange.formatAsString());
+                                               break;
+                                       case CellRangeUtil.ENCLOSES:
+                                               log.warn("from range encloses mergedRegion. remove: {}", tmpRange.formatAsString());
+                                               sheet.removeMergedRegion(i);
+                                               break;
+                                       case CellRangeUtil.OVERLAP:
+                                               log.warn("from range overlap mergedRegion. remove: {}", tmpRange.formatAsString());
+                                               sheet.removeMergedRegion(i);
+                                               break;
+                                       }
+                               }
+                       }
+               }
+
+               // 範囲行でループ
+               for(int r = fromRange.getFirstRow(); r <= fromRange.getLastRow(); r++) {
+                       // コピー元行
+                       fromRow = sheet.getRow(r);
+                       // コピー行
+                       toRow = fromRow;
+                       // 範囲列でループ
+                       colLoop:
+                       for(int c = fromRange.getFirstColumn(); c <= fromRange.getLastColumn(); c++) {
+                               // コピー元セル
+                               fromCell = fromRow == null ? null : fromRow.getCell(c);
+                               // コピー元セルが数式か文字列の場合バリュー保持
+                               fromCellValue = null;
+                               if(fromCell != null && (fromCell.getCellType() == Cell.CELL_TYPE_FORMULA || fromCell.getCellType() == Cell.CELL_TYPE_STRING)) {
+                                       switch(fromCell.getCellType()) {
+                                       case Cell.CELL_TYPE_FORMULA:
+                                               fromCellValue = fromCell.getCellFormula();
+                                               break;
+                                       case Cell.CELL_TYPE_STRING:
+                                               fromCellValue = fromCell.getStringCellValue();
+                                       }
+                                       // 正規表現オブジェクト
+                                       pattern = Pattern.compile("(" + Pattern.quote("${") + "[^\\$\\{\\}]*?\\b+)(" + Pattern.quote(object) + ")([^a-zA-Z_0-9\\$]+?)");
+                                       matcher = pattern.matcher(fromCellValue);
+                               }
+                               
+                               // 繰返し回数でループしコピー
+                               for(int i = startIdx, cpCount = 0; (startIdx <= endIdx && i <= endIdx || startIdx > endIdx && i >= endIdx); i = (startIdx <= endIdx ? i + 1 : i - 1), cpCount++) {
+                                       // 繰返し方向横(列)(行がデフォルト)
+                                       if(COL.equalsIgnoreCase(direction)) {
+                                               // コピー先の範囲チェック
+                                               if(c + colSpan * cpCount > ExcelUtil.getMaxColumnIndex(sheet)) {
+                                                       // コピー先列がシート範囲外
+                                                       log.debug("break col loop. to cell [{}] is outside sheet", c + colSpan * cpCount + 1);
+                                                       break colLoop;
+                                               }
+                                               // コピー元行がnullならコピー先行もnull
+                                               if(fromRow == null) {
+                                                       log.debug("break col loop. from row [{}] and to row [{}] is null", (r + 1), (r + 1));
+                                                       break colLoop;
+                                               } 
+                                       }
+                                       // 繰返し方向縦(行)の場合
+                                       else {
+                                               // コピー先の範囲チェック
+                                               if(r + rowSpan * cpCount > ExcelUtil.getMaxRowIndex(sheet)) {
+                                                       // コピー先行がシート範囲外
+                                                       log.debug("break copy loop. to row [{}] is outside sheet", r + rowSpan * cpCount + 1);
+                                                       break;
+                                               }
+                                               // コピー行
+                                               toRow = sheet.getRow(r + rowSpan * cpCount);
+                                               // コピー元行がnullならコピー先行もnull。continue
+                                               if(fromRow == null) {
+                                                       if(toRow != null) {
+                                                               log.debug("continue. from row [{}] is null. to row [{}] remove", (r + 1), (toRow.getRowNum() + 1));
+                                                               sheet.removeRow(toRow);
+                                                       }
+                                                       else {
+                                                               log.debug("continue. from row [{}] and to row [{}] is null", (r + 1), ((ROW.equalsIgnoreCase(direction) ? (r + rowSpan * cpCount) : r) + 1));
+                                                       }
+                                                       continue;
+                                               } 
+                                       }
+
+                                       // コピー先行がnullなら生成
+                                       if(toRow == null) toRow = sheet.createRow(r + rowSpan * cpCount);
+
+                                       // コピー元行・コピー先行 行番号が異なり、block false、繰返し方向縦(行)なら行情報(高さ)もコピー(範囲列先頭のみ)
+                                       if(cpCount > 0 && !block && c == fromRange.getFirstColumn() && ROW.equalsIgnoreCase(direction))
+                                               toRow.setHeight(fromRow.getHeight());
+
+                                       // コピー先セル
+                                       toCell = toRow.getCell(COL.equalsIgnoreCase(direction) ? c + colSpan * cpCount : c);
+                                       // コピー元セルがnullならコピー先セルもnull
+                                       if(fromCell == null) {
+                                               if(toCell != null) {
+                                                       log.debug("continue. from cell [{}] is null. to cell [{}] remove", (new CellReference(fromRow.getRowNum(), c)).formatAsString(), (new CellReference(toCell.getRowIndex(), toCell.getColumnIndex())).formatAsString());
+                                                       toRow.removeCell(toCell);
+                                               }
+                                               else {
+                                                       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());
+                                               }
+                                               continue;
+                                       }
+                                       // コピー先セルがnullなら生成
+                                       if(toCell == null) toCell = toRow.createCell(COL.equalsIgnoreCase(direction) ? c + colSpan * cpCount : c);
+
+                                       // コピー元列・コピー先列 列番号が異なり、block false、繰返し方向横(列)なら列情報(幅)もコピー(範囲行先頭のみ)
+                                       if(cpCount > 0 && !block && r == fromRange.getFirstRow() && COL.equalsIgnoreCase(direction))
+                                               sheet.setColumnWidth(toCell.getColumnIndex(), sheet.getColumnWidth(fromCell.getColumnIndex()));
+
+                                       // スタイルコピーなら
+                                       if(cpCount > 0  && COPY.equalsIgnoreCase(style)) {
+                                               // スタイルのコピー
+                                               toCell.setCellStyle(fromCell.getCellStyle());
+                                       }
+                                       // 値のコピー
+                                       switch(fromCell.getCellType()) {
+                                       case Cell.CELL_TYPE_BLANK:
+                                               toCell.setCellType(Cell.CELL_TYPE_BLANK);
+                                               break;
+                                       case Cell.CELL_TYPE_BOOLEAN:
+                                               toCell.setCellType(Cell.CELL_TYPE_BOOLEAN);
+                                               toCell.setCellValue(fromCell.getBooleanCellValue());
+                                               break;
+                                       case Cell.CELL_TYPE_ERROR:
+                                               toCell.setCellType(Cell.CELL_TYPE_ERROR);
+                                               toCell.setCellErrorValue(fromCell.getErrorCellValue());
+                                               break;
+                                       case Cell.CELL_TYPE_NUMERIC:
+                                               toCell.setCellType(Cell.CELL_TYPE_NUMERIC);
+                                               toCell.setCellValue(fromCell.getNumericCellValue());
+                                               break;
+                                       case Cell.CELL_TYPE_FORMULA:
+                                               toCell.setCellType(Cell.CELL_TYPE_FORMULA);
+                                               // 数式のパース・移動先に合わせる
+                                               toCell.setCellFormula(
+                                                               ExcelUtil.getMoveFormula(
+                                                                               sheet, 
+                                                                               matcher.replaceAll("$1" + Matcher.quoteReplacement("(" + list + ")[" + (mapKeys == null ? i - 1 : "'" + mapKeys[i - 1] + "'") + "]") + "$3"),
+                                                                               toCell.getRowIndex() - fromCell.getRowIndex(), 
+                                                                               toCell.getColumnIndex() - fromCell.getColumnIndex()
+                                                               )
+                                               );
+                                               log.debug("to cell Formula: {}", toCell.getCellFormula());
+                                               break;
+                                       case Cell.CELL_TYPE_STRING:
+                                               toCell.setCellType(Cell.CELL_TYPE_STRING);
+//                                             log.debug("fromCell   : {}", fromCell.getStringCellValue());
+//                                             log.debug("toCell     : {}", toCell.getStringCellValue());
+//                                             log.debug("i          : {}", i);
+//                                             log.debug("cpCount    : {}", cpCount);
+//                                             log.debug("regex      : {}", "(" + Pattern.quote("${") + "[^\\$\\{\\}]*?\\b+)(" + Pattern.quote(object) + ")([^a-zA-Z_0-9\\$]+?)");
+//                                             log.debug("replacement: {}", "$1" + Matcher.quoteReplacement("(" + list + ")[" + (mapKeys == null ? i - 1 : "'" + mapKeys[i - 1] + "'") + "]") + "$3");
+//                                             log.debug("replaceAll:  {}", matcher.replaceAll("$1" + Matcher.quoteReplacement("(" + list + ")[" + (mapKeys == null ? i - 1 : "'" + mapKeys[i - 1] + "'") + "]") + "$3"));
+                                               toCell.setCellValue(matcher.replaceAll("$1" + Matcher.quoteReplacement("(" + list + ")[" + (mapKeys == null ? i - 1 : "'" + mapKeys[i - 1] + "'") + "]") + "$3"));
+                                               log.debug("to cell string: {}", toCell.getStringCellValue());
+                                               break;
+                                       }
+                               }
+                       }
+               }
+
+               // スタイルコピーなら
+               if(COPY.equalsIgnoreCase(style)) {
+                       // 行列加算用
+                       int addRow = 0;
+                       int addCol = 0;
+                       if(COL.equalsIgnoreCase(direction)) {
+                               addCol = colSpan;
+                       }
+                       else {
+                               addRow = rowSpan;
+                       }
+                       
+                       // コピー元範囲の結合保持なら
+                       if(!rangeList.isEmpty()) {
+                               // 結合をコピー
+                               // 結合保持数でループ
+                               for(int c = 0; c < rangeList.size(); c++) {
+                                       tmpBfRange = rangeList.get(c);
+                                       // 繰返し回数でループ
+                                       for(int i = startIdx, cpCount = 0; (startIdx <= endIdx && i <= endIdx || startIdx > endIdx && i >= endIdx); i = (startIdx <= endIdx ? i + 1 : i - 1), cpCount++) {
+                                               // 初回はコピーしない(コピー元だから)
+                                               if(i == startIdx) continue;
+                                               // コピー先レンジ
+                                               tmpAfRange = new CellRangeAddress(
+                                                               tmpBfRange.getFirstRow() + (addRow * cpCount),
+                                                               tmpBfRange.getLastRow() + (addRow * cpCount),
+                                                               tmpBfRange.getFirstColumn() + (addCol * cpCount),
+                                                               tmpBfRange.getLastColumn() + (addCol * cpCount));
+                                               if(ExcelUtil.validateRange(sheet, tmpAfRange)) {
+                                                       sheet.addMergedRegion(tmpAfRange);
+                                                       log.debug("mergedRegion copy. from: [{}] to: [{}]", tmpBfRange.formatAsString(), tmpAfRange.formatAsString());
+                                               }
+                                       }
+                               }
+                       }
+
+                       // オブジェクトのコピー Excel2007以降 ooxml形式のみ対応
+                       if(sheet instanceof XSSFSheet) {
+                               int r1, c1, r2, c2;
+                               CTDrawing ctDrawing;
+                               CTTwoCellAnchor fAnchor, cpAnchor;
+                               CTMarker from, to; 
+                               try {
+                                       // DocumentPartでループ
+                                       for(POIXMLDocumentPart part : ((XSSFSheet)sheet).getRelations()) {
+                                               if(part == null) continue;
+                                               log.debug("DocumentPart class: {}", part.getClass().getName());
+                                               // DocumentPartがDrawingオブジェクトの場合
+                                               if(part instanceof XSSFDrawing) {
+                                                       ctDrawing = ((XSSFDrawing) part).getCTDrawing();
+                                                       if(ctDrawing != null) {
+                                                               // アンカーでループ
+                                                               int alSize = ctDrawing.getTwoCellAnchorList().size();
+                                                               for (int i = 0; i < alSize; i++) {
+                                                                       fAnchor = ctDrawing.getTwoCellAnchorList().get(i);
+                                                                       // GraphicFrameをもつグラフ、スマートアートは現状非対応
+                                                                       if(fAnchor.isSetGraphicFrame()) continue;
+                                                                       
+                                                                       // アンカーの位置情報
+                                                                       from = fAnchor.getFrom();
+                                                                       r1 = from.getRow();
+                                                                       c1 = from.getCol();
+                                                                       to = fAnchor.getTo();
+                                                                       r2 = to.getRow();
+                                                                       c2 = to.getCol();
+                                                                       tmpBfRange = new CellRangeAddress(r1, r2, c1, c2);
+                                                                       
+                                                                       // コピー元レンジに含まれている、掛かっているならコピー
+                                                                       switch(CellRangeUtil.intersect(fromRange, tmpBfRange)) {
+                                                                       case CellRangeUtil.INSIDE:
+                                                                       case CellRangeUtil.OVERLAP:
+                                                                               // 繰返し回数でループ
+                                                                               for(int j = startIdx, cpCount = 0; (startIdx <= endIdx && j <= endIdx || startIdx > endIdx && j >= endIdx); j = (startIdx <= endIdx ? j + 1 : j - 1), cpCount++) {
+                                                                                       // 初回はコピーしない(コピー元だから)
+                                                                                       if(j == startIdx) continue;
+                                                                                       // オブジェクト(アンカー)のコピー
+                                                                                       cpAnchor = ctDrawing.addNewTwoCellAnchor();
+                                                                                       cpAnchor.set(fAnchor.copy());
+                                                                                       from = cpAnchor.getFrom();
+                                                                                       from.setRow(from.getRow() + (addRow * cpCount));
+                                                                                       from.setCol(from.getCol() + (addCol * cpCount));
+                                                                                       to = cpAnchor.getTo();
+                                                                                       to.setRow(to.getRow() + (addRow * cpCount));
+                                                                                       to.setCol(to.getCol() + (addCol * cpCount));
+                                                                                       if(log.isDebugEnabled()) {
+                                                                                               tmpAfRange = new CellRangeAddress(from.getRow(), to.getRow(), from.getCol(), to.getCol());
+                                                                                               log.debug("object copy from: [{}] to: [{}]", tmpBfRange.formatAsString(), tmpAfRange.formatAsString());
+                                                                                       }
+                                                                               }
+                                                                       }
+                                                               }
+                                                       }
+                                               }
+                                       }
+                               }
+                               catch(Exception e) {
+                                       log.error("object copy error: " + e.getMessage(), e);
+                               }
+                       }
+               }
+               
+               log.trace("parse end");
+       }
+}
diff --git a/Jaxcel/src/org/hanei/jaxcel/parser/IfParser.java b/Jaxcel/src/org/hanei/jaxcel/parser/IfParser.java
new file mode 100644 (file)
index 0000000..7460235
--- /dev/null
@@ -0,0 +1,170 @@
+/**
+ * Copyright 2014 Hanei Management Co.,Ltd. 
+ * 
+ * This file is part of Jaxcel
+ * 
+ *  Jaxcel is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU Lesser General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  Jaxcel is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package org.hanei.jaxcel.parser;
+
+import java.util.ArrayList;
+import java.util.Map;
+import org.apache.commons.lang3.BooleanUtils;
+import org.apache.commons.lang3.math.NumberUtils;
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.util.CellRangeAddress;
+import org.hanei.jaxcel.report.ELManager;
+import org.hanei.jaxcel.report.JaxcelContext;
+import org.hanei.jaxcel.util.ExcelUtil;
+
+/**
+ * if句のパーサー実装<br>
+ * 
+ * @since 1.01.00
+ * @author Noboru Saito
+ */
+public class IfParser extends AbstractTLParser {
+
+       private static final String LEFT = "left";
+       private static final String UP = "up";
+       private static final String CLEAR = "clear";
+       private static final String ROWS = "rows";
+       private static final String COLS = "cols";
+       private static final String DELETE = "delete";
+       private static final String BLOCK = "block";
+       private static final String ROW = "row";
+       private static final String COL = "col";
+       private static final int SPAN_DEF = 1;
+
+       @Override
+       public void parse(JaxcelContext context) {
+               log.trace("parse start");
+
+               Sheet sheet = context.getCurrentSheet();
+               Cell cell = context.getCurrentCell();
+               ELManager elMgr = context.getElManager();
+               Map<String, Object> attributeMap = context.getAttributeMap();
+               String expression = null;
+               String delete = null;
+               boolean block = true;
+               int rowSpan = SPAN_DEF;         // row
+               int colSpan = SPAN_DEF;         // column
+
+               new ArrayList<>(); 
+               // セルタイプのチェック、指示子をクリア
+               switch(cell.getCellType()) {
+               case Cell.CELL_TYPE_STRING:
+                       cell.setCellValue(ExcelUtil.replaceFirstCellValue(cell, context.getParseString(), ""));
+                       break;
+               default:
+                       // セルタイプが文字列でない場合パース終了
+                       log.error("cell type is not strng");
+                       log.trace("parse end");
+                       return;
+               }
+
+               // EL式
+               if(attributeMap.containsKey(Constants.EXPRESSION)) {
+                       expression = (String) attributeMap.get(Constants.EXPRESSION);
+                       log.debug("expression: {}", expression);
+               }
+               else {
+                       // EL式が存在しな場合は終了
+                       log.error("expression is null");
+                       log.trace("parse end");
+                       return;
+               }
+               
+               // blick:"true|false"  foreachレンジをブロック範囲で繰り返すかの指定 デフォルト true
+               if(attributeMap.containsKey(BLOCK)) {
+                       block = BooleanUtils.toBoolean((String) attributeMap.get(BLOCK));
+               }
+               // delete:"left|up|clear"  判定式がflse判定の場合の制御範囲に対する操作を指定 デフォルト left
+               if(attributeMap.containsKey(DELETE)) {
+                       delete = (String) attributeMap.get(DELETE);
+               }
+               // rows:"number"  範囲 デフォルト 1
+               if(attributeMap.containsKey(ROWS)) {
+                       rowSpan = NumberUtils.toInt((String) attributeMap.get(ROWS));
+                       if(rowSpan <= 0) {
+                               log.warn("{} is illegal argument. set default: {}", ROWS, SPAN_DEF);
+                               rowSpan = SPAN_DEF;
+                       }
+               }
+               // cols:"number"  範囲 デフォルト 1
+               if(attributeMap.containsKey(COLS)) {
+                       colSpan = NumberUtils.toInt((String) attributeMap.get(COLS));
+                       if(colSpan <= 0) {
+                               log.warn("{} is illegal argument. set default: {}", COLS, SPAN_DEF);
+                               colSpan = SPAN_DEF;
+                       }
+               }
+               
+               if(log.isDebugEnabled()) {
+                       log.debug("rows: {}", rowSpan);
+                       log.debug("cols: {}", colSpan);
+                       log.debug("delete: {}", delete);
+                       log.debug("block: {}", block);
+               }
+
+               // 判定
+               boolean result;
+               Object elResult = elMgr.evaluate(expression);
+               if(elResult == null) {
+                       log.debug("evaluate result is null");
+                       result = false;
+               }
+               else if(elResult instanceof Boolean) {
+                       result = (boolean) elResult;
+               }
+               else {
+                       result = true;
+               }
+               log.debug("evaluate result: {}", result);
+
+               // 判定falseの場合、範囲の操作
+               if(!result) {
+                       if(delete == null) {
+                               log.debug("{} is null set default: {}", DELETE, LEFT);
+                               delete = LEFT;
+                       }
+                       else if(!LEFT.equalsIgnoreCase(delete) && !UP.equalsIgnoreCase(delete) && !CLEAR.equalsIgnoreCase(delete)) {
+                               log.warn("{} is illegal argument. set default: {}", DELETE, LEFT);
+                               delete = LEFT;
+                       }
+                       else {
+                               log.debug("delete: {}", delete);
+                               delete = delete.toLowerCase().trim();
+                       }
+                       
+                       switch(delete) {
+                       // クリア
+                       case CLEAR:
+                               ExcelUtil.clearRange(sheet, (new CellRangeAddress(cell.getRowIndex(), cell.getRowIndex() + rowSpan - 1, cell.getColumnIndex(), cell.getColumnIndex() + colSpan - 1)), false, false);
+                               break;
+                       // 左詰め
+                       case LEFT:
+                               // IF範囲以降をずらす
+                               ExcelUtil.shift(sheet, new CellRangeAddress(cell.getRowIndex(), cell.getRowIndex() + rowSpan - 1, cell.getColumnIndex() + colSpan, cell.getColumnIndex() + colSpan), COL, -colSpan, block);
+                               break;
+                       // 上詰め
+                       case UP:
+                               // IF範囲以降をずらす
+                               ExcelUtil.shift(sheet, new CellRangeAddress(cell.getRowIndex() + rowSpan, cell.getRowIndex() + rowSpan, cell.getColumnIndex(), cell.getColumnIndex() + colSpan - 1), ROW, -rowSpan, block);
+                       }
+               }
+               log.trace("parse end");
+       }
+}
diff --git a/Jaxcel/src/org/hanei/jaxcel/parser/TLParser.java b/Jaxcel/src/org/hanei/jaxcel/parser/TLParser.java
new file mode 100644 (file)
index 0000000..5c6f358
--- /dev/null
@@ -0,0 +1,38 @@
+/**
+ * Copyright 2014 Hanei Management Co.,Ltd. 
+ * 
+ * This file is part of Jaxcel
+ * 
+ *  Jaxcel is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU Lesser General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  Jaxcel is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package org.hanei.jaxcel.parser;
+
+import org.hanei.jaxcel.report.JaxcelContext;
+
+/**
+ * テンプレートパーサーのインターフェイス<br>
+ * 
+ * @since 1.01.00
+ * @author Noboru Saito
+ */
+public interface TLParser {
+
+       /**
+        * 対象セルのテンプレート指示子をパースする
+        * 
+        * @param context
+        */
+       public void parse(JaxcelContext context);
+
+}
index 8ea6b8d..b1de7f4 100644 (file)
@@ -35,7 +35,7 @@ import org.slf4j.LoggerFactory;
  * EL式の解析、解析で使用するパラメータオブジェクトの保持を行う。<br>\r
  * 式言語エンジンにはJEXLを使用。\r
  * \r
- * @version 1.00.00\r
+ * @since 1.00.00\r
  * @author noboru saito\r
  *\r
  */\r
index 4ff88ab..7e11001 100644 (file)
  */
 package org.hanei.jaxcel.report;
 
+import java.util.Map;
+
+import org.apache.poi.ss.usermodel.Cell;
 import org.apache.poi.ss.usermodel.Sheet;
 
 /**
  * Jaxcelコンテキスト
  * 
- * @version 1.00.00
+ * @since 1.00.00
  * @author noboru saito
  */
 public class JaxcelContext {
 
        private Sheet currentSheet = null;
+       private Cell currentCell = null;
+       private String parseString = null;
+       private Map<String, Object> attributeMap =null;
+       private String[] dateFormat = null;
+       private String[] timeFormat = null;
        private ELManager elManager = null;
        
        /**
@@ -55,6 +63,96 @@ public class JaxcelContext {
        }
 
        /**
+        * 参照中のCellオブジェクトを返却
+        * 
+        * @return 参照中のCellオブジェクト
+        */
+       public Cell getCurrentCell() {
+               return currentCell;
+       }
+
+       /**
+        * 参照中とするCellオブジェクトを設定
+        * 
+        * @param currentCell Cellオブジェクト
+        */
+       public void setCurrentCell(Cell currentCell) {
+               this.currentCell = currentCell;
+       }
+
+       /**
+        * パース対象の指示子文字列を返却
+        * 
+        * @return パース対象の文字列
+        */
+       public String getParseString() {
+               return parseString;
+       }
+
+       /**
+        * パース対象とする指示子文字列を設定
+        * 
+        * @param parseString パース対象の文字列
+        */
+       public void setParseString(String parseString) {
+               this.parseString = parseString;
+       }
+
+       /**
+        * パース対象の指示子属性情報Mapオブジェクトを返却
+        * 
+        * @return Mapオブジェクト
+        */
+       public Map<String, Object> getAttributeMap() {
+               return attributeMap;
+       }
+
+       /**
+        * パース対象の指示子属性情報Mapオブジェクトを設定
+        * 
+        * @param attributeMap Mapオブジェクト
+        */
+       public void setAttributeMap(Map<String, Object> attributeMap) {
+               this.attributeMap = attributeMap;
+       }
+
+       /**
+        * 日付・日付時刻フォーマット文字列配列を返却
+        * 
+        * @return 日付・日付時刻フォーマット文字列配列
+        */
+       public String[] getDateFormat() {
+               return dateFormat;
+       }
+
+       /**
+        * 日付・日付時刻フォーマット文字列配列を設定
+        * 
+        * @param dateFormat  日付・日付時刻フォーマット文字列配列
+        */
+       public void setDateFormat(String[] dateFormat) {
+               this.dateFormat = dateFormat;
+       }
+
+       /**
+        * 時刻フォーマット文字列配列を返却
+        * 
+        * @return 時刻フォーマット文字列配列
+        */
+       public String[] getTimeFormat() {
+               return timeFormat;
+       }
+
+       /**
+        * 時刻フォーマット文字列配列を設定
+        * 
+        * @param dateFormat  時刻フォーマット文字列配列
+        */
+       public void setTimeFormat(String[] timeFormat) {
+               this.timeFormat = timeFormat;
+       }
+
+       /**
         * ELManagerオブジェクトを返却
         * 
         * @return ELManagerオブジェクト
@@ -71,5 +169,5 @@ public class JaxcelContext {
        public void setElManager(ELManager elManager) {
                this.elManager = elManager;
        }
-
+       
 }
index e6bac69..e0bdb6f 100644 (file)
@@ -44,6 +44,7 @@ import org.apache.poi.xssf.usermodel.XSSFDialogsheet;
 import org.apache.poi.xssf.usermodel.XSSFWorkbook;\r
 import org.hanei.jaxcel.exception.JaxcelInputException;\r
 import org.hanei.jaxcel.exception.JaxcelOutputException;\r
+import org.hanei.jaxcel.parser.CellParser;\r
 import org.slf4j.LoggerFactory;\r
 import org.slf4j.Logger;\r
 \r
@@ -159,7 +160,7 @@ import org.slf4j.Logger;
  * </tr>\r
  * </table>\r
  * \r
- * @version 1.00.00\r
+ * @since 1.00.00\r
  * @author Noboru Saito\r
  */\r
 public class ReportMaker {\r
@@ -187,6 +188,11 @@ public class ReportMaker {
         *  コンテキスト\r
         */\r
        private JaxcelContext context  = null;\r
+       \r
+       /**\r
+        *  セルパーサ\r
+        */\r
+       private CellParser cellParser = null;\r
 \r
        /**\r
         *  テンプレート一時ファイル\r
@@ -596,6 +602,9 @@ public class ReportMaker {
        private void makeBook(Workbook book) {\r
                log.trace("makeBook start");\r
                \r
+               // セルパーサ生成\r
+               cellParser = new CellParser(context);\r
+               \r
                // シートでループ\r
                log.debug("sheet count: {}", book.getNumberOfSheets());\r
                for(int i = 0; i < book.getNumberOfSheets(); i++) {\r
@@ -635,11 +644,9 @@ public class ReportMaker {
                \r
                Row row;                                                // 行オブジェクト\r
                Cell cell;                                              // セルブジェクト\r
-               TLParser tlParser;                              // パーサ\r
 \r
                // カレントシート設定、パーサ生成\r
                context.setCurrentSheet(sheet);\r
-               tlParser = new TLParser(context);\r
 \r
                //最大行数\r
                int lastRowNum = sheet.getLastRowNum();\r
@@ -682,9 +689,9 @@ public class ReportMaker {
                                case Cell.CELL_TYPE_STRING:\r
                                case Cell.CELL_TYPE_FORMULA:\r
                                        // パース\r
-                                       tlParser.parse(cell);\r
+                                       cellParser.parse(cell);\r
                                        // 再パースフラグONなら\r
-                                       if(tlParser.isReParseCell()) {\r
+                                       if(cellParser.isReParseCell()) {\r
                                                // もう一度そのセルからループする\r
                                                cellIdx--;\r
                                        }\r
index a20f411..41e67b3 100644 (file)
@@ -53,8 +53,9 @@ import org.slf4j.Logger;
 /**
  * Excelテンプレートシートの指示子(Template Language)、EL式(Expression Language)の検索、パースを行う
  * 
+ * @since 1.00.00
  * @author Noboru Saito
- *
+ * @deprecated 
  */
 public class TLParser {
 
@@ -1444,7 +1445,7 @@ public class TLParser {
         * @return      置換後の文字列
         */
        private String replaceFirst(String replacement) {
-               return mtAll.replaceFirst(replacement == null ? "" : replacement);
+               return mtAll.replaceFirst(replacement == null ? "" : replacement.replace("\\", "\\\\").replace("$", "\\$"));
        }
        
        /**
index a977c44..aa899f3 100644 (file)
@@ -21,6 +21,9 @@ package org.hanei.jaxcel.util;
 import java.lang.reflect.Field;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
 
 
 import org.apache.poi.POIXMLDocumentPart;
@@ -62,7 +65,7 @@ import org.slf4j.Logger;
 /**
  * Excelユーティリティクラス
  * 
- * @version 1.00.00
+ * @since 1.00.00
  * @author Noboru Saito
  */
 public class ExcelUtil {
@@ -655,8 +658,9 @@ public class ExcelUtil {
                if(!validateRange(sheet, range)) {
                        _range = getIntersectRange(sheet, range);
                        if(_range == null) {
-                               log.error("range is illegal: [{}]", range.formatAsString());
-                               throw new JaxcelInputException("range is illegal");
+                               log.warn("range is illegal: [{}]", range.formatAsString());
+                               log.trace("clearRange end");
+                               return;
                        }
                        else {
                                log.info("resize range: [{}]", _range.formatAsString());
@@ -1430,4 +1434,98 @@ public class ExcelUtil {
                        }
                }
        }
+       
+       /**
+        * セルのアドレス比較<br>
+        * 
+        * @param cellA 対象セルA
+        * @param cellB 対象セルB
+        * @param isCheckSheet  trueの場合、シート名を含め比較する
+        * 
+        * @return 判定結果<br>
+        * 対象セルのいずれかでもnullの場合はfalse
+        * 
+        * @since 1.01.00
+        */
+       public static boolean equalsCellAddress(Cell cellA, Cell cellB, boolean isCheckSheet) {
+               
+               if(cellA == null || cellB == null) return false;
+               
+               if(isCheckSheet) {
+                       if(!(cellA.getSheet().getSheetName().equals(cellB.getSheet().getSheetName()))) {
+                               return false;
+                       }
+               }
+               return cellA.getColumnIndex() == cellB.getColumnIndex() &&
+                               cellA.getRowIndex() == cellB.getRowIndex();
+               
+       }
+       
+       /**
+        * セルの文字列・数式から指定された正規表現の最初の部分を指定された文字列に置換した結果を返却<br>
+        * 
+        * replacement内でのバックスラッシュ (\) とドル記号 ($) は、String.replaceFirstと同様に作用します。
+        * 
+        * @param cell  対象セル
+        * @param target        置換される文字列
+        * @param replacement   置き換える文字列
+        * @param regex trueの場合、targetを正規表現として扱う
+        * 
+        * @return 置換後のセル文字列・計算式<br>
+        * 対象セルがnullの場合はnull<br>
+        * 対象セルのセルタイプが文字列・数式以外の場合はnull
+        * 
+        * @since 1.01.00
+        */
+       public static String replaceFirstCellValue(Cell cell, String target, String replacement, boolean regex) {
+               // チェック
+               if(cell == null) return null;
+               String _replacement = replacement == null ? "" : replacement;
+               String _return = null;
+               // セルタイプ毎処理
+               switch(cell.getCellType()) {
+               case Cell.CELL_TYPE_STRING:
+                       if(target == null) _return = cell.getStringCellValue();
+                       try {
+                               _return = cell.getStringCellValue().replaceFirst(regex ? target : Pattern.quote(target), _replacement);
+                       }
+                       catch(PatternSyntaxException e) {
+                               log.warn("replaceFirst error. {}", e.getMessage());
+                       }
+                       break;
+               case Cell.CELL_TYPE_FORMULA:
+                       if(target == null) _return = cell.getCellFormula();
+                       try {
+                               _return = cell.getCellFormula().replaceFirst(regex ? target : Pattern.quote(target), _replacement);
+                       }
+                       catch(PatternSyntaxException e) {
+                               log.warn("replaceFirst error. {}", e.getMessage());
+                       }
+                       break;
+               default:
+                       // セルタイプが上記以外の場合はnull返却
+                       log.warn("cell type is not strng or formula");
+                       return null;
+               }
+               if(_return == null) _return = "";
+               return _return;
+       }       
+
+       /**
+        * セルの文字列・数式から指定された文字列の最初の部分を指定された文字列に置換した結果を返却<br>
+        * 引数のtarget, replacementともに正規表現として扱いません。
+        * 
+        * @param cell  対象セル
+        * @param target        置換される文字列
+        * @param replacement   置き換える文字列
+        * 
+        * @return 置換後のセル文字列・計算式<br>
+        * 対象セルがnullの場合はnull<br>
+        * 対象セルのセルタイプが文字列・数式以外の場合はnull
+        * 
+        * @since 1.01.00
+        */
+       public static String replaceFirstCellValue(Cell cell, String target, String replacement) {
+               return replaceFirstCellValue(cell, target, Matcher.quoteReplacement(replacement), false);
+       }
 }
index 58afbdc..4439cb0 100644 (file)
@@ -30,7 +30,7 @@ import org.slf4j.LoggerFactory;
 /**
  * Excel帳票出力コマンドラインツールクラス
  * 
- * @version 1.00.00
+ * @since 1.00.00
  * @author Noboru Saito
  */
 public class MakeReportTool {