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