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 ;    // 5.6.8.0 (2013/09/06)
088
089//      private static final String ENCODE = "UTF-8" ;
090
091//      private static String JSP_DIR = "C:/opengion/uap/webapps/gf/jsp/";
092        private static String JSP_DIR = HybsSystem.sys( "REAL_PATH" ) + "jsp/" ;
093
094        private static final String CUST_DEF = "customUserDef/" ;
095
096        private transient DBTableModel  table   = null;
097
098        private String          tableId         = HybsSystem.TBL_MDL_KEY;
099        private String          outdir          = JSP_DIR;                                              // 出力先フォルダ
100        private String          pgid            = null;                                                 // 作成画面ID
101        private String          hpgid           = null;                                                 // 雛形画面ID
102        private String[]        tagNames        = null;                                                 // 5.6.8.0 (2013/09/06) 処理を行うタグを部分指定
103        private boolean         useUserDef      = true;                                                 // 仮環境の使用可否(初期値:true 使用する)
104
105//      private static final String[] JCF_LIST = new String[] { "COMMENT" , "COLUMN" , "ORDER_BY" , "QUERY" , "VIEW" , "TABLE_UPDATE" };
106        private static final String[] JCF_LIST = new String[] { "COMMENT" , "HIDEMENU" , "COLUMN" , "ORDER_BY" , "QUERY" , "VIEW" , "TABLE_UPDATE" };   // 5.6.4.4 (2013/05/31)
107
108        /**
109         * Taglibの開始タグが見つかったときに処理する doStartTag() を オーバーライドします。
110         *
111         * @return      後続処理の指示(SKIP_BODY)
112         */
113        @Override
114        public int doStartTag() {
115                table = (DBTableModel)getObject( tableId );
116
117                return(SKIP_BODY);                              // Body を評価しない
118//              return ( EVAL_BODY_BUFFERED );  // Body を評価する
119        }
120
121        /**
122         * Taglibのタグ本体を処理する doAfterBody() を オーバーライドします。
123         *
124         * @return      int
125         */
126//      @Override
127//      public int doAfterBody() {
128//              body = nval( getBodyString(),body );
129//
130//              return(SKIP_BODY);
131//      }
132
133        /**
134         * Taglibの終了タグが見つかったときに処理する doEndTag() を オーバーライドします。
135         *
136         * @return      後続処理の指示
137         */
138        @Override
139        public int doEndTag() {
140                // デバッグ時には、オブジェクト内部情報を表示する。
141                if( isDebug() ) {
142                        debugPrint();
143                }
144
145                boolean okFlag = execute();
146
147                if( okFlag )  {         // 正常
148                        return(EVAL_PAGE);
149                }
150                else {
151                        return(SKIP_PAGE);
152                }
153        }
154
155        /**
156         * タグリブオブジェクトをリリースします。
157         * キャッシュされて再利用されるので、フィールドの初期設定を行います。
158         *
159         * @og.rev 5.6.8.0 (2013/09/06) tagNames 追加
160         */
161        @Override
162        protected void release2() {
163                super.release2();
164                table           = null;
165                tableId         = HybsSystem.TBL_MDL_KEY;
166                outdir          = JSP_DIR;
167                pgid            = null;
168                hpgid           = null;
169                useUserDef      = true;
170                tagNames        = null;                                                 // 5.6.8.0 (2013/09/06) 処理を行うタグを部分指定
171        }
172
173        /**
174         * DBTableModel処理を実行します。
175         *
176         * @og.rev 5.2.1.0 (2010/10/01) 実行クラスのクラス名を変更します。 _OG_ を削除
177         * @og.rev 5.5.2.6 (2012/05/25) findbugs対応。JspConvertEntity.DBKEY を、JspConvertEntity.getDBKEY() に変更。
178         * @og.rev 5.6.8.0 (2013/09/06) 処理対象タグの部分指定対応 (tagNames 追加)
179         *
180         * @return      処理の実行結果
181         */
182        public boolean execute() {
183                boolean okFlag = false;
184
185                // 出力先(セーブ先)のフォルダ
186                File saveDir = (useUserDef) ? new File( outdir + CUST_DEF , pgid ) : new File( outdir , pgid ) ;
187
188                // 雛形(参照元)のフォルダ
189                final File refDir ;
190                if( saveDir.exists() ) {                // 出力先フォルダが存在した場合
191                        refDir = saveDir ;                      // 出力先フォルダが、雛形フォルダになる。
192                }
193                else {                                                  // 出力先フォルダが存在しない場合
194                        refDir = new File( outdir , hpgid );                    // 雛形画面IDをそのまま使用します。
195                        if( ! saveDir.mkdirs() ) {
196                                String errMsg = "出力先のフォルダが作成できませんでした。[" + saveDir + "]" ;
197                                throw new HybsSystemException( errMsg );
198                        }
199                }
200
201                if( ! refDir.exists() ) {       // 雛形(参照元)フォルダが存在しない場合、エラー
202                        String errMsg = "雛形(参照元)フォルダが存在しません。[" + refDir + "]" ;
203                        throw new HybsSystemException( errMsg );
204                }
205
206//              PrintWriter writer = null;
207
208//              int[] clmNo = getTableColumnNo( JspConvertEntity.DBKEY );
209                int[] clmNo = getTableColumnNo( JspConvertEntity.getDBKEY() );          // 5.5.2.6 (2012/05/25) findbugs対応
210                int rowCnt  = table.getRowCount();
211
212                int row = 0;
213                boolean unCmntSet = true;
214                String[] data = null;
215                try {
216                        //JSPを書く為に必要な情報を設定
217                        Map<String,List<JspConvertEntity>> tables = new HashMap<String,List<JspConvertEntity>>();
218
219                        for( row=0; row<rowCnt; row++ ) {
220                                data = table.getValues( row );
221                                JspConvertEntity entry = JspConvertEntity.newInstance( data, clmNo );
222
223                                if( entry != null ) {
224                                        List<JspConvertEntity> array = tables.get( entry.getType() );
225                                        if( array == null ) { array = new ArrayList<JspConvertEntity>(); }
226                                        array.add( entry );
227                                        tables.put( entry.getType(),array );
228                                        if( unCmntSet ) {
229                                                tables.put( "COMMENT",array );
230                                                unCmntSet = false;
231                                        }
232                                }
233                        }
234
235                        // 参照先フォルダから、出力先フォルダに、XMLコピー処理を行います。
236                        JspSaxParser spar = new JspSaxParser();
237
238                        // 5.2.1.0 (2010/10/01) 実行クラスのクラス名を変更します。 _OG_ を削除
239//                      spar.addFilter( JspCreateFactory.newInstance( "COMMENT" ,tables) );
240//                      spar.addFilter( JspCreateFactory.newInstance( "COLUMN"  ,tables) );
241//                      spar.addFilter( JspCreateFactory.newInstance( "SELECT"  ,tables) );
242//                      spar.addFilter( JspCreateFactory.newInstance( "QUERY"   ,tables) );
243//                      spar.addFilter( JspCreateFactory.newInstance( "VIEW"    ,tables) );
244
245                        // 5.6.8.0 (2013/09/06) 処理対象タグの部分指定対応 (tagNames 追加)
246                        if( tagNames == null ) { tagNames = JCF_LIST; }
247//                      for( String jcf : JCF_LIST ) {
248                        for( String jcf : tagNames ) {
249                                spar.addFilter( JspCreateFactory.newInstance( jcf , tables ) );
250                        }
251
252                        spar.copyDirectry( refDir,saveDir );
253
254//                      //query.jsp作成
255//                      writer = FileUtil.getPrintWriter( new File( saveDir,"query.jsp" ),ENCODE );
256//                      writer.print(writeQueryJSP( refDir , tables  ));
257//                      writer.println();
258//                      writer.flush();
259//
260//                      //result.jsp作成
261//                      writer = FileUtil.getPrintWriter( new File( saveDir,"result.jsp" ),ENCODE );
262//                      writer.print(writeResultJSP( refDir , tables  ));
263//                      writer.println();
264//                      writer.flush();
265
266                        okFlag = true;
267                }
268                catch( RuntimeException ex ) {
269                        ex.printStackTrace();
270                        ErrorMessage errMessage = new ErrorMessage( "JspCreateTag Error" );
271                        errMessage.addMessage( row+1,ErrorMessage.NG,"JSPOUT",ex.getMessage() );
272                        errMessage.addMessage( row+1,ErrorMessage.NG,"JSPOUT",StringUtil.array2csv( data ) );
273                        errMessage.addMessage( row+1,ErrorMessage.NG,"JSPOUT","PRGID=[" + pgid + "]" );
274                        // BAT から呼び出す場合があるため、標準エラー出力にも情報を出しておきます。
275                        System.out.println( errMessage );
276                }
277//              finally {
278//                      Closer.ioClose( writer );
279//              }
280
281                return okFlag;
282        }
283
284        /**
285         * query.jspファイルの内容を生成します。
286         *
287         * @param refDir
288         * @param tables
289         *
290         * @return 生成した内容
291         */
292//      private String writeQueryJSP( File refDir , HashMap<String , ArrayList<JspConvertEntity>> tables ) {
293//              String jsp = "";
294//              try{
295//                      //利用する生成クラスを設定
296//                      HashMap< String , JspCreate > generates = new HashMap< String , JspCreate >();
297//                      generates.put( "og:comment"     , JspCreateFactory.newInstance( "OG_COMMENT",tables) );
298//                      generates.put( "table"          , JspCreateFactory.newInstance( "OG_COLUMN",tables) );
299//                      generates.put( "og:select"      , JspCreateFactory.newInstance( "OG_SELECT",tables) );
300//
301//                      //JSPの作成処理を実施。
302//                      JspHandler handler = new JspHandler();
303//                      jsp = handler.generate( new File( refDir , "query.jsp" ), ENCODE , generates);
304//
305//              }catch(Throwable ex){
306//                      ex.printStackTrace();
307//                      throw new RuntimeException(ex);
308//              }
309//              return jsp;
310//      }
311
312        /**
313         * result.jspファイルの内容を生成します。
314         *
315         * @param refDir
316         * @param tables
317         *
318         * @return 生成した内容
319         */
320//      private String writeResultJSP(File refDir , HashMap<String , ArrayList<JspConvertEntity>> tables ) {
321//              String jsp = "";
322//              try{
323//                      //利用する生成クラスを設定
324//                      HashMap< String , JspCreate > generates = new HashMap< String , JspCreate >();
325//                      generates.put( "og:comment"     , JspCreateFactory.newInstance( "OG_COMMENT",tables) );
326//                      generates.put( "og:query"       , JspCreateFactory.newInstance( "OG_QUERY",tables ) );
327//                      generates.put( "og:view"        , JspCreateFactory.newInstance( "OG_VIEW",tables) );
328//
329//                      //JSPの作成処理を実施。
330//                      JspHandler handler = new JspHandler();
331//                      jsp = handler.generate( new File( refDir , "result.jsp" ) , ENCODE  , generates);
332//
333//              }catch(Throwable ex){
334//                      ex.printStackTrace();
335//                      throw new RuntimeException(ex);
336//              }
337//              return jsp;
338//      }
339
340        /**
341         * 【TAG】(通常は使いません)結果のDBTableModelを、sessionに登録するときのキーを指定します
342         *              (初期値:HybsSystem#TBL_MDL_KEY[={@og.value org.opengion.hayabusa.common.HybsSystem#TBL_MDL_KEY}])。
343         *
344         * @og.tag
345         * 検索結果より、DBTableModelオブジェクトを作成します。これを、下流のviewタグ等に
346         * 渡す場合に、通常は、session を利用します。その場合の登録キーです。
347         * query タグを同時に実行して、結果を求める場合、同一メモリに配置される為、
348         * この tableId 属性を利用して、メモリ空間を分けます。
349         *              (初期値:HybsSystem#TBL_MDL_KEY[={@og.value org.opengion.hayabusa.common.HybsSystem#TBL_MDL_KEY}])。
350         *
351         * @param       id sessionに登録する時の ID
352         */
353        public void setTableId( final String id ) {
354                tableId = nval( getRequestParameter( id ),tableId );
355        }
356
357        /**
358         * 【TAG】作成先のディレクトリを指定します。
359         *
360         * @og.tag
361         * 作成先のディレクトリを指定します。
362         * 初期値は、実際に実行しているアプリケーションの REAL_PATH + jsp フォルダ以下です。
363         * 作成先のフォルダも、useUserDef の設定によって異なります。
364         *
365         * @param       dir     出力先のディレクトリ
366         */
367        public void setOutdir( final String dir ) {
368                outdir = nval( getRequestParameter( dir ),outdir );
369        }
370
371        /**
372         * 【TAG】プログラムIDを指定します。
373         *
374         * @og.tag
375         * 作成先のプログラムIDを指定します。
376         * ただし、作成先の実際のフォルダは、useUserDef の設定によって異なります。
377         *
378         * @param       id      プログラムID
379         */
380        public void setPgid( final String id ) {
381                pgid = nval( getRequestParameter( id ),pgid );
382        }
383
384        /**
385         * 【TAG】雛形のプログラムIDを指定します。
386         *
387         * @og.tag
388         * 雛形のプログラムIDをパースして、実際のプログラムを作成します。
389         * ただし、作成先の実際のフォルダは、useUserDef の設定によって異なります。
390         * また、パースするのは、作成先の画面IDのフォルダが存在しない場合のみです。
391         * すでに、存在している場合は、元の画面IDのフォルダを読み取って、パースを
392         * 行います。基本的に、作成先のソースを手で修正した場合でも、パースと
393         * 無関係な箇所の修正はそのまま反映のこされます。
394         *
395         * @param       id      雛形のプログラムID
396         */
397        public void setHpgid( final String id ) {
398                hpgid = nval( getRequestParameter( id ),hpgid );
399        }
400
401        /**
402         * 【TAG】仮環境を使用するかどうか[true/false]を指定します(初期値:true:使用する)。
403         *
404         * @og.tag
405         * true:使用する を設定すると、"customUserDef" フォルダの下に、画面IDの
406         * フォルダを作成します。
407         * false:使用しない を設定すると、実際の リアルパス(REAL_PATH/jsp)の下に、
408         * 画面IDのフォルダを作成します。こちらは、実際の画面と同様に、画面リソース等を
409         * 作成してアクセスすることになります。
410         *
411         * @param       flag    仮環境を使用するかどうか [true:使用する/false:使用しない]
412         */
413        public void setUseUserDef( final String flag ) {
414                useUserDef = nval( getRequestParameter( flag ),useUserDef );
415        }
416
417        /**
418         * 【TAG】処理を行うタグを部分指定します(初期値:内部登録タグすべて)。
419         *
420         * @og.tag
421         * 処理を行うタグは、内部的に、COMMENT,HIDEMENU,COLUMN,ORDER_BY,QUERY,VIEW,TABLE_UPDATE だけ
422         * 予約されており、初期値は、すべてのタグを処理します。
423         * ここでは、その一部のみ実行できるように、カンマ区切り文字で指定できるようにします。
424         * 実行不可のタグ名を指定すると、エラーになります。
425         *
426         * @og.rev 5.6.8.0 (2013/09/06) 新規追加
427         *
428         * @param       tags    処理を行うタグを部分指定する。
429         */
430        public void setTagNames( final String tags ) {
431                String tagNms = nval( getRequestParameter( tags ),null );
432
433                if( tagNms != null ) {
434                        tagNames = tagNms.split( "," );
435
436                        for( int i=0; i<tagNames.length; i++ ) {
437                                String tag = tagNames[i].trim().toUpperCase(Locale.JAPAN);
438                                if( ! check( tag, JCF_LIST ) ) {
439                                        String errMsg = "指定の tagNames は、下記の範囲で指定してください。"
440                                                                        + "tagNames=" + tagNms + " (NG=" + tag + ") : "
441                                                                        + JCF_LIST.toString();
442                                        throw new HybsSystemException( errMsg );
443                                }
444                                tagNames[i] = tag ;             // 大文字に変換した値を戻す。
445                        }
446                }
447        }
448
449        /**
450         *  カラム名配列(String[])より、対応するカラムNo配列(int[])を作成します。
451         *
452         * @param       nameArray カラム名配列
453         *
454         * @return      カラムNo配列
455         */
456        private int[] getTableColumnNo( final String[] nameArray ) {
457                int[] clmNo = new int[ nameArray.length ];
458                for( int i=0; i<clmNo.length; i++ ) {
459                        clmNo[i] = table.getColumnNo( nameArray[i] );
460                }
461                return clmNo;
462        }
463
464        /**
465         * シリアライズ用のカスタムシリアライズ書き込みメソッド
466         *
467         * @param       strm    ObjectOutputStreamオブジェクト
468         * @serialData 一部のオブジェクトは、シリアライズされません。
469         *
470         * @throws IOException  入出力エラーが発生した場合
471         */
472        private void writeObject( final ObjectOutputStream strm ) throws IOException {
473                strm.defaultWriteObject();
474        }
475
476        /**
477         * シリアライズ用のカスタムシリアライズ読み込みメソッド
478         *
479         * ここでは、transient 宣言された内部変数の内、初期化が必要なフィールドのみ設定します。
480         *
481         * @param       strm    ObjectInputStreamオブジェクト
482         * @see #release2()
483         * @serialData 一部のオブジェクトは、シリアライズされません。
484         *
485         * @throws IOException  シリアライズに関する入出力エラーが発生した場合
486         * @throws ClassNotFoundException       クラスを見つけることができなかった場合
487         */
488        private void readObject( final ObjectInputStream strm ) throws IOException , ClassNotFoundException {
489                strm.defaultReadObject();
490        }
491
492        /**
493         * このオブジェクトの文字列表現を返します。
494         * 基本的にデバッグ目的に使用します。
495         *
496         * @return このクラスの文字列表現
497         */
498        @Override
499        public String toString() {
500                return org.opengion.fukurou.util.ToString.title( this.getClass().getName() )
501                                .println( "VERSION"                     ,VERSION                )
502                                .println( "tableId"                     ,tableId                )
503                                .println( "outdir"                      ,outdir                 )
504                                .println( "pgid"                        ,pgid                   )
505                                .println( "hpgid"                       ,hpgid                  )
506                                .println( "useUserDef"          ,useUserDef             )
507                                .fixForm().toString() ;
508        }
509}