• R/O
  • SSH

Commit

Tags
No Tags

Frequently used words (click to add to your profile)

javac++androidlinuxc#windowsobjective-ccocoa誰得qtpythonphprubygameguibathyscaphec計画中(planning stage)翻訳omegatframeworktwitterdomtestvb.netdirectxゲームエンジンbtronarduinopreviewer

Stub for WebAPI


Commit MetaInfo

Revision81a5d2fd9d1db267745b15b118dba0de1e6659cb (tree)
Time2021-11-27 20:42:15
AuthorNanaH
CommiterNanaH

Log Message

Thymeleaf対応

Change Summary

Incremental Difference

diff -r 6d19018d54b6 -r 81a5d2fd9d1d pagedata/example/sports/gameresults/baseball.json
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/pagedata/example/sports/gameresults/baseball.json Sat Nov 27 20:42:15 2021 +0900
@@ -0,0 +1,14 @@
1+[
2+ {
3+ "year": "${年}",
4+ "league": "${リーグ}",
5+ "rank": "${順位}",
6+ "team": "${チーム}",
7+ "game": "${試合数}",
8+ "win": "${勝}",
9+ "lose": "${負}",
10+ "draw": "${引分}",
11+ "rate": "${勝率}",
12+ "gap": "${ゲーム差}",
13+ }
14+]
diff -r 6d19018d54b6 -r 81a5d2fd9d1d pagedata/example/sports/gameresults/baseball.xls
Binary file pagedata/example/sports/gameresults/baseball.xls has changed
diff -r 6d19018d54b6 -r 81a5d2fd9d1d pagedata/example/sports/gameresults/resultForm.json
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/pagedata/example/sports/gameresults/resultForm.json Sat Nov 27 20:42:15 2021 +0900
@@ -0,0 +1,6 @@
1+{
2+ "year" : "${#year#}",
3+ "league": "${#league#}",
4+ "from_rank" : "${#from_rank#}",
5+ "to_rank" : "${#to_rank#}"
6+}
diff -r 6d19018d54b6 -r 81a5d2fd9d1d pagedata/example/sports/gameresults/soccer.json
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/pagedata/example/sports/gameresults/soccer.json Sat Nov 27 20:42:15 2021 +0900
@@ -0,0 +1,14 @@
1+[
2+ {
3+ "year": "${年}",
4+ "league": "${リーグ}",
5+ "rank": "${順位}",
6+ "team": "${チーム}",
7+ "game": "${試合数}",
8+ "win": "${勝}",
9+ "lose": "${負}",
10+ "draw": "${引分}",
11+ "score": "${得点}",
12+ "lost": "${失点}"
13+ }
14+]
diff -r 6d19018d54b6 -r 81a5d2fd9d1d pagedata/example/sports/gameresults/soccer.xls
Binary file pagedata/example/sports/gameresults/soccer.xls has changed
diff -r 6d19018d54b6 -r 81a5d2fd9d1d pom.xml
--- a/pom.xml Wed Oct 13 07:33:47 2021 +0900
+++ b/pom.xml Sat Nov 27 20:42:15 2021 +0900
@@ -20,8 +20,16 @@
2020 <dependencies>
2121 <dependency>
2222 <groupId>org.springframework.boot</groupId>
23+ <artifactId>spring-boot-starter-thymeleaf</artifactId>
24+ </dependency>
25+ <dependency>
26+ <groupId>org.springframework.boot</groupId>
2327 <artifactId>spring-boot-starter-web</artifactId>
2428 </dependency>
29+ <dependency>
30+ <groupId>org.springframework.boot</groupId>
31+ <artifactId>spring-boot-devtools</artifactId>
32+ </dependency>
2533
2634 <dependency>
2735 <groupId>org.springframework.boot</groupId>
diff -r 6d19018d54b6 -r 81a5d2fd9d1d src/main/java/jp/nanah/bastub/BastubApplication.java
--- a/src/main/java/jp/nanah/bastub/BastubApplication.java Wed Oct 13 07:33:47 2021 +0900
+++ b/src/main/java/jp/nanah/bastub/BastubApplication.java Sat Nov 27 20:42:15 2021 +0900
@@ -9,6 +9,7 @@
99 public class BastubApplication {
1010
1111 private static final Logger logger = LoggerFactory.getLogger(BastubApplication.class);
12+
1213 public static void main(String[] args) {
1314 SpringApplication.run(BastubApplication.class, args);
1415 logger.info("*** BASTUB START ***");
diff -r 6d19018d54b6 -r 81a5d2fd9d1d src/main/java/jp/nanah/bastub/BastubConsts.java
--- a/src/main/java/jp/nanah/bastub/BastubConsts.java Wed Oct 13 07:33:47 2021 +0900
+++ b/src/main/java/jp/nanah/bastub/BastubConsts.java Sat Nov 27 20:42:15 2021 +0900
@@ -4,4 +4,9 @@
44
55 public static final String PATH_REPLACE_HEAD = "$";
66
7+ /**
8+ * VIEWに遷移することを示す拡張子。
9+ * 通常、リクエストはJSON応答だが、HTML応答と判断されたときこの拡張子が付与される。
10+ */
11+ public static final String VIEW_TRANS_EXT = ".$view";
712 }
diff -r 6d19018d54b6 -r 81a5d2fd9d1d src/main/java/jp/nanah/bastub/config/BastubLinkBuilder.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/java/jp/nanah/bastub/config/BastubLinkBuilder.java Sat Nov 27 20:42:15 2021 +0900
@@ -0,0 +1,60 @@
1+package jp.nanah.bastub.config;
2+
3+import java.util.Map;
4+
5+import org.slf4j.Logger;
6+import org.slf4j.LoggerFactory;
7+import org.springframework.stereotype.Component;
8+import org.thymeleaf.context.IExpressionContext;
9+import org.thymeleaf.context.WebEngineContext;
10+import org.thymeleaf.linkbuilder.AbstractLinkBuilder;
11+import org.thymeleaf.linkbuilder.StandardLinkBuilder;
12+
13+@Component
14+public class BastubLinkBuilder extends AbstractLinkBuilder{
15+
16+ protected static final Logger logger = LoggerFactory.getLogger(BastubLinkBuilder.class);
17+
18+ private final StandardLinkBuilder standardLinkBuilder = new StandardLinkBuilder();
19+
20+ @Override
21+ public String buildLink(final IExpressionContext context, final String base, final Map<String, Object> parameters) {
22+ String repbase = base;
23+ if (base.startsWith("/")){
24+ if (context instanceof WebEngineContext){
25+ WebEngineContext webcon = (WebEngineContext)context;
26+ repbase = getContextRootBase(base, webcon.getRequest().getRequestURI());
27+ }
28+ }
29+
30+ String link = this.standardLinkBuilder.buildLink(context, repbase, parameters);
31+ return link;
32+ }
33+
34+ /**
35+ * パスの先頭にコンテキストルートを追加する
36+ * 通常、絶対パス指定の場合、Thymeleafは
37+ * "@{/foo/bar}"→"http://hostname:port/コンテキストルート/foo/bar"
38+ * のようなパスを生成するので、何もしなければ
39+ * "@{/foo/bar}"→"http://hostname:port/foo/bar"
40+ * または
41+ * "@{/foo/bar}"→"http://hostname:port/bastub/foo/bar"
42+ * のようなパスが生成される。
43+ * Stubとして動かしたい場合は、元のコンテキストルート(例:example)がパスの中に
44+ * 含まれていてほしいので、
45+ * "@{/foo/bar}"→"http://hostname:port/<b>example</b>/foo/bar"
46+ * "@{/foo/bar}"→"http://hostname:port/bastub/<b>example</b>/foo/bar"
47+ * というパスが吐き出されるようにする。
48+ * @param base
49+ * @param uri
50+ * @return
51+ */
52+ private String getContextRootBase(String base, String uri){
53+ int n = uri.indexOf("/", 1);
54+ if (n > 0){
55+ base = uri.substring(0, n) + base;
56+ }
57+ return base;
58+ }
59+
60+}
diff -r 6d19018d54b6 -r 81a5d2fd9d1d src/main/java/jp/nanah/bastub/config/BastubWebConfig.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/java/jp/nanah/bastub/config/BastubWebConfig.java Sat Nov 27 20:42:15 2021 +0900
@@ -0,0 +1,55 @@
1+package jp.nanah.bastub.config;
2+
3+import java.util.Set;
4+
5+import org.slf4j.Logger;
6+import org.slf4j.LoggerFactory;
7+import org.springframework.beans.factory.annotation.Autowired;
8+import org.springframework.beans.factory.annotation.Value;
9+import org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration;
10+import org.springframework.context.annotation.Bean;
11+import org.springframework.context.annotation.Configuration;
12+import org.springframework.context.annotation.Import;
13+import org.thymeleaf.spring5.SpringTemplateEngine;
14+import org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver;
15+import org.thymeleaf.templateresolver.ITemplateResolver;
16+
17+import jp.nanah.bastub.service.PathService;
18+
19+@Configuration
20+@Import(ThymeleafAutoConfiguration.class)
21+public class BastubWebConfig {
22+
23+ protected static final Logger logger = LoggerFactory.getLogger(BastubLinkBuilder.class);
24+
25+ @Value("${pagedata.root:pagedata}")
26+ private String pagedataPath;
27+
28+ @Autowired
29+ private SpringTemplateEngine templateEngine;
30+
31+ @Autowired
32+ private BastubLinkBuilder bastubLinkBuilder;
33+
34+ @Bean
35+ public SpringTemplateEngine customTemplateEngine() {
36+ this.templateEngine.setLinkBuilder(this.bastubLinkBuilder);
37+
38+ Set<ITemplateResolver> resolvers = templateEngine.getTemplateResolvers();
39+ for (ITemplateResolver resol : resolvers){
40+ if (resol instanceof SpringResourceTemplateResolver){
41+ try {
42+ SpringResourceTemplateResolver srtr = (SpringResourceTemplateResolver)resol;
43+ srtr.setPrefix("file:" + PathService.pageDir.getAbsolutePath());
44+ logger.info("Resource Path set to {}", srtr.getPrefix());
45+ } catch (Exception e){
46+ logger.error("Bad Prefix Path", e);
47+ }
48+ }
49+ }
50+
51+ templateEngine.setLinkBuilder(bastubLinkBuilder);
52+ return this.templateEngine;
53+ }
54+}
55+
diff -r 6d19018d54b6 -r 81a5d2fd9d1d src/main/java/jp/nanah/bastub/controller/AnyRestController.java
--- a/src/main/java/jp/nanah/bastub/controller/AnyRestController.java Wed Oct 13 07:33:47 2021 +0900
+++ b/src/main/java/jp/nanah/bastub/controller/AnyRestController.java Sat Nov 27 20:42:15 2021 +0900
@@ -2,31 +2,15 @@
22
33 import static org.springframework.web.bind.annotation.RequestMethod.*;
44
5-import java.io.File;
6-import java.io.IOException;
7-import java.util.ArrayList;
8-import java.util.Arrays;
9-import java.util.Collections;
105 import java.util.List;
116 import java.util.Map;
12-import java.util.Set;
13-import java.util.TreeMap;
147
158 import javax.servlet.http.HttpServletRequest;
169 import javax.servlet.http.HttpServletResponse;
1710
18-import org.apache.commons.lang3.ArrayUtils;
19-import org.apache.commons.lang3.StringUtils;
20-import org.apache.commons.lang3.math.NumberUtils;
21-import org.apache.poi.ss.usermodel.Cell;
22-import org.apache.poi.ss.usermodel.Row;
23-import org.apache.poi.ss.usermodel.Sheet;
24-import org.apache.poi.ss.usermodel.Workbook;
25-import org.apache.poi.ss.usermodel.WorkbookFactory;
2611 import org.slf4j.Logger;
2712 import org.slf4j.LoggerFactory;
2813 import org.springframework.beans.factory.annotation.Autowired;
29-import org.springframework.beans.factory.annotation.Value;
3014 import org.springframework.http.MediaType;
3115 import org.springframework.ui.ModelMap;
3216 import org.springframework.web.bind.annotation.RequestBody;
@@ -34,14 +18,9 @@
3418 import org.springframework.web.bind.annotation.RequestParam;
3519 import org.springframework.web.bind.annotation.RestController;
3620
37-import jp.nanah.bastub.BastubConsts;
38-import jp.nanah.bastub.data.FilterParam;
39-import jp.nanah.bastub.data.JsonInfo;
40-import jp.nanah.bastub.data.KvData;
21+import jp.nanah.bastub.data.AppInfo;
4122 import jp.nanah.bastub.service.JsonService;
42-import jp.nanah.bastub.service.PageDataService;
43-import jp.nanah.bastub.service.UsingInfo;
44-import jp.nanah.bastub.util.BastubUtils;
23+import jp.nanah.bastub.service.PathService;
4524
4625 @RestController
4726 public class AnyRestController {
@@ -51,28 +30,21 @@
5130 private static final Logger reqlog = LoggerFactory.getLogger("REQLOG");
5231
5332 @Autowired
33+ private AppInfo appService;
34+
35+ @Autowired
5436 private JsonService jsonService;
5537
56- /**
57- * アプリケーション識別値。ログを区別するため。
58- */
59- private static String APP_SNO = Long.toString(System.currentTimeMillis()/1000, 36).toUpperCase();
38+ @Autowired
39+ private PathService pathService;
6040
6141
62- /**
63- * リクエスト通番。起動するたびに0から始まる。
64- */
65- private static long reqNo = 0;
66-
6742 //*********************************
6843 // 主処理
6944 //*********************************
7045
71- @Value("${path_last_slash_valid:1}")
72- private int pathLastSlashValid;
73-
7446 /**
75- * パスの深さは最大10まで(可変にできる書き方を知らないので)。
47+ * HTTPリクエストを受け取って、JSONを返す。
7648 * HTTPのメソッドは、GET/POST/DELETE/PUTのみ対応。(これ以外は使わないと思うので)
7749 *
7850 * @param body
@@ -81,10 +53,8 @@
8153 * @param res
8254 * @return
8355 */
84- @RequestMapping(path="/**"
85- , method={GET, POST, PUT, DELETE})
56+ @RequestMapping(path="/**", method={GET, POST, PUT, DELETE})
8657 public String path1(@RequestBody(required=false) ModelMap body, ModelMap model, HttpServletRequest req, HttpServletResponse res) {
87- logger.debug("★1;" + req.getRequestURI());
8858 return path_common(body, model, req, res);
8959 }
9060
@@ -98,574 +68,38 @@
9868 * @param res
9969 * @return
10070 */
101- @RequestMapping(path= "/**"
102- , method={GET, POST, PUT, DELETE}
103- , consumes = {MediaType.APPLICATION_FORM_URLENCODED_VALUE}
104- )
71+ @RequestMapping(path= "/**", method={GET, POST, PUT, DELETE}
72+ , consumes = {MediaType.APPLICATION_FORM_URLENCODED_VALUE})
10573 public String path1_multipart( @RequestParam(required=false) Map<String, String> paramMap, ModelMap model, HttpServletRequest req, HttpServletResponse res) {
10674 return path_common(paramMap, model, req, res);
10775 }
10876
77+ //=================================
78+ // 共通処理
79+ //=================================
80+
10981 /**
11082 * リクエストの共通処理。
11183 */
112- private String path_common( Map paramMap, ModelMap model, HttpServletRequest req, HttpServletResponse res){
113-
114- synchronized (APP_SNO){
115- Thread.currentThread().setName(APP_SNO + "_" + reqNo);
116- reqNo++;
117- }
84+ private String path_common(Map paramMap, ModelMap model, HttpServletRequest req, HttpServletResponse res){
85+ appService.setCurrentThreadName();
11886
11987 //リクエストを出力
12088 String method = req.getMethod();
12189 String reqPath = req.getRequestURL().toString();
12290 String queryStr = (req.getQueryString() == null) ? "" : req.getQueryString();
12391 Object bodyData = (paramMap == null) ? "" : paramMap;
124- reqlog.info("[{}] {}{} {}", method, reqPath, queryStr, bodyData);
92+ reqlog.info("[{}] {} query={} body={}", method, reqPath, queryStr, bodyData);
12593
12694 logger.info("== ▼ == [{}] {}", method, reqPath);
12795 res.setContentType("application/json;charset=UTF-8");
128- String[] pathArray = req.getRequestURI().substring(1).split("/");
129- List<String> pathList = Arrays.asList(pathArray); //BastubUtils.toValidList(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10);
13096
131- if (pathLastSlashValid == 1){
132- //URLがスラッシュ終わりなら、ファイル名が""の要求が来たものとする
133- if (reqPath.endsWith("/")){
134- pathList.add("");
135- }
136- }
137- String result = getAny(pathList, paramMap, model, req, res);
97+ List<String> pathList = pathService.getPathList(req);
98+ String jsonText = jsonService.getAny(pathList, paramMap, model, req, res);
99+
138100 logger.info("== △ ==");
139101
140- return result;
141- }
142-
143-
144- //=================================
145- // 共通処理
146- //=================================
147-
148- @Autowired
149- private PageDataService pgService;
150-
151- /**
152- * 【共通処理】ここが主処理。
153- *
154- * @param pathList
155- * @param body nullの場合あり
156- * @param model 未使用。将来削除するかも。
157- * @param req HTTPリクエスト
158- * @param res HTTPレスポンス
159- * @return
160- */
161- public String getAny(List<String> pathList, Map body, ModelMap model, HttpServletRequest req, HttpServletResponse res){
162- try {
163- File pageDir = pgService.getPageDir();
164-
165- if (pageDir.exists() == false){
166- String errormsg = "Bastubの設定フォルダ[" + pageDir.getAbsolutePath() + "]が見つかりません。application.propertiesのpagedata.rootを見直してください。";
167- logger.info(errormsg);
168- return errormsg;
169- }
170-
171- UsingInfo ui = UsingInfo.getInitInstance(pathList, req);
172-
173- //パスを生成
174- String path = StringUtils.join(pathList, "/");
175- File dataFile = BastubUtils.getPagedataPath(pageDir, path, req.getMethod(), ".xlsx,.xls");
176- File jsonFile = BastubUtils.getPagedataPath(pageDir, path, req.getMethod(), ".json");
177-
178- //データファイルを読み込む
179- //-----------------------------------------------------------------------------------------------------
180- //データファイルは、パス名+"_"+HTTPメソッド名のファイルがあればそれを優先する。ない場合はパス名のみ。
181- //データファイルはファイルなし、あるいはファイル内のdataシートやfilterシートが無くてもOK。
182- //==>データファイルがなければデータ埋め込みされないのでJSONは固定値で返る。
183- //-----------------------------------------------------------------------------------------------------
184- Sheet paramSheet = null;
185- Sheet dataSheet = null;
186- Workbook wb = readWorkbook(dataFile);
187- if (wb == null) {
188- logger.warn("Excelファイル無しのためJSONを固定で応答 (ExcelFile=[{}])", dataFile.getAbsolutePath());
189- } else {
190- paramSheet = wb.getSheet("filter");
191- dataSheet = wb.getSheet("data");
192- wb.close();
193-
194- if (paramSheet == null) {
195- logger.debug("定義ファイル[{}]に[filter]シートが見つかりません", dataFile.getAbsolutePath());
196- }
197- if (dataSheet == null) {
198- logger.debug("定義ファイル[{}]に[data]シートが見つかりません", dataFile.getAbsolutePath());
199- }
200- }
201- //データ範囲をトリミング(エクセルはデータがなくても編集範囲だったりするので)
202- trimDataSheet(dataSheet);
203-
204- //=================================
205- // [1] リクエスト値をKeyValueリスト化
206- //=================================
207- List<KvData> requestData = getRequestData(req.getParameterMap(), body);
208-
209- //=================================
210- // [2] 絞り込み条件を取得
211- //=================================
212- List<FilterParam> allFilter = getFilterParamList(paramSheet);
213- List<FilterParam> validFilter = null;
214- if (allFilter != null){
215- validFilter = getValidFilter(allFilter, requestData, pathList);
216- }
217-
218- //=================================
219- // [3] エクセルデータから応答に返すレコードを絞り込む
220- //=================================
221- List<Row> resultTarget = pickupSheetData(dataSheet, validFilter);
222-
223- //=================================
224- // [4] 応答用の入れ物を読み込む
225- //=================================
226- JsonInfo jsonInfo = jsonService.readJsonFile(jsonFile);
227- if (jsonInfo.getJsonObject() == null) {
228- //ファイルがない場合は自動で作られるのでここを通過することは殆どない
229- res.setStatus(HttpServletResponse.SC_NOT_FOUND);
230- String jsonPath = jsonFile.getAbsolutePath();
231- logger.warn("## ◆ 応答JSON異常: 理由=[{}] パス=[{}]", jsonInfo.getErrorMessage(), jsonPath);
232- //String errorJson = "{ \"no_file\": \"" + jsonPath.replaceAll("\\\\", "/") + "\"}";
233- String errorJson = "no_file - " + jsonPath.replaceAll("\\\\", "/") + "]";
234- return errorJson;
235- }
236-
237- //=================================
238- // [5] 応答データを生成
239- //=================================
240- jsonService.setDataToJsonObject(null, jsonInfo.getJsonObject(), resultTarget, ui);
241-
242- //応答
243- String jsonText = jsonInfo.getJsonObject().toString(4);
244- if (jsonInfo.isTopArray()){
245- //{"dummy":xxxx … } のカッコを除去する
246- int n1 = jsonText.indexOf(":");
247- int n2 = jsonText.lastIndexOf("}");
248- jsonText = jsonText.substring(n1+1, n2);
249- }
250-
251- //Edgeから直接リクエストするとエラーになることへの対応
252- res.addHeader("Access-Control-Allow-Origin", "*");
253-
254- return jsonText;
255-
256- } catch (Throwable th) {
257- logger.info("処理異常: {}", th.toString());
258- return th.toString();
259- }
260-
261- }
262-
263- //-------------------
264- // Excelデータの処理
265- //-------------------
266-
267- /**
268- * エクセルファイルごと保存。
269- */
270- private Map<String, Object[]> workbookCache = new TreeMap<String, Object[]>();
271-
272- /**
273- * Workbookを読み込む。
274- * キャッシュがあればそれを返す。
275- *
276- * @param file
277- * @return
278- * @throws IOException
279- */
280- protected synchronized Workbook readWorkbook(File file) throws IOException {
281- if (file.exists() == false) {
282- return null;
283- }
284-
285- String key = file.getAbsolutePath();
286- Object[] cache = workbookCache.get(key);
287- if (cache != null){
288- Long filetime = (Long)cache[0];
289- if (filetime.longValue() == file.lastModified()){
290- return (Workbook)cache[1];
291- }
292- }
293-
294- Workbook wb = WorkbookFactory.create(file, null, true);//new HSSFWorkbook(poiFileSystem);
295- workbookCache.put(key, new Object[]{file.lastModified(), wb});
296- return wb;
297- }
298-
299- protected void trimDataSheet(Sheet sheet){
300- if (sheet == null || sheet.getLastRowNum() == 0){
301- return;
302- }
303-
304- //1行目(ヘッダ行)から、列数を確認
305- Row topRow = sheet.getRow(0);
306- int lastColNum = topRow.getLastCellNum() - 1;
307- while (lastColNum >= 0){
308- Cell cell = topRow.getCell(lastColNum);
309- String s = BastubUtils.getCellText(cell);
310- if (StringUtils.isNotBlank(s)){
311- break;
312- }
313- lastColNum--;
314- }
315-
316- int rowNum = sheet.getLastRowNum();
317- while (rowNum >= 0) {
318- Row row = sheet.getRow(rowNum);
319- if (row != null){
320- if (BastubUtils.isBlankRow(row)){
321- sheet.removeRow(row);
322- } else {
323- for (int i=row.getLastCellNum()-1; i>lastColNum; i--){
324- Cell cell = row.getCell(i);
325- if (cell != null){
326- row.removeCell(cell);
327- }
328- }
329- }
330- }
331- rowNum--;
332- }
333- }
334-
335- /**
336- * [1]
337- * @param paramMap
338- * @param body
339- * @return
340- */
341- public List<KvData> getRequestData(Map<String, String[]> paramMap, Map body){
342- List<KvData> paramList = new ArrayList<KvData>();
343-
344- //URLの後ろに渡されるリクエストパラメータをKey/Value形式にする
345- if (paramMap != null) {
346- int cnt = 0;
347- for (Map.Entry<String, String[]> ent : paramMap.entrySet()) {
348- //BODYで渡していてもParamMapに入っているので除外する。
349- if (BastubUtils.isJsonEntry(ent)){
350- continue;
351- }
352- List<String> key = Arrays.asList(new String[] {ent.getKey()});
353- for (String val : ent.getValue()) {
354- KvData kv = appendKvParam(new KvData(key, val), paramList);
355- cnt++;
356- }
357- }
358- }
359-
360- //BodyリクエストをKey/Value形式にする
361- if (body != null) {
362- int startSize = paramList.size();
363- appendEntryToParamList(body.entrySet(), null, paramList);
364- for (int i=startSize; i<paramList.size(); i++){
365- KvData kv = paramList.get(i);
366- }
367- }
368-
369- return paramList;
102+ return jsonText;
370103 }
371104
372- //■TODO 見直し候補
373- //ここの考え方は見直したほうがよい。
374- //リクエスト(QueryStringやBODY)の取得時点ではキー値は重複していてよいし、
375- //key1/key2で組み合わせのときに一致する考え方でないとうまくいかなくなる。
376- //クラス化してあるべきデータ保持形式にすべき。
377-
378- /**
379- * キーが一意になるようにKvDataを追加する。
380- * リクエストやJSONで同一キーがある場合、どれかが一致するような判断となるようにする。
381- *
382- * @param src
383- * @param dstList
384- */
385- private KvData appendKvParam(KvData src, List<KvData> dstList){
386- KvData nowval = null;
387- for (int i=0; i<dstList.size(); i++){
388- if (BastubUtils.equals(dstList.get(i).getKey(), src.getKey())){
389- nowval = dstList.get(i);
390- break;
391- }
392- }
393-
394- if (nowval == null){
395- dstList.add(src);
396- nowval = src;
397-
398- } else {
399- nowval.addValue(src.getValueAsOne());
400- }
401- return nowval;
402- }
403-
404- private void appendEntryToParamList(Set<Map.Entry<String, Object>> set, List<String> parentKeys, List<KvData> paramList){
405- int count = 0;
406- for (Map.Entry<String, Object> ent : set) {
407- String key = ent.getKey();
408- Object val = (ent.getValue() == null) ? "" : ent.getValue();
409-
410- List<String> thisKeys = new ArrayList<String>();
411- if (parentKeys != null){
412- thisKeys.addAll(parentKeys);
413- }
414- thisKeys.add(key);
415-
416- if (val instanceof Map){
417-
418- Map<String, Object> vmap = (Map<String, Object>)val;
419- appendEntryToParamList(vmap.entrySet(), thisKeys, paramList);
420-
421- } else if (val instanceof List){
422-
423- List<Object> vlist = (List<Object>)val;
424- appendListToParamList(vlist, thisKeys, paramList);
425-
426- } else {
427- appendToParamList(val, thisKeys, paramList);
428- }
429- count++;
430- }
431- }
432-
433- private void appendListToParamList(List<Object> list, List<String> parentKeys, List<KvData> paramList){
434- for (Object o : list){
435- if (o instanceof Map){
436- Map<String, Object> map = (Map<String, Object>)o;
437- appendEntryToParamList(map.entrySet(), parentKeys, paramList);
438- } else if (o instanceof List){
439- List<Object> vlist = (List<Object>)o;
440- appendListToParamList(vlist, parentKeys, paramList);
441- } else {
442- appendToParamList(o, parentKeys, paramList);
443- }
444- }
445- }
446-
447- private void appendToParamList(Object obj, List<String> parentKeys, List<KvData> paramList){
448- String valstr = obj.toString();
449- KvData kv = new KvData(parentKeys, valstr);
450- appendKvParam(kv, paramList);
451- }
452-
453- /**
454- * [2]
455- * エクセルデータをフィルタリングするときの定義パラメータを取得する。
456- * ここでは置き換え文字や接尾辞を加工できないので設定値をそのまま取り出す(置き換えない)
457- *
458- * @param sheet
459- * @return Map<Cellの列名, フィルタリングデータのリスト>
460- */
461- private List<FilterParam> getFilterParamList(Sheet sheet){
462- List<FilterParam> filterList = null;
463- if (sheet == null) {
464- return filterList;
465- }
466-
467- for (int i=0; i<=sheet.getLastRowNum(); i++) {
468- Row row = sheet.getRow(i);
469- List<String> cellList = BastubUtils.getRowValueList(row, false);
470-
471- //String prevName = "";
472- if (cellList.size() >= 3) {
473- // 1行でもフィルタリングデータがあるときだけフィルタリングを適用する
474- if (filterList == null){
475- filterList =new ArrayList<FilterParam>();
476- }
477- String cellName = cellList.get(0);
478- String compType = cellList.get(1);
479- List<String> paramKey = cellList.subList(2, cellList.size());
480- FilterParam fp = new FilterParam(cellName, compType, paramKey);
481-
482- filterList.add(fp);
483- }
484- }
485- return filterList;
486- }
487-
488- /**
489- * [2]-2
490- * フィルタリング時に判定する、データシートの列値と比較する判定値を設定する。
491- * フィルタリングで使用するキー値がrequestDataにあるときだけ、requestDataの値をフィルタリングに採用する。
492- * 判定値がないときはnullのまま。
493- *
494- * @param requestData
495- * @param filterParamList
496- */
497- public List<FilterParam> getValidFilter(List<FilterParam> filterParamList, List<KvData> requestData, List<String> pathList) {
498- List<FilterParam> validList = new ArrayList<FilterParam>();
499- for (FilterParam fp : filterParamList) {
500- //パスインデックスのときは、URLのパス値に置き換える
501- if (fp.getRequestKeys().size() > 0) {
502- String key0 = fp.getRequestKeys().get(0);
503- if (key0.startsWith(BastubConsts.PATH_REPLACE_HEAD)) {
504- int pathNumber = NumberUtils.toInt(key0.substring(1));
505- if (pathNumber > 0 && pathNumber <= pathList.size()) {
506- fp.setOneValue(pathList.get(pathNumber - 1));
507- validList.add(fp);
508- continue;
509- }
510- }
511- }
512-
513- //要求キーのときは要求パラメータから値を探す
514- List<String> reqKey = fp.getRequestKeys(); //こっちは定義
515-
516- //実際のリクエストのキー値と比較
517- boolean isFound = false;
518- String delimText = getDelimText(fp.getCompareType());
519- for (KvData kv : requestData) {
520- if (BastubUtils.equalsTail(kv.getKey(), reqKey)) {
521- List<String> fpValues = toFilterValue(delimText, kv.getValues());
522- fp.setValues(fpValues);
523- validList.add(fp);
524- isFound = true;
525- //break;
526- }
527- }
528- if (isFound == false){
529- logger.warn("HTTPリクエスト内に、項目値[{}]が見つかりません。", reqKey);
530- }
531- if (delimText != null){
532- fp.setCompareType("=");
533- }
534- }
535- return validList;
536- }
537-
538- private String getDelimText(String compareType){
539- int n1 = compareType.indexOf("[");
540- int n2 = compareType.indexOf("]");
541- if (n1 <0 || n2 < 0){
542- return null;
543- }
544- String delim = compareType.substring(n1+1, n2);
545- return delim;
546- }
547-
548- // 記号が=[x]のとき、値を分割する
549- private List<String> toFilterValue(String delimText, List<String> org){
550- if (delimText == null){
551- return org;
552- }
553-
554- List<String> dst = new ArrayList<String>();
555- for (String s: org){
556- dst.addAll(Arrays.asList((String[]) s.split(delimText, 0)));
557- }
558- return dst;
559- }
560-
561- /**
562- * [3]
563- */
564- public List<Row> pickupSheetData(Sheet sheet, List<FilterParam> validFilter) {
565- List<Row> noDatasheet = new ArrayList<Row>();
566- if (sheet == null) {
567- return noDatasheet;
568- }
569-
570- //まずは全データを対象にする
571- List<Row> sheetData = new ArrayList<Row>();
572- for (int i=sheet.getFirstRowNum(); i<=sheet.getLastRowNum(); i++) {
573- Row row = sheet.getRow(i);
574- if (row != null){
575- sheetData.add(sheet.getRow(i));
576- }
577- }
578-
579- // フィルターがないときは全データ対象
580- if (validFilter == null || validFilter.isEmpty()){
581- return sheetData;
582- }
583-
584- //列名
585- String[] columnNames = getColumnNames(sheet);
586-
587- //フィルターがあるときは適合しないものを除去する
588- for (FilterParam fp : validFilter) {
589- int columnIndex = ArrayUtils.indexOf(columnNames, fp.getColumnName());
590- if (columnIndex < 0) {
591- logger.warn("***** dataシートに、列名[{}]がありません。", fp.getColumnName());
592- continue;
593- }
594-
595- //↓先頭行は列名なので除外する
596- for (int i=1; i<sheetData.size(); i++) {
597- Row row = sheetData.get(i);
598- if (row == null) {
599- continue;
600- }
601-
602- Cell cell = row.getCell(columnIndex);
603- String v = BastubUtils.getCellText(cell);
604- if (v == null){
605- continue;
606- }
607-
608- boolean isMatch = false;
609- String ct = fp.getCompareType();
610- for (String fv : fp.getValues()){
611-
612- int n;
613- if (StringUtils.isNumericSpace(v) && StringUtils.isNumericSpace(fv)){
614- Integer v1 = Integer.parseInt(v);
615- Integer v2 = Integer.parseInt(fv);
616- n = v1.compareTo(v2);
617- } else {
618- n = v.compareTo(fv);
619- }
620-
621- if (ct.equals("=")) {
622- isMatch = (n == 0);
623- } else if (ct.equals("<")) {
624- isMatch = n < 0;
625- } else if (ct.equals("<=")) {
626- isMatch = n <= 0;
627- } else if (ct.equals(">")) {
628- isMatch = n > 0;
629- } else if (ct.equals(">=")) {
630- isMatch = n >= 0;
631- } else if (ct.equals("!=") || ct.equals("<>")) {
632- isMatch = n != 0;
633- }
634- if (isMatch){
635- break;
636- }
637- }
638-
639- if (isMatch == false) {
640- sheetData.set(i, null);
641- }
642- }
643- }
644-
645- sheetData.removeAll(Collections.singleton(null));
646-
647- //debug
648- for (Row row : sheetData) {
649- StringBuilder sb = new StringBuilder();
650- for (int i=row.getFirstCellNum(); i<row.getLastCellNum(); i++) {
651- if (sb.length() > 0) {
652- sb.append(",");
653- }
654- sb.append(BastubUtils.getCellText(row.getCell(i)));
655- }
656- }
657-
658- return sheetData;
659- }
660-
661- private String[] getColumnNames(Sheet sheet) {
662- Row row = sheet.getRow(sheet.getFirstRowNum());
663- String[] columns = new String[row.getLastCellNum()];
664- for (int i=row.getFirstCellNum(); i<row.getLastCellNum(); i++) {
665- columns[i] = BastubUtils.getCellText(row.getCell(i));
666- }
667- return columns;
668- }
669-
670-
671105 }
diff -r 6d19018d54b6 -r 81a5d2fd9d1d src/main/java/jp/nanah/bastub/controller/AnyViewController.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/java/jp/nanah/bastub/controller/AnyViewController.java Sat Nov 27 20:42:15 2021 +0900
@@ -0,0 +1,141 @@
1+package jp.nanah.bastub.controller;
2+
3+import java.io.File;
4+import java.io.IOException;
5+import java.util.ArrayList;
6+import java.util.HashMap;
7+import java.util.List;
8+import java.util.Map;
9+
10+import javax.servlet.http.HttpServletRequest;
11+import javax.servlet.http.HttpServletResponse;
12+
13+import org.slf4j.Logger;
14+import org.slf4j.LoggerFactory;
15+import org.springframework.beans.factory.annotation.Autowired;
16+import org.springframework.http.MediaType;
17+import org.springframework.stereotype.Controller;
18+import org.springframework.ui.ModelMap;
19+import org.springframework.web.bind.annotation.RequestBody;
20+import org.springframework.web.bind.annotation.RequestMapping;
21+import org.springframework.web.bind.annotation.RequestParam;
22+
23+import com.fasterxml.jackson.databind.ObjectMapper;
24+
25+import jp.nanah.bastub.BastubConsts;
26+import jp.nanah.bastub.data.AppInfo;
27+import jp.nanah.bastub.service.JsonService;
28+import jp.nanah.bastub.service.PathService;
29+import jp.nanah.bastub.util.BastubUtils;
30+
31+/**
32+ *
33+ * @author kemono
34+ *
35+ */
36+@Controller
37+public class AnyViewController {
38+
39+ /** 動作ログ */
40+ private static final Logger logger = LoggerFactory.getLogger(AnyViewController.class);
41+ /** リクエストのみ */
42+ private static final Logger reqlog = LoggerFactory.getLogger("REQLOG");
43+
44+ @Autowired
45+ private AppInfo appService = new AppInfo();
46+
47+ @Autowired
48+ private JsonService jsonService;
49+
50+ @Autowired
51+ private PathService pathService;
52+
53+
54+ //*********************************
55+ // 主処理
56+ //*********************************
57+
58+ @RequestMapping(value = {"/**/*.$view"})
59+ public String path1(@RequestBody(required=false) ModelMap body, ModelMap model, HttpServletRequest req, HttpServletResponse res) throws IOException {
60+ return path_common(body, model, req, res);
61+ }
62+
63+ @RequestMapping(path= "/**", consumes = {MediaType.APPLICATION_FORM_URLENCODED_VALUE})
64+ public String path1_multipart( @RequestParam(required=false) Map<String, String> paramMap, ModelMap model, HttpServletRequest req, HttpServletResponse res) {
65+ return path_common(paramMap, model, req, res);
66+ }
67+
68+ //=================================
69+ // 共通処理
70+ //=================================
71+
72+ public String path_common(Map paramMap, ModelMap model, HttpServletRequest req, HttpServletResponse res){
73+ appService.setCurrentThreadName();
74+
75+ //リクエストを出力
76+ String method = req.getMethod();
77+ String reqPath = req.getRequestURL().toString();
78+ String queryStr = (req.getQueryString() == null) ? "" : req.getQueryString();
79+ Object bodyData = (paramMap == null) ? "" : paramMap;
80+ reqlog.info("[{}] {}, query={} body={}", method, reqPath, queryStr, bodyData);
81+
82+ //同名フォルダ配下のjsonファイルを見つける
83+ File pageDir = pathService.getPageDir();
84+ List<String> pathList = new ArrayList<String>(pathService.getPathList(req));
85+ int lastIdx = pathList.size() - 1;
86+
87+ // Viewに遷移するためにつけた拡張子を取り除く(元に戻す)
88+ String lastPath = pathList.get(lastIdx);
89+ pathList.set(lastIdx, BastubUtils.removeExtText(lastPath, BastubConsts.VIEW_TRANS_EXT));
90+ pathList.add("");
91+ lastIdx++;
92+
93+ //遷移先のView名を取得する
94+ String viewName = getViewName(req);
95+
96+ //
97+ //logger.debug("pageDir=[{}] Viewに遷移:[{}]", pageDir.getAbsolutePath(), viewName);
98+ File file = new File(pageDir, viewName);
99+ if (file.isDirectory()){
100+ File[] subDirFiles = file.listFiles();
101+ for (File f : subDirFiles){
102+ //logger.info("subdir=[{}", f.getAbsolutePath());
103+ String ext = BastubUtils.getExtText(f.getAbsolutePath());
104+ if (ext.equals(".json")){
105+ pathList.set(lastIdx, BastubUtils.removeExtText(f.getName(), ".json"));
106+ String jsonText = jsonService.getAny(pathList, paramMap, model, req, res);
107+ try {
108+ String modelKey = BastubUtils.removeExtText(f.getName(), null);
109+ if (jsonText.trim().startsWith("[") && jsonText.endsWith("]")){
110+ List<Object> response = new ObjectMapper().readValue(jsonText, ArrayList.class);
111+ model.addAttribute(modelKey, response);
112+ } else {
113+ Map<String, Object> response = new ObjectMapper().readValue(jsonText, HashMap.class);
114+ model.addAttribute(modelKey, response);
115+ }
116+ } catch (Exception e){
117+ logger.error("変換異常", e);
118+ }
119+ }
120+ }
121+
122+ }
123+
124+ logger.info("== △ ==");
125+ return viewName;
126+ }
127+
128+ /**
129+ * リクエストから、本来のView名
130+ * @param req
131+ * @return
132+ */
133+ private String getViewName(HttpServletRequest req){
134+ String path = req.getRequestURI();
135+ int n = path.lastIndexOf(BastubConsts.VIEW_TRANS_EXT);
136+ String viewName = path.substring(0, n);
137+ return viewName;
138+ }
139+
140+
141+}
diff -r 6d19018d54b6 -r 81a5d2fd9d1d src/main/java/jp/nanah/bastub/controller/StaticController.java
--- a/src/main/java/jp/nanah/bastub/controller/StaticController.java Wed Oct 13 07:33:47 2021 +0900
+++ b/src/main/java/jp/nanah/bastub/controller/StaticController.java Sat Nov 27 20:42:15 2021 +0900
@@ -22,8 +22,8 @@
2222 import org.springframework.web.bind.annotation.RequestMapping;
2323 import org.springframework.web.bind.annotation.ResponseBody;
2424
25-import jp.nanah.bastub.service.PageDataService;
26-import jp.nanah.bastub.service.UsingInfo;
25+import jp.nanah.bastub.data.UsingInfo;
26+import jp.nanah.bastub.service.PathService;
2727
2828 @Controller
2929 public class StaticController {
@@ -48,7 +48,7 @@
4848 }
4949
5050 @Autowired
51- private PageDataService pgService;
51+ private PathService pgService;
5252
5353 @ResponseBody
5454 @RequestMapping(value = { "/**/*.jpg","/**/*.jpeg","/**/*.png","/**/*.gif","/**/*.pdf","/**/*.ico"})
@@ -100,8 +100,6 @@
100100 return mt;
101101 }
102102
103-
104-
105103 private HttpHeaders createHeaders(MediaType mt, byte[] by){
106104 HttpHeaders headers = new HttpHeaders();
107105 headers.setContentType(mt);
diff -r 6d19018d54b6 -r 81a5d2fd9d1d src/main/java/jp/nanah/bastub/data/AppInfo.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/java/jp/nanah/bastub/data/AppInfo.java Sat Nov 27 20:42:15 2021 +0900
@@ -0,0 +1,34 @@
1+package jp.nanah.bastub.data;
2+
3+import org.slf4j.Logger;
4+import org.slf4j.LoggerFactory;
5+import org.springframework.stereotype.Component;
6+
7+/**
8+ * 全体で共通する(一意の)サービスを提供する。
9+ */
10+@Component
11+public class AppInfo {
12+
13+ /** 動作ログ */
14+ private static final Logger logger = LoggerFactory.getLogger(AppInfo.class);
15+
16+ /**
17+ * アプリケーション識別値。ログを区別するため。
18+ */
19+ private static String APP_SNO = Long.toString(System.currentTimeMillis()/1000, 36).toUpperCase();
20+
21+ /**
22+ * リクエスト通番。起動するたびに0から始まる。
23+ */
24+ private static long reqNo = 0;
25+
26+ public long setCurrentThreadName(){
27+ synchronized (APP_SNO){
28+ Thread.currentThread().setName(APP_SNO + "_" + reqNo);
29+ reqNo++;
30+ return reqNo;
31+ }
32+ }
33+
34+}
diff -r 6d19018d54b6 -r 81a5d2fd9d1d src/main/java/jp/nanah/bastub/data/UsingInfo.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/java/jp/nanah/bastub/data/UsingInfo.java Sat Nov 27 20:42:15 2021 +0900
@@ -0,0 +1,307 @@
1+package jp.nanah.bastub.data;
2+
3+import java.text.SimpleDateFormat;
4+import java.util.ArrayList;
5+import java.util.Arrays;
6+import java.util.Date;
7+import java.util.HashMap;
8+import java.util.Iterator;
9+import java.util.LinkedHashMap;
10+import java.util.List;
11+import java.util.Map;
12+import java.util.Set;
13+
14+import javax.servlet.http.HttpServletRequest;
15+import javax.servlet.http.HttpSession;
16+
17+import org.apache.poi.ss.usermodel.Cell;
18+import org.apache.poi.ss.usermodel.Row;
19+import org.slf4j.Logger;
20+import org.slf4j.LoggerFactory;
21+
22+import jp.nanah.bastub.util.BastubUtils;
23+
24+/**
25+ * 適用データのうち、使用した列値を記憶して
26+ * 下位の階層にデータを受け渡すデータの絞り込みを行う。
27+ *
28+ */
29+public class UsingInfo {
30+
31+ protected static final Logger logger = LoggerFactory.getLogger(UsingInfo.class);
32+
33+ /**
34+ * データの参照
35+ */
36+ private List<Row> dataList;
37+
38+ /**
39+ * システム側で固定値で置き換える文字列のMap
40+ */
41+ private Map<String, String> replaceConst = new HashMap<String, String>();
42+
43+ private static SimpleDateFormat DF_DATE = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
44+
45+ /**
46+ * JSONの値を固定値に変換するためのMap。
47+ * @param pathList
48+ * @param req
49+ * @return
50+ */
51+ public static UsingInfo getInitInstance(HttpServletRequest req){
52+ String[] pathArray = req.getRequestURI().split("/");
53+ List<String> pathList = Arrays.asList(pathArray);
54+ return getInitInstance(pathList, req, null);
55+ }
56+
57+ /**
58+ * JSONの値を固定値に変換するためのMap。
59+ * @param pathList
60+ * @param req
61+ * @return
62+ */
63+ public static UsingInfo getInitInstance(List<String> pathList, HttpServletRequest req, Map body){
64+ UsingInfo ui = new UsingInfo();
65+
66+ Map<String, String> map = ui.replaceConst;// new HashMap<String, String>();
67+
68+ String ymdhms = DF_DATE.format(new Date());
69+ HttpSession session = req.getSession(true);
70+ map.put("${#SYSDATE#}", ymdhms);
71+ map.put("${#SYS_YMDHMS#}", ymdhms);
72+ map.put("${#SYS_YMD#}", ymdhms.substring(0, 10));
73+ map.put("${#SYS_HM#}", ymdhms.substring(11, 19));
74+ map.put("${#SYS_MSEC#}", String.valueOf(System.currentTimeMillis()));
75+ map.put("${#SYS_SEC#}", String.valueOf(System.currentTimeMillis()/1000));
76+ map.put("${#THREAD_ID#}", String.valueOf(Thread.currentThread().getId()));
77+ map.put("${#THREAD_NAME#}", Thread.currentThread().getName());
78+ map.put("${#SESSION_ID#}", session.getId());
79+
80+ //"${#PATHx#}→パス値に変換
81+ for (int i=0; i<pathList.size(); i++){
82+ map.put("${#PATH" + i + "#}", pathList.get(i));
83+ }
84+ String url = req.getRequestURL().toString();
85+ map.put("${#URL#}", url);
86+ map.put("${#URL_#}", req.getContextPath());
87+ map.put("${#DOMAIN#}", BastubUtils.getUrlDomain(url));
88+
89+ //リクエストを埋め込み文字にする
90+ ui.putReplaceMap("", req.getParameterMap(), map);
91+ ui.putReplaceMap("", body, map);
92+
93+
94+ for (String mapkey : map.keySet()){
95+ logger.info("全置き換え文字: [{}]=[{}]", mapkey, map.get(mapkey));
96+ }
97+
98+ return ui;
99+ }
100+
101+ public void putReplaceMap(String pkey, Map valMap, Map<String, String> replaceMap){
102+ if (valMap == null) {
103+ return;
104+ }
105+
106+ Set keys = valMap.keySet();
107+ for (Object k : keys){
108+ String key = ((pkey.length() == 0) ? "" : pkey + ".") + k.toString();
109+ Object val = valMap.get(k);
110+
111+ if (val == null){
112+ continue;
113+ }
114+
115+ if (val instanceof Map){
116+ putReplaceMap(key, (Map)val, replaceMap);
117+ continue;
118+ }
119+
120+ if (val instanceof List){
121+ List list = (List)val;
122+ for (int i=0; i<list.size(); i++){
123+ String repkey = getReplaceKey(key + i, "#","#");
124+ replaceMap.put(repkey, val.toString());
125+ }
126+ } else if (val instanceof String[]){
127+ String[] valary = (String[])val;
128+ if (valary.length == 1){
129+ String repkey = getReplaceKey(key, "#","#");
130+ replaceMap.put(repkey, valary[0]);
131+ } else {
132+ for (int i=0; i<valary.length; i++){
133+ String repkey = getReplaceKey(key + i, "#","#");
134+ replaceMap.put(repkey, valary[i]);
135+ }
136+ }
137+ } else {
138+ String repkey = getReplaceKey(key, "#","#");
139+ replaceMap.put(repkey, val.toString());
140+ }
141+ }
142+ }
143+
144+ private String getReplaceKey(String inkey, String prefix, String suffix){
145+ suffix = (suffix == null) ? prefix : suffix;
146+ return "${" + prefix + inkey + suffix + "}";
147+ }
148+
149+ /**
150+ * 使用済みのデータインデックス。0なら未使用、1~はdataListのインデックス値と一致。
151+ */
152+ private int[] usedRow;
153+
154+ private UsingInfo(){
155+ }
156+
157+ public UsingInfo(List<Row> dataList, UsingInfo ui){
158+ this.dataList = dataList;
159+
160+ //列数は先頭行。1行目に列名は必須
161+ int colMax = (dataList.size() > 0) ? dataList.get(0).getLastCellNum() : 0;
162+ usedRow = new int[colMax];
163+
164+ //固定情報があれば引き継ぐ
165+ if (ui != null){
166+ replaceConst = ui.getReplaceConst();
167+ }
168+ }
169+
170+ public Map<String, String> getReplaceConst(){
171+ return replaceConst;
172+ }
173+
174+
175+ /**
176+ * 置換文字ならデータに置き換えて返す。
177+ * 置換文字でなければもともとのJSON値を返す。
178+ * 置換文字なのに該当データがなければ空文字を返す。(これでよいはず)
179+ *
180+ * @param keyList ログ用にしか使わない。
181+ * @param key JSONのキー値
182+ * @param orgval JSONの現在値
183+ * @return 置換後の文字列。非null。
184+ */
185+ public String getDirectValue(List<String> keyList, String key, String orgval){
186+ if (orgval == null){
187+ return "";
188+ }
189+
190+ String dstval = orgval;
191+
192+ //直値で置き換え文字なら置き換え名が列データにあるか探す
193+ int colIndex = BastubUtils.getColumnNoForReplace(orgval, dataList, key);
194+
195+ if (colIndex >=0){
196+ if (dataList.size() > 1) {
197+ int dataRow = usedRow[colIndex] + 1;
198+ if (dataRow < dataList.size()){
199+ dstval = BastubUtils.getCellText(dataList.get(dataRow).getCell(colIndex));
200+ usedRow[colIndex]++;
201+ } else {
202+ dstval = "";
203+ logger.warn("[{}]: JSON内でキー {\"{}\" が重複登録されている可能性あり", keyList, key);
204+ }
205+ }
206+
207+ } else {
208+ String rep = replaceConst.get(orgval);
209+ if (rep != null){
210+ dstval = rep;
211+ }
212+
213+ //置換文字なのに置換列がないときはそのままにせず空文字にする
214+ if (colIndex == -1){
215+ //dstval = "";
216+ //↑設定が間違っている可能性が高いので空白にせず元の文字列をだすことにする
217+ }
218+ }
219+
220+ return dstval;
221+ }
222+
223+ /**
224+ * 下層に適用するデータの一覧を取得する。
225+ * 下層には、列値として参照したもの
226+ *
227+ * @return
228+ */
229+ public List<Row> getLoweredData(){
230+ //1行目は列名なので最低2行以上ないとダメ。1行だけならデータなし。
231+ if (dataList.size() < 2){
232+ //return null;
233+ return dataList;
234+ //↑nullを返すと配列やJSONオブジェクトの中の置き換え文字が消えないのでそのまま返すようにした。
235+ }
236+
237+ List<Row> loweredList = new ArrayList<Row>();
238+
239+ //1行目の使用データを、列番号→列値のマップ化。
240+ Map<Integer, List<String>> usedDataMap = getUsedDataMap();
241+
242+ //データ未使用なら全データが対象
243+ if (usedDataMap.size() == 0){
244+ return dataList;
245+ }
246+
247+ //各行から先頭データと同じデータを持っていれば、次の層にも適用する(1行目も含む)
248+ for (int r=0; r<dataList.size(); r++){
249+ Row rowVal = dataList.get(r);
250+
251+ if (r == 0){
252+ //列名の行は無条件でコピー
253+ } else {
254+ //データ行は、現階層で使用した値でなければ下層には送らない
255+ if (isUsedData(usedDataMap, rowVal) == false){
256+ continue;
257+ }
258+ }
259+
260+ loweredList.add(rowVal);
261+ }
262+
263+ return loweredList;
264+ }
265+
266+ /**
267+ * 使用済みデータのMapを返す。
268+ * Key=列番号(0~)、value=列値のリスト
269+ * @return
270+ */
271+ Map<Integer, List<String>> getUsedDataMap(){
272+ Map<Integer, List<String>> usedDataMap = new LinkedHashMap<Integer, List<String>>();
273+ for (int c=0; c<usedRow.length; c++){
274+ if (usedRow[c] > 0){
275+ List<String> usedList = new ArrayList<String>();
276+ for (int r=1; r<=usedRow[c]; r++){
277+ Cell cell = dataList.get(r).getCell(c);
278+ usedList.add( BastubUtils.getCellText(cell) );
279+ }
280+ usedDataMap.put(new Integer(c), usedList);
281+ }
282+ }
283+ return usedDataMap;
284+ }
285+
286+ public boolean isUsedData(Map<Integer, List<String>> usedMap, Row rowVal){
287+ Iterator<Integer> it = usedMap.keySet().iterator();
288+ while (it.hasNext()){
289+ Integer key = it.next();
290+ String v = BastubUtils.getCellText(rowVal.getCell(key.intValue()));
291+ if (usedMap.get(key).contains(v) == false){
292+ return false;
293+ }
294+ }
295+ return true;
296+ }
297+
298+ public String replaceSring(String s){
299+ for (Map.Entry<String, String> ent : replaceConst.entrySet()) {
300+ String key = ent.getKey();
301+ while (s.contains(key)){
302+ s = s.replace(key, ent.getValue());
303+ }
304+ }
305+ return s;
306+ }
307+}
diff -r 6d19018d54b6 -r 81a5d2fd9d1d src/main/java/jp/nanah/bastub/filter/BastubHttpRequest.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/java/jp/nanah/bastub/filter/BastubHttpRequest.java Sat Nov 27 20:42:15 2021 +0900
@@ -0,0 +1,39 @@
1+package jp.nanah.bastub.filter;
2+
3+import javax.servlet.http.HttpServletRequest;
4+import javax.servlet.http.HttpServletRequestWrapper;
5+
6+import jp.nanah.bastub.util.BastubUtils;
7+
8+public class BastubHttpRequest extends HttpServletRequestWrapper{
9+
10+ private String uri = null;
11+
12+ private StringBuffer url = null;
13+
14+ /**
15+ * Viewに遷移するためのHTTPリクエストクラス。
16+ * 固定ファイルがあるときは、
17+ * SpringFrameworkのControllerに、情報を
18+ * @param req
19+ */
20+ public BastubHttpRequest(HttpServletRequest req){
21+ super(req);
22+ }
23+
24+ @Override
25+ public String getRequestURI(){
26+ return (uri == null) ? super.getRequestURI() : uri;
27+ }
28+
29+ @Override
30+ public StringBuffer getRequestURL(){
31+ return (url == null) ? super.getRequestURL() : url;
32+ }
33+
34+ public void setUrl(String path){
35+ url = new StringBuffer(path);
36+ uri = BastubUtils.getUriPath(path);
37+ }
38+
39+}
diff -r 6d19018d54b6 -r 81a5d2fd9d1d src/main/java/jp/nanah/bastub/filter/ReplaceFilter.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/java/jp/nanah/bastub/filter/ReplaceFilter.java Sat Nov 27 20:42:15 2021 +0900
@@ -0,0 +1,167 @@
1+package jp.nanah.bastub.filter;
2+
3+import java.io.File;
4+import java.io.IOException;
5+import java.net.URL;
6+import java.util.ArrayList;
7+import java.util.Iterator;
8+import java.util.List;
9+import java.util.Map;
10+import java.util.TreeMap;
11+
12+import javax.servlet.Filter;
13+import javax.servlet.FilterChain;
14+import javax.servlet.FilterConfig;
15+import javax.servlet.ServletException;
16+import javax.servlet.ServletRequest;
17+import javax.servlet.ServletResponse;
18+import javax.servlet.http.HttpServletRequest;
19+
20+import org.apache.commons.io.FileUtils;
21+import org.apache.commons.lang3.StringUtils;
22+import org.slf4j.Logger;
23+import org.slf4j.LoggerFactory;
24+import org.springframework.stereotype.Component;
25+
26+import jp.nanah.bastub.BastubConsts;
27+import jp.nanah.bastub.service.PathService;
28+import jp.nanah.bastub.util.BastubUtils;
29+
30+@Component
31+public class ReplaceFilter implements Filter {
32+
33+ /** 動作ログ */
34+ private static final Logger logger = LoggerFactory.getLogger(BastubHttpRequest.class);
35+
36+ @Override
37+ public void init(FilterConfig filterConfig) throws ServletException {
38+ System.out.println("init!!");
39+ }
40+
41+ @Override
42+ public void doFilter(ServletRequest request, ServletResponse response,
43+ FilterChain chain) throws IOException, ServletException{
44+
45+ System.out.println("doFilter...");
46+
47+ BastubHttpRequest req = new BastubHttpRequest((HttpServletRequest)request);
48+ String orgurl = req.getRequestURL().toString();
49+ String url = orgurl;
50+
51+ //■差し替え#1
52+ //replace.propertiesに定義があればパスをそのまま差し替える
53+ List<String[]> pathList = getReplacePathList();
54+ String repurl = replacePath(pathList, orgurl);
55+ if (repurl != null) {
56+ logger.debug("replace1: [{}]-->[{}]", url, repurl);
57+ url = repurl;
58+ }
59+
60+ //■差し替え#2
61+ //リクエストのURI+".html"に該当する.htmlファイルがあるとき、
62+ //そのリクエストはJSON応答ではなくHTMLを返す。
63+ //今のところThymeleaf対応のみ対応とする。
64+
65+ String uri = BastubUtils.getUriPath(url);
66+ File file = new File(PathService.pageDir, uri + ".html");
67+ if (file.exists()){
68+
69+ // Viewに遷移するために拡張子を付加する。AnyViewControllerはこの拡張子のものを処理する
70+ url = url + BastubConsts.VIEW_TRANS_EXT;
71+ logger.debug("replace2: -->[{}]", url);
72+ }
73+
74+ //Viewファイル
75+ req.setUrl(url);
76+
77+ chain.doFilter(req, response);
78+ }
79+
80+// @Override
81+// public void destroy() {
82+// }
83+
84+
85+ /**
86+ * パスを置き換える。
87+ * 置き換えられなかった場合はnullを返す。
88+ * @return
89+ */
90+ public String replacePath(List<String[]> pathList, String src){
91+ logger.debug("置き換え定義数: [{}]", pathList.size());
92+ for (String[] path : pathList){
93+ logger.debug("src=[{}]: path=[{}][{}]", src, path[0],path[1]);
94+ int n = src.indexOf(path[0]);
95+ if (n >=0){
96+ return src.replace(path[0], path[1]);
97+ }
98+ }
99+ return null;
100+ }
101+
102+ /**
103+ * 置き換えファイル。
104+ */
105+ private File replaceFile = null;
106+
107+ /**
108+ * 初回または更新されたときだけ読み直すために更新時刻を保持。
109+ */
110+ private long lastUpdated = 0;
111+
112+ /**
113+ * 置き換えファイルの中身。
114+ * [0]=置き換え前、[1]=置き換え後
115+ */
116+ private List<String[]> replaceList;
117+
118+ /**
119+ * 置き換えパス情報を取得する。
120+ * @return
121+ */
122+ public List<String[]> getReplacePathList(){
123+
124+ if (replaceFile == null){
125+ URL replaceFileUrl = getClass().getResource("/replace.properties");
126+ replaceFile = new File(replaceFileUrl.getFile());
127+ }
128+ if (replaceList != null){
129+ if (lastUpdated == replaceFile.lastModified()){
130+ return replaceList;
131+ }
132+ }
133+
134+ try {
135+ List<String> lines = FileUtils.readLines(replaceFile); //UTF-8 only
136+ Map<String, String> sdMap = new TreeMap<String, String>();
137+ for (String s : lines){
138+ if (s.startsWith("#")){
139+ continue;
140+ }
141+ s = StringUtils.normalizeSpace(s);
142+ int n = s.indexOf(" ");
143+ if (n >= 0){
144+ String src = s.substring(0, n);
145+ String dst = s.substring(n).trim();
146+ sdMap.put(src, dst);
147+ }
148+ }
149+
150+
151+ //長い順に並べる
152+ replaceList = new ArrayList<String[]>();
153+ Iterator<String> it = sdMap.keySet().iterator();
154+ while (it.hasNext()){
155+ String srcPath = it.next();
156+ String dstPath = sdMap.get(srcPath);
157+ replaceList.add(0, new String[]{srcPath, dstPath});
158+ logger.info("[{}] src=[{}] dst=[{}]", replaceList.size(), srcPath, dstPath);
159+ }
160+ } catch (IOException e){
161+ logger.warn("read_error:{}", replaceFile.getAbsolutePath());
162+ }
163+
164+ return replaceList;
165+ }
166+
167+}
diff -r 6d19018d54b6 -r 81a5d2fd9d1d src/main/java/jp/nanah/bastub/service/JsonService.java
--- a/src/main/java/jp/nanah/bastub/service/JsonService.java Wed Oct 13 07:33:47 2021 +0900
+++ b/src/main/java/jp/nanah/bastub/service/JsonService.java Sat Nov 27 20:42:15 2021 +0900
@@ -4,19 +4,40 @@
44 import java.io.FileInputStream;
55 import java.io.IOException;
66 import java.util.ArrayList;
7+import java.util.Arrays;
8+import java.util.Collections;
79 import java.util.List;
10+import java.util.Map;
11+import java.util.Set;
12+import java.util.TreeMap;
13+
14+import javax.servlet.http.HttpServletRequest;
15+import javax.servlet.http.HttpServletResponse;
816
917 import org.apache.commons.io.FileUtils;
1018 import org.apache.commons.io.IOUtils;
19+import org.apache.commons.lang3.ArrayUtils;
20+import org.apache.commons.lang3.StringUtils;
21+import org.apache.commons.lang3.math.NumberUtils;
22+import org.apache.poi.ss.usermodel.Cell;
1123 import org.apache.poi.ss.usermodel.Row;
24+import org.apache.poi.ss.usermodel.Sheet;
25+import org.apache.poi.ss.usermodel.Workbook;
26+import org.apache.poi.ss.usermodel.WorkbookFactory;
1227 import org.json.JSONArray;
1328 import org.json.JSONObject;
1429 import org.slf4j.Logger;
1530 import org.slf4j.LoggerFactory;
31+import org.springframework.beans.factory.annotation.Autowired;
1632 import org.springframework.beans.factory.annotation.Value;
1733 import org.springframework.stereotype.Service;
34+import org.springframework.ui.ModelMap;
1835
36+import jp.nanah.bastub.BastubConsts;
37+import jp.nanah.bastub.data.FilterParam;
1938 import jp.nanah.bastub.data.JsonInfo;
39+import jp.nanah.bastub.data.KvData;
40+import jp.nanah.bastub.data.UsingInfo;
2041 import jp.nanah.bastub.util.BastubUtils;
2142
2243 @Service
@@ -24,11 +45,597 @@
2445
2546 protected static final Logger logger = LoggerFactory.getLogger(JsonService.class);
2647
48+ /**
49+ * エクセルシートによるフィルタリングがないことを示すリスト。
50+ * この値はオブジェクトの参照比較で使用する。
51+ */
52+ public static final List<Row> NO_FILTERING_DATA_NONE = new ArrayList<Row>();
53+
2754 public static final String JSON_DATA_NONE_TEXT = "{\"key\":\"auto created.\"}";
2855
2956 @Value("${auto_create:false}")
3057 protected boolean isAutoCreate;
3158
59+ @Autowired
60+ private PathService pathService;
61+
62+ /**
63+ * 【共通処理】ここが主処理。
64+ *
65+ * @param pathList
66+ * @param body nullの場合あり
67+ * @param model 未使用。将来削除するかも。
68+ * @param req HTTPリクエスト
69+ * @param res HTTPレスポンス
70+ * @return
71+ */
72+ public String getAny(List<String> pathList, Map body, ModelMap model, HttpServletRequest req, HttpServletResponse res){
73+ try {
74+ File pageDir = pathService.getPageDir();
75+
76+ if (pageDir.exists() == false){
77+ String errormsg = "Bastubの設定フォルダ[" + pageDir.getAbsolutePath() + "]が見つかりません。application.propertiesのpagedata.rootを見直してください。";
78+ logger.info(errormsg);
79+ return errormsg;
80+ }
81+
82+ UsingInfo ui = UsingInfo.getInitInstance(pathList, req, body);
83+
84+ //パスを生成
85+ String path = StringUtils.join(pathList, "/");
86+ File dataFile = BastubUtils.getPagedataPath(pageDir, path, req.getMethod(), ".xlsx,.xls");
87+ File jsonFile = BastubUtils.getPagedataPath(pageDir, path, req.getMethod(), ".json");
88+
89+ //データファイルを読み込む
90+ //-----------------------------------------------------------------------------------------------------
91+ //データファイルは、パス名+"_"+HTTPメソッド名のファイルがあればそれを優先する。ない場合はパス名のみ。
92+ //データファイルはファイルなし、あるいはファイル内のdataシートやfilterシートが無くてもOK。
93+ //==>データファイルがなければデータ埋め込みされないのでJSONは固定値で返る。
94+ //-----------------------------------------------------------------------------------------------------
95+ Sheet paramSheet = null;
96+ Sheet dataSheet = null;
97+ Workbook wb = readWorkbook(dataFile);
98+ if (wb == null) {
99+ logger.warn("Excelファイル無しのためJSONを固定で応答 (ExcelFile=[{}])", dataFile.getAbsolutePath());
100+ } else {
101+ paramSheet = wb.getSheet("filter");
102+ dataSheet = wb.getSheet("data");
103+ wb.close();
104+
105+ if (paramSheet == null) {
106+ logger.debug("定義ファイル[{}]に[filter]シートが見つかりません", dataFile.getAbsolutePath());
107+ }
108+ if (dataSheet == null) {
109+ logger.debug("定義ファイル[{}]に[data]シートが見つかりません", dataFile.getAbsolutePath());
110+ }
111+ }
112+ //データ範囲をトリミング(エクセルはデータがなくても編集範囲だったりするので)
113+ trimDataSheet(dataSheet);
114+
115+ //=================================
116+ // [1] リクエスト値をKeyValueリスト化
117+ //=================================
118+ List<KvData> requestData = getRequestData(req.getParameterMap(), body);
119+
120+ //=================================
121+ // [2] 絞り込み条件を取得
122+ //=================================
123+ List<FilterParam> allFilter = getFilterParamList(paramSheet);
124+ List<FilterParam> validFilter = null;
125+ if (allFilter != null){
126+ validFilter = getValidFilter(allFilter, requestData, pathList);
127+ }
128+
129+ //=================================
130+ // [3] エクセルデータから応答に返すレコードを絞り込む
131+ //=================================
132+ List<Row> resultTarget = pickupSheetData(dataSheet, validFilter);
133+
134+ //=================================
135+ // [4] 応答用の入れ物を読み込む
136+ //=================================
137+ JsonInfo jsonInfo = readJsonFile(jsonFile);
138+ if (jsonInfo.getJsonObject() == null) {
139+ //ファイルがない場合は自動で作られるのでここを通過することは殆どない
140+ res.setStatus(HttpServletResponse.SC_NOT_FOUND);
141+ String jsonPath = jsonFile.getAbsolutePath();
142+ logger.warn("## ◆ 応答JSON異常: 理由=[{}] パス=[{}]", jsonInfo.getErrorMessage(), jsonPath);
143+ //String errorJson = "{ \"no_file\": \"" + jsonPath.replaceAll("\\\\", "/") + "\"}";
144+ String errorJson = "no_file - " + jsonPath.replaceAll("\\\\", "/") + "]";
145+ return errorJson;
146+ }
147+
148+ //=================================
149+ // [5] 応答データを生成
150+ //=================================
151+ setDataToJsonObject(null, jsonInfo.getJsonObject(), resultTarget, ui);
152+
153+ //応答
154+ String jsonText = jsonInfo.getJsonObject().toString(4);
155+ if (jsonInfo.isTopArray()){
156+ //{"dummy":xxxx … } のカッコを除去する
157+ int n1 = jsonText.indexOf(":");
158+ int n2 = jsonText.lastIndexOf("}");
159+ jsonText = jsonText.substring(n1+1, n2);
160+ }
161+
162+ //Edgeから直接リクエストするとエラーになることへの対応
163+ res.addHeader("Access-Control-Allow-Origin", "*");
164+
165+ return jsonText;
166+
167+ } catch (Throwable th) {
168+ logger.info("処理異常: {}", th);
169+ return th.toString();
170+ }
171+
172+ }
173+
174+ //-------------------
175+ // Excelデータの処理
176+ //-------------------
177+
178+ /**
179+ * エクセルファイルごと保存。
180+ */
181+ private Map<String, Object[]> workbookCache = new TreeMap<String, Object[]>();
182+
183+ /**
184+ * Workbookを読み込む。
185+ * キャッシュがあればそれを返す。
186+ *
187+ * @param file
188+ * @return
189+ * @throws IOException
190+ */
191+ protected synchronized Workbook readWorkbook(File file) throws IOException {
192+ if (file.exists() == false) {
193+ return null;
194+ }
195+
196+ String key = file.getAbsolutePath();
197+ Object[] cache = workbookCache.get(key);
198+ if (cache != null){
199+ Long filetime = (Long)cache[0];
200+ if (filetime.longValue() == file.lastModified()){
201+ return (Workbook)cache[1];
202+ }
203+ }
204+
205+ Workbook wb = WorkbookFactory.create(file, null, true);//new HSSFWorkbook(poiFileSystem);
206+ workbookCache.put(key, new Object[]{file.lastModified(), wb});
207+ return wb;
208+ }
209+
210+ protected void trimDataSheet(Sheet sheet){
211+ if (sheet == null || sheet.getLastRowNum() == 0){
212+ return;
213+ }
214+
215+ //1行目(ヘッダ行)から、列数を確認
216+ Row topRow = sheet.getRow(0);
217+ int lastColNum = topRow.getLastCellNum() - 1;
218+ while (lastColNum >= 0){
219+ Cell cell = topRow.getCell(lastColNum);
220+ String s = BastubUtils.getCellText(cell);
221+ if (StringUtils.isNotBlank(s)){
222+ break;
223+ }
224+ lastColNum--;
225+ }
226+
227+ int rowNum = sheet.getLastRowNum();
228+ while (rowNum >= 0) {
229+ Row row = sheet.getRow(rowNum);
230+ if (row != null){
231+ if (BastubUtils.isBlankRow(row)){
232+ sheet.removeRow(row);
233+ } else {
234+ for (int i=row.getLastCellNum()-1; i>lastColNum; i--){
235+ Cell cell = row.getCell(i);
236+ if (cell != null){
237+ row.removeCell(cell);
238+ }
239+ }
240+ }
241+ }
242+ rowNum--;
243+ }
244+ }
245+
246+ /**
247+ * [1]
248+ * @param paramMap
249+ * @param body
250+ * @return
251+ */
252+ public List<KvData> getRequestData(Map<String, String[]> paramMap, Map body){
253+ List<KvData> paramList = new ArrayList<KvData>();
254+
255+ //URLの後ろに渡されるリクエストパラメータをKey/Value形式にする
256+ if (paramMap != null) {
257+ int cnt = 0;
258+ for (Map.Entry<String, String[]> ent : paramMap.entrySet()) {
259+ //BODYで渡していてもParamMapに入っているので除外する。
260+ if (BastubUtils.isJsonEntry(ent)){
261+ continue;
262+ }
263+ List<String> key = Arrays.asList(new String[] {ent.getKey()});
264+ for (String val : ent.getValue()) {
265+ KvData kv = appendKvParam(new KvData(key, val), paramList);
266+ cnt++;
267+ }
268+ }
269+ }
270+
271+ //BodyリクエストをKey/Value形式にする
272+ if (body != null) {
273+ int startSize = paramList.size();
274+ appendEntryToParamList(body.entrySet(), null, paramList);
275+ for (int i=startSize; i<paramList.size(); i++){
276+ KvData kv = paramList.get(i);
277+ }
278+ }
279+
280+ return paramList;
281+ }
282+
283+ //■TODO 見直し候補
284+ //ここの考え方は見直したほうがよい。
285+ //リクエスト(QueryStringやBODY)の取得時点ではキー値は重複していてよいし、
286+ //key1/key2で組み合わせのときに一致する考え方でないとうまくいかなくなる。
287+ //クラス化してあるべきデータ保持形式にすべき。
288+
289+ /**
290+ * キーが一意になるようにKvDataを追加する。
291+ * リクエストやJSONで同一キーがある場合、どれかが一致するような判断となるようにする。
292+ *
293+ * @param src
294+ * @param dstList
295+ */
296+ private KvData appendKvParam(KvData src, List<KvData> dstList){
297+ KvData nowval = null;
298+ for (int i=0; i<dstList.size(); i++){
299+ if (BastubUtils.equals(dstList.get(i).getKey(), src.getKey())){
300+ nowval = dstList.get(i);
301+ break;
302+ }
303+ }
304+
305+ if (nowval == null){
306+ dstList.add(src);
307+ nowval = src;
308+
309+ } else {
310+ nowval.addValue(src.getValueAsOne());
311+ }
312+ return nowval;
313+ }
314+
315+ private void appendEntryToParamList(Set<Map.Entry<String, Object>> set, List<String> parentKeys, List<KvData> paramList){
316+ int count = 0;
317+ for (Map.Entry<String, Object> ent : set) {
318+ String key = ent.getKey();
319+ Object val = (ent.getValue() == null) ? "" : ent.getValue();
320+
321+ List<String> thisKeys = new ArrayList<String>();
322+ if (parentKeys != null){
323+ thisKeys.addAll(parentKeys);
324+ }
325+ thisKeys.add(key);
326+
327+ if (val instanceof Map){
328+
329+ Map<String, Object> vmap = (Map<String, Object>)val;
330+ appendEntryToParamList(vmap.entrySet(), thisKeys, paramList);
331+
332+ } else if (val instanceof List){
333+
334+ List<Object> vlist = (List<Object>)val;
335+ appendListToParamList(vlist, thisKeys, paramList);
336+
337+ } else {
338+ appendToParamList(val, thisKeys, paramList);
339+ }
340+ count++;
341+ }
342+ }
343+
344+ private void appendListToParamList(List<Object> list, List<String> parentKeys, List<KvData> paramList){
345+ for (Object o : list){
346+ if (o instanceof Map){
347+ Map<String, Object> map = (Map<String, Object>)o;
348+ appendEntryToParamList(map.entrySet(), parentKeys, paramList);
349+ } else if (o instanceof List){
350+ List<Object> vlist = (List<Object>)o;
351+ appendListToParamList(vlist, parentKeys, paramList);
352+ } else {
353+ appendToParamList(o, parentKeys, paramList);
354+ }
355+ }
356+ }
357+
358+ private void appendToParamList(Object obj, List<String> parentKeys, List<KvData> paramList){
359+ String valstr = obj.toString();
360+ KvData kv = new KvData(parentKeys, valstr);
361+ appendKvParam(kv, paramList);
362+ }
363+
364+ /**
365+ * [2]
366+ * エクセルデータをフィルタリングするときの定義パラメータを取得する。
367+ * ここでは置き換え文字や接尾辞を加工できないので設定値をそのまま取り出す(置き換えない)
368+ *
369+ * @param sheet
370+ * @return Map<Cellの列名, フィルタリングデータのリスト>
371+ */
372+ private List<FilterParam> getFilterParamList(Sheet sheet){
373+ List<FilterParam> filterList = null;
374+ if (sheet == null) {
375+ return filterList;
376+ }
377+
378+ for (int i=0; i<=sheet.getLastRowNum(); i++) {
379+ Row row = sheet.getRow(i);
380+ List<String> cellList = BastubUtils.getRowValueList(row, false);
381+
382+ //String prevName = "";
383+ if (cellList.size() >= 3) {
384+ // 1行でもフィルタリングデータがあるときだけフィルタリングを適用する
385+ if (filterList == null){
386+ filterList =new ArrayList<FilterParam>();
387+ }
388+ String cellName = cellList.get(0);
389+ String compType = cellList.get(1);
390+ List<String> paramKey = cellList.subList(2, cellList.size());
391+ FilterParam fp = new FilterParam(cellName, compType, paramKey);
392+
393+ filterList.add(fp);
394+ }
395+ }
396+ return filterList;
397+ }
398+
399+ /**
400+ * [2]-2
401+ * フィルタリング時に判定する、データシートの列値と比較する判定値を設定する。
402+ * フィルタリングで使用するキー値がrequestDataにあるときだけ、requestDataの値をフィルタリングに採用する。
403+ * 判定値がないときはnullのまま。
404+ *
405+ * @param requestData
406+ * @param filterParamList
407+ */
408+ public List<FilterParam> getValidFilter(List<FilterParam> filterParamList, List<KvData> requestData, List<String> pathList) {
409+ List<FilterParam> validList = new ArrayList<FilterParam>();
410+ for (FilterParam fp : filterParamList) {
411+ //パスインデックスのときは、URLのパス値に置き換える
412+ if (fp.getRequestKeys().size() > 0) {
413+ String key0 = fp.getRequestKeys().get(0);
414+ if (key0.startsWith(BastubConsts.PATH_REPLACE_HEAD)) {
415+ int pathNumber = NumberUtils.toInt(key0.substring(1));
416+ if (pathNumber > 0 && pathNumber <= pathList.size()) {
417+ fp.setOneValue(pathList.get(pathNumber - 1));
418+ validList.add(fp);
419+ continue;
420+ }
421+ }
422+ }
423+
424+ //要求キーのときは要求パラメータから値を探す
425+ List<String> reqKey = fp.getRequestKeys(); //こっちは定義
426+
427+ //実際のリクエストのキー値と比較
428+ boolean isFound = false;
429+ String delimText = getDelimText(fp.getCompareType());
430+ for (KvData kv : requestData) {
431+ if (BastubUtils.equalsTail(kv.getKey(), reqKey)) {
432+ List<String> fpValues = toFilterValue(delimText, kv.getValues());
433+ fp.setValues(fpValues);
434+ validList.add(fp);
435+ isFound = true;
436+ //break;
437+ }
438+ }
439+ if (isFound == false){
440+ logger.warn("HTTPリクエスト内に、項目値[{}]が見つかりません。", reqKey);
441+ }
442+ if (delimText != null){
443+ fp.setCompareType("=");
444+ }
445+ }
446+ return validList;
447+ }
448+
449+ private String getDelimText(String compareType){
450+ int n1 = compareType.indexOf("[");
451+ int n2 = compareType.indexOf("]");
452+ if (n1 <0 || n2 < 0){
453+ return null;
454+ }
455+ String delim = compareType.substring(n1+1, n2);
456+ return delim;
457+ }
458+
459+ // 記号が=[x]のとき、値を分割する
460+ private List<String> toFilterValue(String delimText, List<String> org){
461+ if (delimText == null){
462+ return org;
463+ }
464+
465+ List<String> dst = new ArrayList<String>();
466+ for (String s: org){
467+ dst.addAll(Arrays.asList((String[]) s.split(delimText, 0)));
468+ }
469+ return dst;
470+ }
471+
472+ /**
473+ * [3]
474+ */
475+ public List<Row> pickupSheetData(Sheet sheet, List<FilterParam> validFilter) {
476+ List<Row> noDatasheet = new ArrayList<Row>();
477+ if (sheet == null) {
478+ return NO_FILTERING_DATA_NONE;
479+ }
480+
481+ //まずは全データを対象にする
482+ List<Row> sheetData = new ArrayList<Row>();
483+ for (int i=sheet.getFirstRowNum(); i<=sheet.getLastRowNum(); i++) {
484+ Row row = sheet.getRow(i);
485+ if (row != null){
486+ sheetData.add(sheet.getRow(i));
487+ }
488+ }
489+
490+ // フィルターがないときは全データ対象
491+ if (validFilter == null || validFilter.isEmpty()){
492+ return sheetData;
493+ }
494+
495+ //列名
496+ String[] columnNames = getColumnNames(sheet);
497+
498+ //フィルターがあるときは適合しないものを除去する
499+ for (FilterParam fp : validFilter) {
500+ int columnIndex = ArrayUtils.indexOf(columnNames, fp.getColumnName());
501+ if (columnIndex < 0) {
502+ logger.warn("***** dataシートに、列名[{}]がありません。", fp.getColumnName());
503+ continue;
504+ }
505+
506+ //↓先頭行は列名なので除外する
507+ for (int i=1; i<sheetData.size(); i++) {
508+ Row row = sheetData.get(i);
509+ if (row == null) {
510+ continue;
511+ }
512+
513+ Cell cell = row.getCell(columnIndex);
514+ String v = BastubUtils.getCellText(cell);
515+ if (v == null){
516+ continue;
517+ }
518+
519+ boolean isMatch = false;
520+ String ct = fp.getCompareType();
521+ for (String fv : fp.getValues()){
522+
523+ int n;
524+ if (StringUtils.isEmpty(fv) && ct.equals("==") == false){
525+ //条件が未指定のとき、厳密な等価判定以外は条件なしとして扱う
526+ n = 0;
527+ } else {
528+ if (StringUtils.isNumeric(v) && StringUtils.isNumeric(fv)){
529+ logger.info("数値変換[{}]: [{}]<-->[{}]", columnIndex, v, fv);
530+ Integer v1 = Integer.parseInt(v);
531+ Integer v2 = Integer.parseInt(fv);
532+ n = v1.compareTo(v2);
533+ } else {
534+ n = v.compareTo(fv);
535+ }
536+ }
537+
538+ if (ct.equals("=") || ct.equals("==")) {
539+ isMatch = (n == 0);
540+ } else if (ct.equals("<")) {
541+ isMatch = n < 0;
542+ } else if (ct.equals("<=")) {
543+ isMatch = n <= 0;
544+ } else if (ct.equals(">")) {
545+ isMatch = n > 0;
546+ } else if (ct.equals(">=")) {
547+ isMatch = n >= 0;
548+ } else if (ct.equals("!=") || ct.equals("<>")) {
549+ isMatch = n != 0;
550+ }
551+ if (isMatch){
552+ break;
553+ }
554+ }
555+
556+ if (isMatch == false) {
557+ sheetData.set(i, null);
558+ }
559+ }
560+ }
561+
562+ sheetData.removeAll(Collections.singleton(null));
563+
564+ //debug
565+ for (Row row : sheetData) {
566+ StringBuilder sb = new StringBuilder();
567+ for (int i=row.getFirstCellNum(); i<row.getLastCellNum(); i++) {
568+ if (sb.length() > 0) {
569+ sb.append(",");
570+ }
571+ sb.append(BastubUtils.getCellText(row.getCell(i)));
572+ }
573+ }
574+
575+ return sheetData;
576+ }
577+
578+ private String[] getColumnNames(Sheet sheet) {
579+ Row row = sheet.getRow(sheet.getFirstRowNum());
580+ String[] columns = new String[row.getLastCellNum()];
581+ for (int i=row.getFirstCellNum(); i<row.getLastCellNum(); i++) {
582+ columns[i] = BastubUtils.getCellText(row.getCell(i));
583+ }
584+ return columns;
585+ }
586+
587+
588+
589+
590+
591+
592+
593+
594+
595+
596+
597+
598+
599+
600+
601+
602+
603+
604+
605+
606+
607+
608+
609+
610+
611+
612+
613+
614+
615+
616+
617+
618+
619+
620+
621+
622+
623+
624+
625+
626+
627+
628+
629+
630+
631+
632+
633+
634+
635+
636+
637+
638+
32639 /**
33640 * [4]
34641 * @param file
@@ -81,6 +688,8 @@
81688 try {
82689 JSONObject jsonObject = new JSONObject( reptext );
83690 jsonInfo.setJsonObject(jsonObject);
691+ logger.debug("reptext ==[{}]", reptext);
692+ logger.debug("逆変換 ==[{}]", jsonInfo.getJsonObject().toString(2));
84693 } catch (Throwable th){
85694 jsonInfo.setErrorMessage("JSON解析に失敗しました。書式が正しくない可能性があります。");
86695 }
@@ -241,19 +850,24 @@
241850 //JSONObjectのとき
242851 String baseText = jsonAry.getJSONObject(i).toString();
243852 List<String> extendedList = new ArrayList<String>();
244- for (int r=1; r<dataList.size(); r++){
245-
246- List<Row> lowerDataList = new ArrayList<Row>();
247- lowerDataList.add(dataList.get(0));
248- lowerDataList.add(dataList.get(r));
853+ if (dataList != NO_FILTERING_DATA_NONE){
854+ for (int r=1; r<dataList.size(); r++){
855+ List<Row> lowerDataList = new ArrayList<Row>();
856+ lowerDataList.add(dataList.get(0));
857+ lowerDataList.add(dataList.get(r));
858+ JSONObject extendJson = new JSONObject(baseText);
859+ setDataToJsonObject(keyList, extendJson, lowerDataList, ui);
860+ String extendText = extendJson.toString();
861+ if (extendedList.contains(extendText) == false){
862+ extendedList.add(extendText);
863+ extendAry.put(extendAry.length(), extendJson);
864+ }
865+ }
866+ } else {
867+ // フィルターデータがそもそも未指定のときは通過させる
249868 JSONObject extendJson = new JSONObject(baseText);
250- setDataToJsonObject(keyList, extendJson, lowerDataList, ui);
251- String extendText = extendJson.toString();
252-
253- if (extendedList.contains(extendText) == false){
254- extendedList.add(extendText);
255- extendAry.put(extendAry.length(), extendJson);
256- }
869+ setDataToJsonObject(keyList, extendJson, dataList, ui);
870+ extendAry.put(extendAry.length(), extendJson);
257871 }
258872 //配列の中を直接書き換えるからこれでOK
259873
diff -r 6d19018d54b6 -r 81a5d2fd9d1d src/main/java/jp/nanah/bastub/service/PageDataService.java
--- a/src/main/java/jp/nanah/bastub/service/PageDataService.java Wed Oct 13 07:33:47 2021 +0900
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,43 +0,0 @@
1-package jp.nanah.bastub.service;
2-
3-import java.io.File;
4-import java.io.IOException;
5-
6-import javax.servlet.http.HttpServletRequest;
7-
8-import org.slf4j.Logger;
9-import org.slf4j.LoggerFactory;
10-import org.springframework.beans.factory.annotation.Value;
11-import org.springframework.stereotype.Service;
12-
13-import jp.nanah.bastub.controller.AnyRestController;
14-import nanah.oslib.ResUtils;
15-
16-@Service
17-public class PageDataService {
18-
19- private static final Logger logger = LoggerFactory.getLogger(AnyRestController.class);
20-
21- @Value("${pagedata.root:pagedata}")
22- private String pagedataPath;
23-
24- public File getPageDir(){
25- File pageDir = new File(pagedataPath);
26- if (pageDir.isAbsolute() == false){
27- File appRoot = ResUtils.getRootDirAs(this.getClass());
28- pageDir = new File(appRoot, pagedataPath);
29- }
30- try {
31- pageDir = pageDir.getCanonicalFile();
32- } catch (IOException e){
33- pageDir = pageDir.getAbsoluteFile();
34- }
35- return pageDir;
36- }
37-
38- public File getPageDataFile(File dir, HttpServletRequest req){
39- File file = new File(dir, req.getRequestURI());
40- logger.debug("Path=[{}]", file.getAbsolutePath());
41- return file;
42- }
43-}
diff -r 6d19018d54b6 -r 81a5d2fd9d1d src/main/java/jp/nanah/bastub/service/PathService.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/java/jp/nanah/bastub/service/PathService.java Sat Nov 27 20:42:15 2021 +0900
@@ -0,0 +1,77 @@
1+package jp.nanah.bastub.service;
2+
3+import java.io.File;
4+import java.io.IOException;
5+import java.util.Arrays;
6+import java.util.List;
7+
8+import javax.annotation.PostConstruct;
9+import javax.servlet.http.HttpServletRequest;
10+
11+import org.slf4j.Logger;
12+import org.slf4j.LoggerFactory;
13+import org.springframework.beans.factory.annotation.Value;
14+import org.springframework.stereotype.Service;
15+
16+import jp.nanah.bastub.controller.AnyRestController;
17+import nanah.oslib.ResUtils;
18+
19+@Service
20+public class PathService {
21+
22+ private static final Logger logger = LoggerFactory.getLogger(AnyRestController.class);
23+
24+ @Value("${pagedata.root:pagedata}")
25+ private String pagedataPath;
26+
27+ public static File pageDir;
28+
29+ @PostConstruct
30+ public void setupDir() throws Exception {
31+ pageDir = getPageDir();
32+ }
33+
34+ public File getPageDir(){
35+ if (pageDir != null){
36+ return pageDir;
37+ }
38+ File dir = new File(pagedataPath);
39+ if (dir.isAbsolute() == false){
40+ File appRoot = ResUtils.getRootDirAs(this.getClass());
41+ dir = new File(appRoot, pagedataPath);
42+ }
43+ try {
44+ pageDir = dir.getCanonicalFile();
45+ } catch (IOException e){
46+ pageDir = dir.getAbsoluteFile();
47+ }
48+ return pageDir;
49+ }
50+
51+ public File getPageDataFile(File dir, HttpServletRequest req){
52+ File file = new File(dir, req.getRequestURI());
53+ logger.debug("Path=[{}]", file.getAbsolutePath());
54+ return file;
55+ }
56+
57+ @Value("${path_last_slash_valid:1}")
58+ private int pathLastSlashValid;
59+
60+ public List<String> getPathList(HttpServletRequest req){
61+ String reqPath = req.getRequestURI();
62+ String[] pathArray = reqPath.substring(1).split("/");
63+ List<String> pathList = Arrays.asList(pathArray); //BastubUtils.toValidList(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10);
64+
65+ if (pathLastSlashValid == 1){
66+ //URLがスラッシュ終わりなら、ファイル名が""の要求が来たものとする
67+ logger.info("URL={}, URI={}", req.getRequestURL().toString(), reqPath );
68+ if (reqPath.endsWith("/")){
69+ pathList.add("");
70+ }
71+ }
72+
73+ return pathList;
74+ }
75+
76+
77+}
diff -r 6d19018d54b6 -r 81a5d2fd9d1d src/main/java/jp/nanah/bastub/service/UsingInfo.java
--- a/src/main/java/jp/nanah/bastub/service/UsingInfo.java Wed Oct 13 07:33:47 2021 +0900
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,251 +0,0 @@
1-package jp.nanah.bastub.service;
2-
3-import java.text.SimpleDateFormat;
4-import java.util.ArrayList;
5-import java.util.Arrays;
6-import java.util.Date;
7-import java.util.HashMap;
8-import java.util.Iterator;
9-import java.util.LinkedHashMap;
10-import java.util.List;
11-import java.util.Map;
12-
13-import javax.servlet.http.HttpServletRequest;
14-import javax.servlet.http.HttpSession;
15-
16-import org.apache.poi.ss.usermodel.Cell;
17-import org.apache.poi.ss.usermodel.Row;
18-import org.slf4j.Logger;
19-import org.slf4j.LoggerFactory;
20-
21-import jp.nanah.bastub.util.BastubUtils;
22-
23-/**
24- * 適用データのうち、使用した列値を記憶して
25- * 下位の階層にデータを受け渡すデータの絞り込みを行う。
26- *
27- */
28-public class UsingInfo {
29-
30- protected static final Logger logger = LoggerFactory.getLogger(UsingInfo.class);
31-
32- /**
33- * データの参照
34- */
35- private List<Row> dataList;
36-
37- /**
38- * システム側で固定値で置き換える文字列のMap
39- */
40- private Map<String, String> replaceConst = new HashMap<String, String>();
41-
42- private static SimpleDateFormat DF_DATE = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
43-
44- /**
45- * JSONの値を固定値に変換するためのMap。
46- * @param pathList
47- * @param req
48- * @return
49- */
50- public static UsingInfo getInitInstance(HttpServletRequest req){
51- String[] pathArray = req.getRequestURI().split("/");
52- List<String> pathList = Arrays.asList(pathArray);
53- return getInitInstance(pathList, req);
54- }
55-
56-
57- /**
58- * JSONの値を固定値に変換するためのMap。
59- * @param pathList
60- * @param req
61- * @return
62- */
63- public static UsingInfo getInitInstance(List<String> pathList, HttpServletRequest req){
64- UsingInfo ui = new UsingInfo();
65-
66- Map<String, String> map = ui.replaceConst;// new HashMap<String, String>();
67-
68- String ymdhms = DF_DATE.format(new Date());
69- HttpSession session = req.getSession(true);
70- map.put("${#SYSDATE#}", ymdhms);
71- map.put("${#SYS_YMDHMS#}", ymdhms);
72- map.put("${#SYS_YMD#}", ymdhms.substring(0, 10));
73- map.put("${#SYS_HM#}", ymdhms.substring(11, 19));
74- map.put("${#SYS_MSEC#}", String.valueOf(System.currentTimeMillis()));
75- map.put("${#SYS_SEC#}", String.valueOf(System.currentTimeMillis()/1000));
76- map.put("${#THREAD_ID#}", String.valueOf(Thread.currentThread().getId()));
77- map.put("${#THREAD_NAME#}", Thread.currentThread().getName());
78- map.put("${#SESSION_ID#}", req.getSession(true).getId());
79-
80- //"${#PATHx#}→パス値に変換
81- for (int i=0; i<pathList.size(); i++){
82- map.put("${#PATH" + i + "#}", pathList.get(i));
83- }
84- String url = req.getRequestURL().toString();
85- map.put("${#URL#}", url);
86- map.put("${#URL_#}", req.getContextPath());
87- map.put("${#DOMAIN#}", BastubUtils.getUrlDomain(url));
88-
89-
90- return ui;
91- }
92-
93- /**
94- * 使用済みのデータインデックス。0なら未使用、1~はdataListのインデックス値と一致。
95- */
96- private int[] usedRow;
97-
98- private UsingInfo(){
99- }
100-
101- public UsingInfo(List<Row> dataList, UsingInfo ui){
102- this.dataList = dataList;
103-
104- //列数は先頭行。1行目に列名は必須
105- int colMax = (dataList.size() > 0) ? dataList.get(0).getLastCellNum() : 0;
106- usedRow = new int[colMax];
107-
108- //固定情報があれば引き継ぐ
109- if (ui != null){
110- replaceConst = ui.getReplaceConst();
111- }
112- }
113-
114- public Map<String, String> getReplaceConst(){
115- return replaceConst;
116- }
117-
118-
119- /**
120- * 置換文字ならデータに置き換えて返す。
121- * 置換文字でなければもともとのJSON値を返す。
122- * 置換文字なのに該当データがなければ空文字を返す。(これでよいはず)
123- *
124- * @param keyList ログ用にしか使わない。
125- * @param key JSONのキー値
126- * @param orgval JSONの現在値
127- * @return 置換後の文字列。非null。
128- */
129- public String getDirectValue(List<String> keyList, String key, String orgval){
130- if (orgval == null){
131- return "";
132- }
133-
134- String dstval = orgval;
135-
136- //直値で置き換え文字なら置き換え名が列データにあるか探す
137- int colIndex = BastubUtils.getColumnNoForReplace(orgval, dataList, key);
138-
139- if (colIndex >=0){
140- if (dataList.size() > 1) {
141- int dataRow = usedRow[colIndex] + 1;
142- if (dataRow < dataList.size()){
143- dstval = BastubUtils.getCellText(dataList.get(dataRow).getCell(colIndex));
144- usedRow[colIndex]++;
145- } else {
146- dstval = "";
147- logger.warn("[{}]: JSON内でキー {\"{}\" が重複登録されている可能性あり", keyList, key);
148- }
149- }
150-
151- } else {
152- String rep = replaceConst.get(orgval);
153- if (rep != null){
154- dstval = rep;
155- }
156-
157- //置換文字なのに置換列がないときはそのままにせず空文字にする
158- if (colIndex == -1){
159- //dstval = "";
160- //↑設定が間違っている可能性が高いので空白にせず元の文字列をだすことにする
161- }
162- }
163-
164- return dstval;
165- }
166-
167- /**
168- * 下層に適用するデータの一覧を取得する。
169- * 下層には、列値として参照したもの
170- *
171- * @return
172- */
173- public List<Row> getLoweredData(){
174- //1行目は列名なので最低2行以上ないとダメ。1行だけならデータなし。
175- if (dataList.size() < 2){
176- //return null;
177- return dataList;
178- //↑nullを返すと配列やJSONオブジェクトの中の置き換え文字が消えないのでそのまま返すようにした。
179- }
180-
181- List<Row> loweredList = new ArrayList<Row>();
182-
183- //1行目の使用データを、列番号→列値のマップ化。
184- Map<Integer, List<String>> usedDataMap = getUsedDataMap();
185-
186- //データ未使用なら全データが対象
187- if (usedDataMap.size() == 0){
188- return dataList;
189- }
190-
191- //各行から先頭データと同じデータを持っていれば、次の層にも適用する(1行目も含む)
192- for (int r=0; r<dataList.size(); r++){
193- Row rowVal = dataList.get(r);
194-
195- if (r == 0){
196- //列名の行は無条件でコピー
197- } else {
198- //データ行は、現階層で使用した値でなければ下層には送らない
199- if (isUsedData(usedDataMap, rowVal) == false){
200- continue;
201- }
202- }
203-
204- loweredList.add(rowVal);
205- }
206-
207- return loweredList;
208- }
209-
210- /**
211- * 使用済みデータのMapを返す。
212- * Key=列番号(0~)、value=列値のリスト
213- * @return
214- */
215- Map<Integer, List<String>> getUsedDataMap(){
216- Map<Integer, List<String>> usedDataMap = new LinkedHashMap<Integer, List<String>>();
217- for (int c=0; c<usedRow.length; c++){
218- if (usedRow[c] > 0){
219- List<String> usedList = new ArrayList<String>();
220- for (int r=1; r<=usedRow[c]; r++){
221- Cell cell = dataList.get(r).getCell(c);
222- usedList.add( BastubUtils.getCellText(cell) );
223- }
224- usedDataMap.put(new Integer(c), usedList);
225- }
226- }
227- return usedDataMap;
228- }
229-
230- public boolean isUsedData(Map<Integer, List<String>> usedMap, Row rowVal){
231- Iterator<Integer> it = usedMap.keySet().iterator();
232- while (it.hasNext()){
233- Integer key = it.next();
234- String v = BastubUtils.getCellText(rowVal.getCell(key.intValue()));
235- if (usedMap.get(key).contains(v) == false){
236- return false;
237- }
238- }
239- return true;
240- }
241-
242- public String replaceSring(String s){
243- for (Map.Entry<String, String> ent : replaceConst.entrySet()) {
244- String key = ent.getKey();
245- while (s.contains(key)){
246- s = s.replace(key, ent.getValue());
247- }
248- }
249- return s;
250- }
251-}
diff -r 6d19018d54b6 -r 81a5d2fd9d1d src/main/java/jp/nanah/bastub/util/BastubUtils.java
--- a/src/main/java/jp/nanah/bastub/util/BastubUtils.java Wed Oct 13 07:33:47 2021 +0900
+++ b/src/main/java/jp/nanah/bastub/util/BastubUtils.java Sat Nov 27 20:42:15 2021 +0900
@@ -113,6 +113,12 @@
113113 }
114114 }
115115
116+ public static String removeExtText(String src, String ext){
117+ ext = (ext == null) ? "." : ext;
118+ int n = src.lastIndexOf(ext);
119+ return src.substring(0, n);
120+ }
121+
116122 /**
117123 * URLのドメインまでを返す。
118124 * 例えば、http://www.foo.co.jp/aa/bb/ccならば、"http://www.foo.co.jp"を返す。
@@ -130,6 +136,26 @@
130136 return url;
131137 }
132138
139+ /**
140+ * http://で始まるURLから、URI部分を抽出して返す。
141+ * @param httpPath
142+ */
143+ public static String getUriPath(String httpPath){
144+ if (httpPath == null){
145+ return null;
146+ }
147+
148+ int n1 = httpPath.indexOf("://");
149+ if (n1 >= 0){
150+ int n2 = httpPath.indexOf("/", n1 + 3);
151+ if (n2 >= 0){
152+ logger.debug("");
153+ return httpPath.substring(n2);
154+ }
155+ }
156+ return "";
157+ }
158+
133159 //===============================
134160 // POI関連
135161 //===============================
diff -r 6d19018d54b6 -r 81a5d2fd9d1d src/main/resources/application.properties
--- a/src/main/resources/application.properties Wed Oct 13 07:33:47 2021 +0900
+++ b/src/main/resources/application.properties Sat Nov 27 20:42:15 2021 +0900
@@ -34,3 +34,21 @@
3434 # true\u306b\u3059\u308b\u3068\u3001.json\u30d5\u30a1\u30a4\u30eb\u304c\u306a\u3044\u3068\u304d\u306b\u3001\u305d\u306e\u5834\u6240\u306b\u81ea\u52d5\u7684\u306b\u7a7a\u30d5\u30a1\u30a4\u30eb\u3092\u4f5c\u308b\u3002
3535 # \u30d5\u30a9\u30eb\u30c0\u968e\u5c64\u3082\u81ea\u52d5\u3067\u6398\u308b\u3002
3636 auto_create=false
37+
38+#View\u7528\u306e\u30d5\u30a1\u30a4\u30eb\u3092\u7f6e\u304f\u5834\u6240\u3002\u5b9f\u884c\u6642\u306e\u30ab\u30ec\u30f3\u30c8\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u304b\u3089\u306e\u76f8\u5bfe\u3001\u307e\u305f\u306f\u7d76\u5bfe\u30d1\u30b9\u3067\u6307\u5b9a\u3002
39+#pagedata.root\u3068\u540c\u3058\u5834\u6240\u3067\u3082\u30d1\u30b9\u6307\u5b9a\u306f\u540c\u3058\u306b\u306a\u3089\u306a\u3044\u3053\u3068\u306b\u6ce8\u610f
40+#spring.thymeleaf.prefix=classpath:/templates/
41+#spring.thymeleaf.prefix=file:C:/MyLib/Bastub_OSDN/Dev/pagedata/
42+#spring.thymeleaf.prefix=classpath:/templates/views/
43+#\u2191\u3046\u307e\u304f\u3044\u304f
44+#\u2193\u30c0\u30e1
45+#spring.thymeleaf.prefix=/pagedata/
46+#spring.thymeleaf.prefix=/templates/
47+#spring.thymeleaf.prefix=classpath:../
48+#spring.thymeleaf.prefix=classpath:../../pagedata/
49+#spring.thymeleaf.prefix=classpath:/templates/views/
50+#spring.thymeleaf.prefix=${mydir}/temp/pagedata/
51+#spring.thymeleaf.prefix=C:/MyLib/Bastub_OSDN/Dev/pagedata/
52+
53+#Thmeleaf\u3092\u4f7f\u3046\u3068\u304d\u3001\u4fee\u6b63\u3059\u308b\u305f\u3073\u306b\u518d\u8d77\u52d5\u306f\u4e0d\u4fbf\u306a\u306e\u3067\u3002
54+spring.thymeleaf.cache=false
diff -r 6d19018d54b6 -r 81a5d2fd9d1d src/main/resources/replace.properties
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/resources/replace.properties Sat Nov 27 20:42:15 2021 +0900
@@ -0,0 +1,1 @@
1+/foo/bar.html /foo/templates/bar.html