    001/*
    002 * Copyright (c) 2009 The openGion Project.
    003 *
    004 * Licensed under the Apache License, Version 2.0 (the "License");
    005 * you may not use this file except in compliance with the License.
    006 * You may obtain a copy of the License at
    007 *
    008 *     http://www.apache.org/licenses/LICENSE-2.0
    009 *
    010 * Unless required by applicable law or agreed to in writing, software
    011 * distributed under the License is distributed on an "AS IS" BASIS,
    012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
    013 * either express or implied. See the License for the specific language
    014 * governing permissions and limitations under the License.
    015 */
    016package org.opengion.hayabusa.taglib;
    017
    018import java.io.IOException;
    019import java.nio.file.Files;
    020import java.nio.file.Paths;
    021import java.nio.charset.StandardCharsets;
    022import java.util.Map;
    023import java.util.stream.Stream;
    024import java.util.stream.Collectors;
    025
    026import org.opengion.hayabusa.common.HybsSystem;
    027import org.opengion.hayabusa.common.HybsSystemException;
    028import org.opengion.fukurou.util.JSONScan;
    029import org.opengion.fukurou.util.StringUtil;
    030import org.opengion.fukurou.util.ToString;
    031
    032/**
    033 * Ior のﾘｸｴｽﾄJSON に対して､Select文を生成するｸﾗｽです｡
    034 *
    035 * 通常は､POSTﾃﾞｰﾀの JSONか､loadFileで指定したﾌｧｲﾙ形式の JSONを読み込んで､
    036 * SQL文を作成するため､この結果を､queryﾀｸﾞ等で実行して､DBTableModel を生成します｡
    037 *
    038 * @og.formSample
    039 * ●形式：&lt;og:iorSQL /&gt;
    040 * ●body：なし
    041 *
    042 * POSTﾃﾞｰﾀ､または､loadFile に記載する JSON は､下記の形式です｡
    043 * {
    044 *   "report_name": "≪ﾃｰﾌﾞﾙ名≫",
    045 *   "select_cols": "PN,sum(SUNYSY) AS AAA,max(sys_index),COUNT(*) as cnt,TANI as TANIIII",
    046 *   "where_lk": "{'TANI':'%%g'}",
    047 *   "where_gt": "{'DYNYSY':'20210101','SUNYSY':'1'}",
    048 *   "where_lt": "{'SUNYSY':2000}",
    049 *   "group_by":"PN,TANI",
    050 *   "order_by":"TANIIII"
    051 * }
    052 *
    053 * ●Tag定義：
    054 *   &lt;og:iorSQL
    055 *       reqKey             【TAG】JSONﾃﾞｰﾀを取り出すPOSTﾘｸｴｽﾄのｷｰ
    056 *       loadFile           【TAG】ﾌｧｲﾙからJSONﾃﾞｰﾀを読み取ります(指定された場合､ﾌｧｲﾙ優先)
    057 *       caseKey            【TAG】このﾀｸﾞ自体を利用するかどうかの条件ｷｰを指定します(初期値:null)
    058 *       caseVal            【TAG】このﾀｸﾞ自体を利用するかどうかの条件値を指定します(初期値:null)
    059 *       caseNN             【TAG】指定の値が､null/ｾﾞﾛ文字列 でない場合(Not Null=NN)は､このﾀｸﾞは使用されます(初期値:判定しない)
    060 *       caseNull           【TAG】指定の値が､null/ｾﾞﾛ文字列 の場合は､このﾀｸﾞは使用されます(初期値:判定しない)
    061 *       caseIf             【TAG】指定の値が､true/TRUE文字列の場合は､このﾀｸﾞは使用されます(初期値:判定しない)
    062 *       debug              【TAG】ﾃﾞﾊﾞｯｸﾞ情報を出力するかどうか[true/false]を指定します(初期値:false)
    063 *   /&gt;
    064 *
    065 * @og.group 画面部品
    066 * @og.rev 8.1.1.0 (2022/02/04) 新規追加
    067 *
    068 * @version  8.1
    069 * @author       Kazuhiko Hasegawa
    070 * @since    JDK17,
    071 */
    072public class IorSQLTag extends CommonTagSupport {
    073        /** このﾌﾟﾛｸﾞﾗﾑのVERSION文字列を設定します｡ {@value} */
    074        private static final String VERSION = "8.1.1.0 (2022/02/04)" ;
    075        private static final long serialVersionUID = 811020220204L ;
    076
    077        private String  fileURL = HybsSystem.sys( "FILE_URL" );         // ﾌｧｲﾙURL
    078
    079        private String  jsonVal ;
    080        private String  reqKey;                 // POSTﾘｸｴｽﾄから読み込む場合のｷｰ
    081        private String  loadFile;               // ﾌｧｲﾙから読み込む場合のﾌｧｲﾙ名
    082
    083        /**
    084         * ﾃﾞﾌｫﾙﾄｺﾝｽﾄﾗｸﾀｰ
    085         *
    086         * @og.rev 6.4.2.0 (2016/01/29) PMD refactoring. Each class should declare at least one constructor.
    087         */
    088        public IorSQLTag() { super(); }         // これも､自動的に呼ばれるが､空のﾒｿｯﾄﾞを作成すると警告されるので､明示的にしておきます｡
    089
    090        /**
    091         * Taglibの開始ﾀｸﾞが見つかったときに処理する doStartTag() を ｵｰﾊﾞｰﾗｲﾄﾞします｡
    092         *
    093         * @return      後続処理の指示( EVAL_BODY_BUFFERED )
    094         */
    095        @Override
    096        public int doStartTag() {
    097                if( useTag() ) {
    098                        useXssCheck(  false );
    099                        useQuotCheck( false );
    100                }
    101
    102                return SKIP_BODY ;              // Body を評価しない
    103        }
    104
    105        /**
    106         * Taglibの終了ﾀｸﾞが見つかったときに処理する doEndTag() を ｵｰﾊﾞｰﾗｲﾄﾞします｡
    107         *
    108         * @return      後続処理の指示
    109         */
    110        @Override
    111        public int doEndTag() {
    112                debugPrint();           // 4.0.0 (2005/02/28)
    113
    114                // 優先順位は、loadFile があればloadFileから。
    115                if( loadFile != null ) {
    116                        // Files.lines には､try-with-resources文 が必要って…こんな感じ？
    117                        try( Stream<String> srm = Files.lines( Paths.get(loadFile) , StandardCharsets.UTF_8 ) ) {
    118                                jsonVal = srm.collect( Collectors.joining() );
    119                        }
    120                        catch( final IOException ex ) {
    121                                final String errMsg = "loadFile 処理中でｴﾗｰが発生しました｡"        + CR
    122                                                        + "\t " + ex.getMessage()                                                       + CR ;
    123                                throw new HybsSystemException( errMsg, ex );
    124                        }
    125                }
    126                else if( reqKey != null ) {
    127                        jsonVal = getRequest().getParameter( reqKey );
    128                }
    129                else {
    130                        final String errMsg = "reqKey か、loadFile は指定してください。" ;
    131                        throw new HybsSystemException( errMsg );
    132                }
    133
    134                printSQL() ;                    // 画面上に生成したSQL文を出力します。
    135
    136                return EVAL_PAGE ;
    137        }
    138
    139        /**
    140         * ﾀｸﾞﾘﾌﾞｵﾌﾞｼﾞｪｸﾄをﾘﾘｰｽします｡
    141         * ｷｬｯｼｭされて再利用されるので､ﾌｨｰﾙﾄﾞの初期設定を行います｡
    142         *
    143         */
    144        @Override
    145        protected void release2() {
    146                super.release2();
    147                jsonVal         = null;
    148                reqKey          = null;         // POSTﾘｸｴｽﾄから読み込む場合のｷｰ
    149                loadFile        = null;         // ﾌｧｲﾙから読み込む場合のﾌｧｲﾙ名
    150        }
    151
    152        /**
    153         * ﾀｸﾞﾘﾌﾞｵﾌﾞｼﾞｪｸﾄをﾘﾘｰｽします｡
    154         * ｷｬｯｼｭされて再利用されるので､ﾌｨｰﾙﾄﾞの初期設定を行います｡
    155         *
    156         * @param       jsonVal 読み込むｷｰ
    157         */
    158        private void printSQL() {
    159                final Map<String,String> jsonMap = JSONScan.json2Map( jsonVal );
    160
    161                if( isDebug() ) {
    162                        // SQL文を画面に出す関係で、ｺﾏﾝﾄﾞﾌﾟﾛﾝｳﾄに出すことにする。
    163                        jsonMap.forEach( (k,v) -> System.out.println( k + ":" + v ) );
    164                }
    165
    166                final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE );
    167
    168                final String cols = jsonMap.getOrDefault( "select_cols" , "*" );
    169                buf.append( "select " ).append( cols ).append( " from " );
    170                final String tbl = jsonMap.get( "report_name" );
    171                if( StringUtil.isNull(tbl) ) {
    172                        final String errMsg = "IOr 検索用 JSON では、report_name(=ﾃｰﾌﾞﾙ名)は必須です。" ;
    173                        throw new HybsSystemException( errMsg );
    174                }
    175                buf.append( tbl );
    176
    177                boolean useWhere = true ;               // 最初に where ｷｰを出すため
    178                useWhere = whereStr( buf , jsonMap.get( "where_lk" ) , " like " , useWhere ) ;  // LIKE 検索
    179                useWhere = whereStr( buf , jsonMap.get( "where_gt" ) , " > "    , useWhere ) ;  // GT(>)検索
    180                useWhere = whereStr( buf , jsonMap.get( "where_lt" ) , " < "    , useWhere ) ;  // LT(<)検索
    181
    182                final String gby = jsonMap.get( "group_by" );                   // GROUP BY
    183                if( StringUtil.isNotNull(gby) ) {
    184                        buf.append( " group by " ).append( gby );
    185                }
    186
    187                final String oby = jsonMap.get( "order_by" );                   // ORDER BY
    188                if( StringUtil.isNotNull(oby) ) {
    189                        buf.append( " order by " ).append( oby );
    190                }
    191
    192                jspPrint( buf.toString() );
    193        }
    194
    195        /**
    196         * 指定の文字列の前後のｼﾝｸﾞﾙｸｵｰﾄを削除します。
    197         *
    198         * @param       buf                     書き込む文字列ﾊﾞｯﾌｧ
    199         * @param       wkey            WHERE条件の指定ｷｰ
    200         * @param       operator        WHERE条件式(前後にｽﾍﾟｰｽを付けておいてください)
    201         * @param       useWhere        文字列連結時に、whereを使用済みかどうか
    202         * @return      where の書き込みがあれば、false を、なければ、引数のuseWhere を返す。
    203         */
    204        private boolean whereStr( final StringBuilder buf , final String wkey , final String operator , final boolean useWhere ) {
    205                boolean rtnWhere = useWhere ;
    206                if( StringUtil.isNotNull(wkey) ) {
    207                        final String[] ary = JSONScan.json2Array( wkey );       // ｶﾝﾏで分割,JSONScan.json2Trim 済
    208                        for( final String str : ary ) {
    209                                final String[] kv = str.split(":", 2);
    210                                if( kv.length == 2 ) {
    211                                        if( rtnWhere ) { buf.append( " where " ); rtnWhere=false; }
    212                                        else           { buf.append( " and " ); }
    213                                        buf.append( quotTrim(kv[0]) ).append( operator ).append( kv[1] );
    214                                }
    215                        }
    216                }
    217                return rtnWhere ;
    218        }
    219
    220        /**
    221         * 指定の文字列の前後のｼﾝｸﾞﾙｸｵｰﾄを削除します。
    222         *
    223         * @param       str     読み込む文字列
    224         * @return      key     前後のｼﾝｸﾞﾙｸｵｰﾄを削除した文字列
    225         */
    226        private String quotTrim( final String str ) {
    227                if( str.length() >= 2 && str.charAt(0) == '\'' && str.charAt(str.length()-1) == '\'' ) {
    228                        return str.substring(1,str.length()-1);
    229                }
    230                else {
    231                        return str ;
    232                }
    233        }
    234
    235        /**
    236         * 【TAG】POSTﾘｸｴｽﾄから読み込む場合のｷｰを指定します｡
    237         *
    238         * @og.tag
    239         *  POSTﾘｸｴｽﾄから読み込む場合のｷｰを指定
    240         *
    241         * @param       key     読み込むｷｰ
    242         */
    243        public void setReqKey( final String key ) {
    244                reqKey = StringUtil.nval( getRequestParameter( key ),reqKey );
    245        }
    246
    247        /**
    248         * 【TAG】ﾌｧｲﾙからURL接続結果に相当するﾃﾞｰﾀを読み取ります｡
    249         *
    250         * @og.tag
    251         * 主にﾃﾞﾊﾞｯｸﾞ用として使われます｡
    252         *
    253         * @param       file    検索するﾌｧｲﾙ
    254         */
    255        public void setLoadFile( final String file ) {
    256                loadFile = StringUtil.nval( getRequestParameter( file ),loadFile );
    257                if( loadFile != null ) {
    258                        loadFile = HybsSystem.url2dir( StringUtil.urlAppend( fileURL,loadFile ) );
    259                }
    260        }
    261
    262        /**
    263         * このｵﾌﾞｼﾞｪｸﾄの文字列表現を返します｡
    264         * 基本的にﾃﾞﾊﾞｯｸﾞ目的に使用します｡
    265         *
    266         * @return このｸﾗｽの文字列表現
    267         * @og.rtnNotNull
    268         */
    269        @Override
    270        public String toString() {
    271                return ToString.title( this.getClass().getName() )
    272                                .println( "VERSION"                             ,VERSION                        )
    273                                .println( "reqKey"                              ,reqKey                         )
    274                                .println( "loadFile"                    ,loadFile                       )
    275                                .fixForm().toString() ;
    276        }
    277}