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 org.opengion.fukurou.system.OgRuntimeException ;                 // 6.4.2.0 (2016/01/29)
019import org.opengion.hayabusa.common.HybsSystem;
020import org.opengion.hayabusa.common.HybsSystemException;
021import org.opengion.hayabusa.db.DBTableModel;
022import org.opengion.hayabusa.develop.JspCreateFactory;
023import org.opengion.hayabusa.develop.JspConvertEntity;
024import org.opengion.fukurou.system.ThrowUtil;                                   // 6.4.2.0 (2016/01/29)
025import org.opengion.fukurou.util.ErrorMessage;
026import org.opengion.fukurou.util.StringUtil;
027import org.opengion.fukurou.util.ToString;                                              // 6.1.1.0 (2015/01/17)
028import org.opengion.fukurou.util.ArraySet;                                              // 6.4.3.4 (2016/03/11)
029import org.opengion.fukurou.xml.JspSaxParser;
030
031import static org.opengion.fukurou.util.StringUtil.nval ;
032
033import java.io.File;
034import java.util.Map;
035import java.util.HashMap;
036import java.util.List;
037import java.util.ArrayList;
038import java.util.Locale ;
039import java.util.Set;                                                                                   // 6.4.3.4 (2016/03/11)
040
041/**
042 * JspCreateTag は、画面定義情報より、JSP画面を自動作成するツールです。
043 *
044 * 画面作成するにあたり、3箇所の参照元が存在します。
045 *
046 *   1.画面属性の設定 : 雛形   → 仮画面   ・・・ hpgid で指定の画面ID
047 *   2.仮画面修正     : 仮画面 → 仮画面   ・・・ jsp/customUserDef/画面ID
048 *   3.本環境修正     : 本画面 → 本画面   ・・・ jsp/画面ID
049 *
050 * それぞれ、作成先の画面IDフォルダが存在する場合は、取込元を作成先に設定します。
051 * つまり、一度作成すると、それ以降は、作成された画面を基準に処理を行います。
052 *
053 * @og.formSample
054 * ●形式:<og:jspCreate outdir="…" pgid="…" />
055 * ●body:なし
056 *
057 * ●Tag定義:
058 *   <og:jspCreate
059 *       pgid             ○【TAG】プログラムIDを指定します(必須)。
060 *       outdir             【TAG】作成先のディレクトリを指定します(初期値:HybsSystem#REAL_PATH + "jsp/")
061 *       hpgid              【TAG】雛形のプログラムIDを指定します
062 *       useUserDef         【TAG】仮環境を使用するかどうか[true/false]を指定します(初期値:true:使用する)
063 *       tagNames           【TAG】処理を行うタグを部分指定します(初期値:内部登録タグすべて)
064 *       tableId            【TAG】(通常は使いません)DBTableModel が登録されているメモリのキーを指定します
065 *       scope              【TAG】キャッシュする場合のスコープ[request/page/session/application]を指定します(初期値:session)
066 *       debug              【TAG】デバッグ情報を出力するかどうか[true/false]を指定します(初期値:false)
067 *   />
068 *
069 * ●使用例
070 *    ・先に、query タグでDBTableModel が作成済み
071 *          <og:jspCreate
072 *              outdir      = "{@SYS.REAL_PATH}jsp/"    :出力先フォルダ
073 *              pgid        = "{@PGID}"                 :作成画面ID
074 *              hpgid       = "{@HPGID}"                :雛形画面ID
075 *              useUserDef  = "true/false"                   :仮環境の使用可否(初期値:true 使用する)
076 *          />
077 *
078 * @og.group 開発補助
079 * @og.rev 5.1.9.0 (2010/08/01) 新規作成
080 *
081 * @version  0.9.0  2000/10/17
082 * @author   Kazuhiko Hasegawa
083 * @since    JDK1.6,
084 */
085public class JspCreateTag extends CommonTagSupport {
086        /** このプログラムのVERSION文字列を設定します。   {@value} */
087        private static final String VERSION = "6.5.0.1 (2016/10/21)" ;
088        private static final long serialVersionUID = 650120161021L ;
089
090        private static final String JSP_DIR = HybsSystem.sys( "REAL_PATH" ) + "jsp/" ;          // 6.3.9.0 (2015/11/06) Variables should start with a lowercase character(PMD)
091
092        private static final String CUST_DEF = "customUserDef/" ;
093
094        private transient DBTableModel  table   ;
095
096        private String          tableId         = HybsSystem.TBL_MDL_KEY;
097        private String          outdir          = JSP_DIR;                                              // 出力先フォルダ
098        private String          pgid            ;                                                               // 作成画面ID
099        private String          hpgid           ;                                                               // 雛形画面ID
100        private String[]        tagNames        ;                                                               // 5.6.8.0 (2013/09/06) 処理を行うタグを部分指定
101        private boolean         useUserDef      = true;                                                 // 仮環境の使用可否(初期値:true 使用する)
102
103        // 6.4.3.4 (2016/03/11) String配列 から、Setに置き換えます。
104        private static final Set<String> JCF_SET = new ArraySet<>( "COMMENT" , "HIDEMENU" , "COLUMN" , "ORDER_BY" , "QUERY" , "VIEW" , "TABLE_UPDATE" );
105
106        /**
107         * デフォルトコンストラクター
108         *
109         * @og.rev 6.4.2.0 (2016/01/29) PMD refactoring. Each class should declare at least one constructor.
110         */
111        public JspCreateTag() { super(); }              // これも、自動的に呼ばれるが、空のメソッドを作成すると警告されるので、明示的にしておきます。
112
113        /**
114         * Taglibの開始タグが見つかったときに処理する doStartTag() を オーバーライドします。
115         *
116         * @return      後続処理の指示(SKIP_BODY)
117         */
118        @Override
119        public int doStartTag() {
120                table = (DBTableModel)getObject( tableId );
121
122                return SKIP_BODY ;                              // Body を評価しない
123        }
124
125        /**
126         * Taglibの終了タグが見つかったときに処理する doEndTag() を オーバーライドします。
127         *
128         * @return      後続処理の指示
129         */
130        @Override
131        public int doEndTag() {
132                // デバッグ時には、オブジェクト内部情報を表示する。
133                if( isDebug() ) {
134                        debugPrint();
135                }
136
137                final boolean okFlag = execute();
138
139                // 6.4.1.1 (2016/01/16) PMD refactoring. A method should have only one exit point, and that should be the last statement in the method
140                return okFlag ? EVAL_PAGE : SKIP_PAGE ;
141
142        }
143
144        /**
145         * タグリブオブジェクトをリリースします。
146         * キャッシュされて再利用されるので、フィールドの初期設定を行います。
147         *
148         * @og.rev 5.6.8.0 (2013/09/06) tagNames 追加
149         */
150        @Override
151        protected void release2() {
152                super.release2();
153                table           = null;
154                tableId         = HybsSystem.TBL_MDL_KEY;
155                outdir          = JSP_DIR;
156                pgid            = null;
157                hpgid           = null;
158                useUserDef      = true;
159                tagNames        = null;                                                 // 5.6.8.0 (2013/09/06) 処理を行うタグを部分指定
160        }
161
162        /**
163         * DBTableModel処理を実行します。
164         *
165         * @og.rev 5.2.1.0 (2010/10/01) 実行クラスのクラス名を変更します。 _OG_ を削除
166         * @og.rev 5.5.2.6 (2012/05/25) findbugs対応。JspConvertEntity.DBKEY を、JspConvertEntity.getDBKEY() に変更。
167         * @og.rev 5.6.8.0 (2013/09/06) 処理対象タグの部分指定対応 (tagNames 追加)
168         * @og.rev 6.3.9.0 (2015/11/06) コンストラクタで初期化されていないフィールドを null チェックなしで利用している(findbugs)
169         * @og.rev 6.4.2.0 (2016/01/29) ex.printStackTrace() を、ThrowUtil#ogStackTrace(Throwable) に置き換え。
170         * @og.rev 6.4.3.4 (2016/03/11) String配列 から、Setに置き換えます。
171         * @og.rev 6.5.0.1 (2016/10/21) ErrorMessage をまとめるのと、直接 Throwable を渡します。
172         *
173         * @return      処理の実行結果
174         */
175        public boolean execute() {
176
177                // 出力先(セーブ先)のフォルダ
178                final File saveDir = useUserDef ? new File( outdir + CUST_DEF , pgid ) : new File( outdir , pgid ) ;
179
180                // 雛形(参照元)のフォルダ
181                final File refDir ;
182                if( saveDir.exists() ) {                // 出力先フォルダが存在した場合
183                        refDir = saveDir ;                      // 出力先フォルダが、雛形フォルダになる。
184                }
185                else {                                                  // 出力先フォルダが存在しない場合
186                        refDir = new File( outdir , hpgid );                    // 雛形画面IDをそのまま使用します。
187                        if( ! saveDir.mkdirs() ) {
188                                final String errMsg = "出力先のフォルダが作成できませんでした。[" + saveDir + "]" ;
189                                throw new HybsSystemException( errMsg );
190                        }
191                }
192
193                if( ! refDir.exists() ) {       // 雛形(参照元)フォルダが存在しない場合、エラー
194                        final String errMsg = "雛形(参照元)フォルダが存在しません。[" + refDir + "]" ;
195                        throw new HybsSystemException( errMsg );
196                }
197
198                // 6.3.9.0 (2015/11/06) コンストラクタで初期化されていないフィールドを null チェックなしで利用している(findbugs)
199                if( table == null ) {
200                        final String errMsg = "DBTableModel が取得できていません。" + CR
201                                                        + "   tableId=[" + tableId + "] や、scope=[" + getScope() + "] の確認を行ってください。" ;
202                        throw new OgRuntimeException( errMsg );
203                }
204
205                final int[] clmNo = getTableColumnNo( JspConvertEntity.getDBKEY() );            // 5.5.2.6 (2012/05/25) findbugs対応
206                final int rowCnt  = table.getRowCount();
207
208                int row = 0;
209                boolean unCmntSet = true;
210                String[] data = null;
211                // 6.4.1.1 (2016/01/16) PMD refactoring. Avoid declaring a variable if it is unreferenced before a possible exit point.
212                boolean okFlag = false;
213                try {
214                        //JSPを書く為に必要な情報を設定
215                        final Map<String,List<JspConvertEntity>> jceLstMap = new HashMap<>();
216
217                        for( row=0; row<rowCnt; row++ ) {
218                                data = table.getValues( row );
219                                final JspConvertEntity entry = JspConvertEntity.newInstance( data, clmNo );
220
221                                if( entry != null ) {
222                                        List<JspConvertEntity> array = jceLstMap.get( entry.getType() );
223                                        if( array == null ) { array = new ArrayList<>(); }
224                                        array.add( entry );
225                                        jceLstMap.put( entry.getType(),array );
226                                        if( unCmntSet ) {
227                                                jceLstMap.put( "COMMENT",array );
228                                                unCmntSet = false;
229                                        }
230                                }
231                        }
232
233                        // 参照先フォルダから、出力先フォルダに、XMLコピー処理を行います。
234                        final JspSaxParser spar = new JspSaxParser();
235
236                        // 6.4.3.4 (2016/03/11) String配列 から、Setに置き換えます。
237                        if( tagNames == null ) {
238                                // tagnames が null の場合は、全項目を使用します。
239                                for( final String jcf : JCF_SET ) {
240                                        spar.addFilter( JspCreateFactory.newInstance( jcf , jceLstMap ) );
241                                }
242                        }
243                        else {
244                                for( final String jcf : tagNames ) {
245                                        spar.addFilter( JspCreateFactory.newInstance( jcf , jceLstMap ) );
246                                }
247                        }
248
249                        spar.copyDirectry( refDir,saveDir );
250
251                        okFlag = true;
252                }
253                catch( final RuntimeException ex ) {
254                        System.err.println( ThrowUtil.ogStackTrace( ex ) );                             // 6.4.2.0 (2016/01/29)
255                        // 6.5.0.1 (2016/10/21) ErrorMessage をまとめるのと、直接 Throwable を渡します。
256                        final ErrorMessage errMessage = new ErrorMessage( "JspCreateTag Error" )
257                                .addMessage( row+1,ErrorMessage.NG,"JSPOUT"
258                                        , "PRGID=[" + pgid + "]"
259                                        , StringUtil.array2csv( data )
260                                        )
261                                        .addMessage( ex );
262                        // BAT から呼び出す場合があるため、標準エラー出力にも情報を出しておきます。
263                        System.out.println( errMessage );
264                }
265
266                return okFlag;
267        }
268
269        /**
270         * 【TAG】(通常は使いません)結果のDBTableModelを、sessionに登録するときのキーを指定します
271         *              (初期値:HybsSystem#TBL_MDL_KEY[={@og.value HybsSystem#TBL_MDL_KEY}])。
272         *
273         * @og.tag
274         * 検索結果より、DBTableModelオブジェクトを作成します。これを、下流のviewタグ等に
275         * 渡す場合に、通常は、session を利用します。その場合の登録キーです。
276         * query タグを同時に実行して、結果を求める場合、同一メモリに配置される為、
277         * この tableId 属性を利用して、メモリ空間を分けます。
278         *              (初期値:HybsSystem#TBL_MDL_KEY[={@og.value HybsSystem#TBL_MDL_KEY}])。
279         *
280         * @param       id テーブルID (sessionに登録する時のID)
281         */
282        public void setTableId( final String id ) {
283                tableId = nval( getRequestParameter( id ),tableId );
284        }
285
286        /**
287         * 【TAG】作成先のディレクトリを指定します(初期値:HybsSystem#REAL_PATH + "jsp/")。
288         * @og.tag
289         * 作成先のディレクトリを指定します。
290         * 初期値は、実際に実行しているアプリケーションの REAL_PATH + jsp フォルダ以下です。
291         * 作成先のフォルダも、useUserDef の設定によって異なります。
292         *
293         * @og.rev 6.0.2.4 (2014/10/17) メソッド名の統一の為
294         *
295         * @param       dir     出力先のディレクトリ
296         */
297        public void setOutDir( final String dir ) {
298                outdir = nval( getRequestParameter( dir ),outdir );
299        }
300
301        /**
302         * 【TAG】プログラムIDを指定します。
303         *
304         * @og.tag
305         * 作成先のプログラムIDを指定します。
306         * ただし、作成先の実際のフォルダは、useUserDef の設定によって異なります。
307         *
308         * @param       id      プログラムID
309         */
310        public void setPgid( final String id ) {
311                pgid = nval( getRequestParameter( id ),pgid );
312        }
313
314        /**
315         * 【TAG】雛形のプログラムIDを指定します。
316         *
317         * @og.tag
318         * 雛形のプログラムIDをパースして、実際のプログラムを作成します。
319         * ただし、作成先の実際のフォルダは、useUserDef の設定によって異なります。
320         * また、パースするのは、作成先の画面IDのフォルダが存在しない場合のみです。
321         * すでに、存在している場合は、元の画面IDのフォルダを読み取って、パースを
322         * 行います。基本的に、作成先のソースを手で修正した場合でも、パースと
323         * 無関係な箇所の修正はそのまま反映のこされます。
324         *
325         * @param       id      雛形のプログラムID
326         */
327        public void setHpgid( final String id ) {
328                hpgid = nval( getRequestParameter( id ),hpgid );
329        }
330
331        /**
332         * 【TAG】仮環境を使用するかどうか[true/false]を指定します(初期値:true:使用する)。
333         *
334         * @og.tag
335         * true:使用する を設定すると、"customUserDef" フォルダの下に、画面IDの
336         * フォルダを作成します。
337         * false:使用しない を設定すると、実際の リアルパス(REAL_PATH/jsp)の下に、
338         * 画面IDのフォルダを作成します。こちらは、実際の画面と同様に、画面リソース等を
339         * 作成してアクセスすることになります。
340         *
341         * @param       flag    仮環境の使用 [true:使用する/false:使用しない]
342         */
343        public void setUseUserDef( final String flag ) {
344                useUserDef = nval( getRequestParameter( flag ),useUserDef );
345        }
346
347        /**
348         * 【TAG】処理を行うタグを部分指定します(初期値:内部登録タグすべて)。
349         *
350         * @og.tag
351         * 処理を行うタグは、内部的に、COMMENT,HIDEMENU,COLUMN,ORDER_BY,QUERY,VIEW,TABLE_UPDATE だけ
352         * 予約されており、初期値は、すべてのタグを処理します。
353         * ここでは、その一部のみ実行できるように、CSV形式で指定できるようにします。
354         * 実行不可のタグ名を指定すると、エラーになります。
355         *
356         * @og.rev 5.6.8.0 (2013/09/06) 新規追加
357         * @og.rev 6.3.4.0 (2015/08/01) Arrays.toString から String.join に置き換え。
358         * @og.rev 6.4.3.4 (2016/03/11) String配列 から、Setに置き換えます。
359         *
360         * @param       tags    処理を行うタグを部分指定
361         */
362        public void setTagNames( final String tags ) {
363                final String tagNms = nval( getRequestParameter( tags ),null );
364
365                if( tagNms != null ) {
366                        tagNames = tagNms.split( "," );
367
368                        for( int i=0; i<tagNames.length; i++ ) {
369                                final String tag = tagNames[i].trim().toUpperCase(Locale.JAPAN);
370                                if( ! check( tag, JCF_SET ) ) {
371                                        final String errMsg = "指定の tagNames は、下記の範囲で指定してください。"  + CR
372                                                                        + "tagNames=[" + tagNms + "] "                                                          + CR
373                                                                        + "tagNames List=" + String.join( ", " , JCF_SET ) ;
374                                        throw new HybsSystemException( errMsg );
375
376                                }
377                                tagNames[i] = tag ;             // 大文字に変換した値を戻す。
378                        }
379                }
380        }
381
382        /**
383         *  カラム名配列(String[])より、対応するカラムNo配列(int[])を作成します。
384         *
385         * @param       nameArray カラム名配列
386         *
387         * @return      カラムNo配列(可変長引数)
388         */
389        private int[] getTableColumnNo( final String... nameArray ) {
390                int[] clmNo = new int[nameArray.length];
391                for( int i=0; i<clmNo.length; i++ ) {
392                        clmNo[i] = table.getColumnNo( nameArray[i] );
393                }
394                return clmNo;
395        }
396
397        /**
398         * このオブジェクトの文字列表現を返します。
399         * 基本的にデバッグ目的に使用します。
400         *
401         * @return このクラスの文字列表現
402         * @og.rtnNotNull
403         */
404        @Override
405        public String toString() {
406                return ToString.title( this.getClass().getName() )
407                                .println( "VERSION"                     ,VERSION                )
408                                .println( "tableId"                     ,tableId                )
409                                .println( "outdir"                      ,outdir                 )
410                                .println( "pgid"                        ,pgid                   )
411                                .println( "hpgid"                       ,hpgid                  )
412                                .println( "useUserDef"          ,useUserDef             )
413                                .fixForm().toString() ;
414        }
415}